// ==UserScript==
// @name          Easy Scrapper
// @namespace     http://niku.suku.name/media/greasemonkey
// @description   Scrap selected content.
// @include       *
// @version       1.0.0 (2007/12/26)
// ==/UserScript==

var StringManager = (function() { this.initialize.apply(this, arguments); });
StringManager.xmlSpecialChars = (function(source) {
  var result = source;
  result = result.replace(/&/g, '&amp;');
  result = result.replace(/</g, '&lt;');
  result = result.replace(/>/g, '&gt;');
  result = result.replace(/"/g, '&quot;');
  result = result.replace(/'/g, '&#39;');
  return result;
});
StringManager.toXML = (function(source) {
  xmlString = source.replace(/(?:^\s+|\s+$)/g, '').replace(/^<\?.*?\?>/, '');
  return new XML(xmlString);
});

var Option = (function() { this.initialize.apply(this, arguments); });
Option.prototype = {
  initialize : function(optionName, defaultValue) {
    this.name = optionName.toLowerCase();
    this.defaultValue = (typeof(defaultValue) != 'undefined') ? defaultValue : null;
    Option.options[optionName] = this;
  },
  getValue : function() {
    return GM_getValue(this.name, this.defaultValue);
  },
  setValue : function(newValue) {
    GM_setValue(this.name, newValue);
  }
};
Option.options = {};
Option.get = (function(optionName, defaultValue) {
  optionName = optionName.toLowerCase();
  defaultValue = (typeof(defaultValue) != 'undefined') ? defaultValue : null;
  if (typeof (Option.options[optionName]) == 'undefined') {
    new Option(optionName, defaultValue);
  }
  return Option.options[optionName];
});

var Menu = (function() { this.initialize.apply(this, arguments); });
Menu.register = (function(title, callback, accelKey, accessKey) {
  accelKey = (accelKey || '').toString().toLowerCase();
  accessKey = (accessKey || '').toString().toLowerCase();
  var accelSetting = Menu.parseAccelKey(accelKey);
  GM_registerMenuCommand(title, callback, accelSetting.key, accelSetting.modifiers, accessKey);
});
Menu.parseAccelKey = (function(accelKey) {
  var aAccelKey = accelKey.split(' '), result = {};
  var keys = [], modifiers = [];
  var validModifiers = ' shift alt meta control accel ', tempKey;
  for(var i = 0, len = aAccelKey.length; i < len; i++) {
    tempKey = aAccelKey[i];
    if (tempKey.length == 1) {
      keys[keys.length] = tempKey;
    } else if (validModifiers.indexOf(' ' + tempKey + ' ') != -1) {
      modifiers[modifiers.length] = tempKey;
    }
  }
  if (keys.length > 0) {
    result.key = keys[0];
    result.modifiers = modifiers.join(' ');
  } else {
    result.key = '';
    result.modifiers = '';
  }
  return result;
});

var Messenger = (function() { this.initialize.apply(this, arguments); });
Messenger.error = (function() {
  if (console && console.log) {
    return console.log;
  } else {
    return alert;
  }
})();
Messenger.show = (function() {
  if (console && console.log) {
    return console.log;
  } else {
    return (function() {});
  }
})();

var XMLRPC = (function() { this.initialize.apply(this, arguments); });

XMLRPC.Response = (function() { this.initialize.apply(this, arguments); });
XMLRPC.Response.prototype = {
  initialize : function(xmlString) {
    this.objXML = StringManager.toXML(xmlString);
    if (this.objXML.fault.length()) {
      this.isSucceeded = false;
      this.isFault = true;
      var structMembers = this.objXML.fault.value.struct.member, valueName, value;
      console.debug(this.objXML.toXMLString());
      for (var i = 0, len = structMembers.length(); i< len; i++ ) {
        valueName = structMembers[i].name.toString();
        value = structMembers[i].value.*.toString();
        this[valueName] = value;
      }
    } else {
      this.isSucceeded = true;
      this.isFault = false;
      this.responseText = this.objXML.toString();
    }
  }
};

XMLRPC.Value = (function() { this.initialize.apply(this, arguments); });

XMLRPC.Value.String = (function() { this.initialize.apply(this, arguments); });
XMLRPC.Value.String.prototype = {
  initialize : function(value) {
    this.value = value;
  },
  createXMLString : function() {
    var value = this.value;
    value = StringManager.xmlSpecialChars(value);
    return ('<value><string>' + value + '</string></value>');
  }
};

XMLRPC.Value.Boolean = (function() { this.initialize.apply(this, arguments); });
XMLRPC.Value.Boolean.prototype = {
  initialize : function(value) {
    this.value = value;
  },
  createXMLString : function() {
    var value = ((this.value) ? '1' : '0');
    return ('<value><boolean>' + value + '</boolean></value>');;
  }
};

XMLRPC.Value.Struct = (function() { this.initialize.apply(this, arguments); });
XMLRPC.Value.Struct.prototype = {
  initialize : function(value) {
    this.value = value;
  },
  createXMLString : function() {
    var value = '';
    for (var prop in this.value) {
      value += '<member><name>' + prop + '</name>';
      value += this.value[prop].createXMLString();
      value += '</member>';
    }
    return ('<value><struct>' + value + '</struct></value>');;
  }
};

XMLRPC.Message = (function() {this.initialize.apply(this, arguments); });
XMLRPC.Message.prototype = {
  initialize : function(methodName) {
    this.methodName = '';
    if (methodName) {
      this.setMethodName(methodName);
    }
    this.params = [];
  },
  setMethodName : function(methodName) {
    this.methodName = methodName;
  },
  addParam : function() {
    this.params = this.params.concat.apply(this.params, arguments);
  },
  createXMLString : function() {
    var result = '';
    result += '<?xml version="1.0"?>';
    result += '<methodCall>';
    result += '<methodName>' + this.methodName + '</methodName>';
    result += '<params>';
    for (var i = 0, len = this.params.length; i < len; i++) {
      result += '<param>' + this.params[i].createXMLString() + '</param>';
    }
    result += '</params>';
    result += '</methodCall>';
    return result;
  },
  post : function(targetUrl) {
    var data = this.createXMLString();
    GM_xmlhttpRequest({
      method: 'POST',
      url: targetUrl,
      headers: {
        'User-agent' : 'Greasemonkey',
        'Content-type' : 'text/xml'
      },
      data : data,
      onload : function(originalResponse) {
        var response = new XMLRPC.Response(originalResponse.responseText);
        if (response.isSucceeded) {
          Messenger.show('Your posting is succeeded');
        } else {
          var message = 'Your posting is failed!';
          if (response.faultCode) {
            message += "\nFault code : " + response.faultCode;
          }
          if (response.faultString) {
            message += "\nFault string : " + response.faultString;
          }
          Messenger.error(message);
        }
      }
    });
  }
};

var ContentCreator = (function() { this.initialize.apply(this, arguments); });
ContentCreator.getContent = (function() {
  var w = (unsafeWindow||window);
  var result = '', content = w.getSelection().toString();
  if (content) {
    result += ('<blockquote cite="' + StringManager.xmlSpecialChars(location.href) + '">');
    result += content;
    result += '</blockquote>';
  }
  return result;
});
ContentCreator.getTitle = (function() {
  var w = (unsafeWindow||window);
  var document = w.document;
  var result = '';
  result += 'via ';
  result += ('<a href="' + StringManager.xmlSpecialChars(location.href) + '">');
  result += StringManager.xmlSpecialChars(document.title);
  result += '</a>'
  return result;
});

var Blog = (function() { this.initialize.apply(this, arguments); });
Blog.prototype = {
  initialize : function() {
    this.readyToPost = false;
    this.isMenuRegistered = false;
    this.isPostMenuRegistered = false;
    this.options = this.loadOptions();
    this.registerSettingMenu();
    this.registerPostMenu();
  },
  loadOptions : function() {
    var result = {}, options = Blog.options, currentOption;
    for (var i = 0, len = options.length; i < len; i++) {
      currentOption = options[i];
      result[currentOption.prefName] = this.getOption(currentOption.prefName, '');
    }
    return result;
  },
  checkToReady : function(options) {
    return !!(
      (options.apiUrl != '') && 
      (options.blogId != '') && 
      (options.userName != '')
    );
  },
  getOption : function(optionName, defaultValue) {
    return (Option.get(optionName, defaultValue)).getValue();
  },
  setOption : function(optionName, newValue) {
    (Option.get(optionName).setValue(newValue));
  },
  onSettingMenu : function() {
    var flgRepeat = true;
    var options = Blog.options, len = options.length;
    var currentOption, tempOptions = {}, inputted;
    for (var i = 0; i < len; i++) {
      currentOption = options[i];
      inputted = prompt(currentOption.message, this.options[currentOption.prefName]);
      if (inputted === null) {
        return;
      } else {
        tempOptions[currentOption.prefName] = inputted;
      }
    }
    if (!this.checkToReady(tempOptions)) {
      return;
    }
    for (i = 0; i < len; i++) {
      currentOption = options[i];
      this.options[currentOption.prefName] = tempOptions[currentOption.prefName];
      this.setOption(currentOption.prefName, this.options[currentOption.prefName]);
    }
    this.registerPostMenu();
  },
  registerSettingMenu : function() {
    var thisBlog = this;
    Menu.register(
      'EasyScrapper - Setting',
      (function() {
        thisBlog.onSettingMenu();
      })
    );
  },
  registerPostMenu : function() {
    if (this.checkToReady(this.options) && !this.isPostMenuRegistered) {
      var thisBlog = this;
      this.isPostMenuRegistered = true;
      Menu.register(
        'EasyScrapper - Post',
        (function() {
          var title = ContentCreator.getTitle();
          var content = ContentCreator.getContent();
          var publish = true;
          thisBlog.post(title, content, publish);
        }),
        this.options.accelKey,
        this.options.accessKey
      );
    }
  },
  post : function(title, content, publish) {
    if (!this.checkToReady(this.options)) {
      return;
    }
    var password = (
      this.options.password ||
      prompt('Enter password', '') ||
      ''
    );
    var pingMessage = new XMLRPC.Message('metaWeblog.newPost');
    pingMessage.addParam(
      new XMLRPC.Value.String(this.options.blogId), // blogid
      new XMLRPC.Value.String(this.options.userName), // username
      new XMLRPC.Value.String(password), // password
      new XMLRPC.Value.Struct({
        'title' : new XMLRPC.Value.String(title),
        'description' : new XMLRPC.Value.String(content),
        'mt_allow_comments' : new XMLRPC.Value.Boolean(true) // for Nucleus 3.3
      }),
      new XMLRPC.Value.Boolean(!!(publish)) // publish
    );
    pingMessage.post(this.options.apiUrl);
  }
};
Blog.options = [
  { prefName : 'apiUrl', message : 'Enter XML-RPC API URL' },
  { prefName : 'blogId', message : 'Enter blog ID' },
  { prefName : 'userName', message : 'Enter your user name' },
  { prefName : 'password', message : 'Enter password (optional)' },
  { prefName : 'accelKey', message : 'Enter accel key (ex. "alt s") (optional)'},
  { prefName : 'accessKey', message : 'Enter access key (optional)'}
];

(function() {
  if (self.location.href==top.location.href) {
    new Blog();
  }
})();