var easyDragToGo = {

  loaded: false,
  moving: false,
  StartAlready: false,
  onStartEvent: null,       // drag start event
  onDropEvent: null,        // drag drop event
  aXferData: null,          // drag data
  aDragSession: null,       // drag session

  onLoad: function() {
    if (!easyDragToGo.loaded) {
      var contentArea = getBrowser().mPanelContainer;
      if (contentArea) {
        eval("nsDragAndDrop.checkCanDrop =" + nsDragAndDrop.checkCanDrop.toString().replace(
          'if ("canDrop" in aDragDropObserver)',
          'if (easyDragToGo.StartAlready) this.mDragSession.canDrop = true; $&')
        );
        contentArea.addEventListener('draggesture', function(e) {easyDragToGo.dragStart(e)}, true);
        contentArea.addEventListener('dragover', function(e) {
            nsDragAndDrop.dragOver(e, easyDragToGoDNDObserver);
          }, false);
        contentArea.addEventListener('dragdrop', function(e) {
            nsDragAndDrop.drop(e, easyDragToGoDNDObserver);
          }, false);
        contentArea.addEventListener('drop', function(e) {
            nsDragAndDrop.drop(e, easyDragToGoDNDObserver);
          }, false);
      }
      easyDragToGo.loaded = true;
    }
  },

  dragStart: function(aEvent) {
    this.onStartEvent = aEvent;
    this.StartAlready = true;
  },

  clean: function() {
    this.StartAlready = false;
    if (this.onDropEvent) {
      this.onDropEvent.preventDefault();
      this.onDropEvent.stopPropagation();
    }
    this.onStartEvent = this.onDropEvent = this.aXferData = this.aDragSession = null;
  },

  openURL: function(aURI, src, target, X, Y) {
    if (!aURI) return;

    var act = "";
    var browser = getTopWin().getBrowser();
    var uri = "";
    var bg = true;
    var postData = {};

    act = Y<0 ? "link-fg" : "link-bg";
    // get search strings
    if ((target == "text" || target == "fromContentOuter.text")) {
      var submission = this.getSearchSubmission(aURI, act);
      if (submission) {
        uri = submission.uri.spec;
        postData.value = submission.postData;
        if (uri)
          act = act.replace("link", "search");
        else
          act = "";
      }
      else
        act = "";
      if (!act) alert("No Search Engines!");
    }

    switch (act) {
      case "search-fg":
      case "link-fg":
        // open a new tab and selected it
        bg = false;
      case "search-bg":
      case "link-bg":
        if (!uri) uri = getShortcutOrURI(aURI, postData);
        try {
          var cur = (!bg || browser.mTabs.length == 1) &&
                browser.webNavigation.currentURI.spec == "about:blank" &&
                !browser.mCurrentBrowser.webProgress.isLoadingDocument ||
                (/^(javascript|mailto):/i.test(uri));
        } catch(e) {}
        if (cur)
          // open in current tab
          loadURI(uri, null, postData.value, true);
        else {
          // for Tree Style Tab extension
          if ("TreeStyleTabService" in window && (target == "link" && !this.aDragSession.sourceNode.localName || target == "img"))
            try {TreeStyleTabService.readyToOpenChildTab(gBrowser.selectedTab);} catch(e) {}

          // open a new tab
          browser.loadOneTab(uri, null, null, postData.value, bg, true);
        }
        break;
      default:
        break;
    }
  },

  customCode: function(code, url, src, target, X, Y) {
    eval(code);
  },

  getSearchSubmission: function(searchStr, action) {
    try {
      var ss = Components.classes["@mozilla.org/browser/search-service;1"]
                .getService(Components.interfaces.nsIBrowserSearchService);
      var engine, engineName;
      if ( /^search-(.+?)-?(fg|bg|cur)$/.test(action) )
        engineName = RegExp.$1;
      else
        engineName = "c";

      if ( engineName == "c" )
        engine = ss.currentEngine || ss.defaultEngine;
      else if ( engineName == "d" )
        engine = ss.defaultEngine || ss.currentEngine;
      else {
        engine = ss.getEngineByName(engineName);
        if (!engine) engine = ss.currentEngine || ss.defaultEngine;
      }
      return engine.getSubmission(searchStr, null);
    }
    catch (e) {
      return null;
    }
  },

  SelectedText: function(node) {
    if (!node) return "";
    if ( node.localName == "TEXTAREA" || (node.localName == "INPUT" && node.type == "text") )
      return node.value.substring(node.selectionStart, node.selectionEnd);
    else
      return document.commandDispatcher.focusedWindow.getSelection().toString();
  },

  seemAsURL: function(url) {
      // url test
      var DomainName = /(\w+(\-+\w+)*\.)+\w{2,7}/;
      var HasSpace = /\S\s+\S/;
      var KnowNameOrSlash = /^(www|bbs|forum|blog)|\//;
      var KnowTopDomain1 = /\.(com|net|org|gov|edu|info|mobi|mil|asia)$/;
      var KnowTopDomain2 = /\.(de|uk|eu|nl|it|cn|be|us|br|jp|ch|fr|at|se|es|cz|pt|ca|ru|hk|tw|pl)$/;
      var IsIpAddress = /^([1-2]?\d?\d\.){3}[1-2]?\d?\d/;
      return !HasSpace.test(url) && DomainName.test(url) && (KnowNameOrSlash.
          test(url) || KnowTopDomain1.test(url) || KnowTopDomain2.test(url) ||
          IsIpAddress.test(url));
  },

  getForceURL: function(url) {
    var code;
    var str = "";
    url = url.replace(/\s|\r|\n|\u3000/g, "");
    for (var i = 0; i < url.length; i++) {
      code = url.charCodeAt(i);
      if (code >= 65281 && code <= 65373)
        str += String.fromCharCode(code - 65248);
      else
        str += url.charAt(i);
    }
    str = this.fixupSchemer(str);
    str = this.SecurityCheckURL(str);
    return str;
  },

  SecurityCheckURL: function(aURI) {
    if ( /^data:/.test(aURI) ) return "";
    if ( /^javascript:/.test(aURI) || /^about:/.test(aURI)) return aURI;
    var sourceURL = getBrowser().currentURI.spec;
    const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
    var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
                  .getService(nsIScriptSecurityManager);
    const nsIScriptSecMan = Components.interfaces.nsIScriptSecurityManager;
    try {
      secMan.checkLoadURIStr(sourceURL, aURI, nsIScriptSecMan.STANDARD);
    } catch(e) {
      aURI = "";
    }
    return aURI;
  },

  fixupSchemer: function(aURI) {
    if ( /^(?::\/\/|\/\/|\/)?(([1-2]?\d?\d\.){3}[1-2]?\d?\d(\/.*)?|[a-z]+[\-\w]+\.[\-\w\.]+(\/.*)?)$/i.test(aURI) )
      aURI = "http://" + RegExp.$1;
    else if ( /^\w+[\-\.\w]*@(\w+(\-+\w+)*\.)+\w{2,7}$/.test(aURI) )
      aURI = "mailto:" + aURI;
    else {
      var table = "ttp=>http,tp=>http,p=>http,ttps=>https,tps=>https,ps=>https,s=>https";
      var regexp = new RegExp();
      if (aURI.match(regexp.compile('^('+ table.replace(/=>[^,]+|=>[^,]+$/g, '').replace(/\s*,\s*/g, '|')+'):', 'g'))) {
        var target = RegExp.$1;
        table.match(regexp.compile('(,|^)'+target+'=>([^,]+)'));
        aURI = aURI.replace(target, RegExp.$2);
      }
    }
    return aURI;
  }
};

var easyDragToGoDNDObserver = {

  onDragOver: function(aEvent, aFlavour, aDragSession) {
    aDragSession.canDrop = true;
    // for drag tabs or bookmarks
    if (!easyDragToGo.StartAlready) {
      easyDragToGo.onStartEvent = aEvent;
      easyDragToGo.StartAlready = true;
    }
  },

  onDrop: function(aEvent, aXferData, aDragSession) {
    if (!easyDragToGo.StartAlready) return;
    easyDragToGo.onDropEvent = aEvent;
    easyDragToGo.aXferData = aXferData;
    easyDragToGo.aDragSession = aDragSession;

    var sNode = aDragSession.sourceNode;
    var url;
    if ( !sNode ) {
      // Drag and Drop from content outer
      try {url = aXferData.data.replace( /^[\s\n]+|[\s\n]+$/g, '' )} catch(e) {}
      if (!url) {
        easyDragToGo.clean();
        return;
      }
      var target = "fromContentOuter.text";
      if ( easyDragToGo.seemAsURL(url) || (/^file:\/\/\/[\S]+$/.test(url)) ) {
        //force it to a url or local file/directory
        if ( /^file:\/\/\//.test(url)) {
          if ( /([^\/]+\.(xpi|jar))$/.test(url) ) {
            eval("InstallTrigger.install({ '" + RegExp.$1 + "' : url })");
            easyDragToGo.clean();
            return;
          }
          else
            target = "fromContentOuter.link";
        }
        else {
          var tmpurl = url;
          url = easyDragToGo.fixupSchemer(url);
          url = easyDragToGo.SecurityCheckURL(url);
          if (url)
            target = "fromContentOuter.link";
          else
            url = tmpurl;
        }
      }
      easyDragToGo.openURL(url, null, target);
    }
    else {
      // Drag and Drop from Content area
      var relX = aEvent.screenX - easyDragToGo.onStartEvent.screenX;
      var relY = aEvent.screenY - easyDragToGo.onStartEvent.screenY;
      // do nothing with drag distance less than 10px
      if ( Math.abs(relX) < 10 && Math.abs(relY) < 10 ) {
        easyDragToGo.clean();
        return;
      }

      var str, src;
      var selectStr =  "";
      var type = "STRING";
      var target = "link";

      url = str = aXferData.data.replace( /\r\n/g, "\n").replace( /\r/g, "\n");

      try {
        selectStr = easyDragToGo.SelectedText(easyDragToGo.onStartEvent.target);
        selectStr = selectStr.replace( /\r\n/g, "\n").replace( /\r/g, "\n");
      } catch(e) {}

      if (str != selectStr) {
        var idx = str.indexOf("\n");
        if (idx > 0) {
          url = str.substr(0, idx);
          str = str.substr(idx + 1);
        }
        if (str == selectStr)
          url = str;
        else if ( !(/\s|\n/.test(url)) && (/^([a-z]{2,7}:\/\/|mailto:|about:|javascript:)/i.test(url)) )
          type = "URL";
        else
          url = selectStr;
      }
      else if (!(/\s|\n/.test(url)) && (/^([a-z]{2,7}:\/\/|mailto:|about:|javascript:)/i.test(url)))
        type = "URL";

      url = url.replace( /^[\s\n]+|[\s\n]+$/g, '' );

      if ( url && type == "URL" ) {
        src = url = easyDragToGo.SecurityCheckURL(url);

        if (sNode.nodeName == "IMG" || sNode.nodeName == "A" && !sNode.textContent && sNode.firstElementChild instanceof HTMLImageElement) {
          try {src = sNode.src || sNode.firstElementChild.src;} catch(e) {}
          target = "img";
        }
        else if (aEvent.ctrlKey) {
          // as text with ctrlkey
          var aNode = easyDragToGo.onStartEvent.target;
          while (aNode && aNode.nodeName != "A") aNode = aNode.parentNode;
          if (aNode && aNode.textContent) {
            url = aNode.textContent;
            target = "text";
          }
        }
      }
      else if (url) {
        var tmpurl = url;
        if (aEvent.ctrlKey) {
          url = easyDragToGo.getForceURL(url)    // force convert to a url
          if (url)
            target = "link";
          else
            url = tmpurl;
        }
        else if ( easyDragToGo.seemAsURL(url) ) { //seem as a url
          url = easyDragToGo.fixupSchemer(url);
          url = easyDragToGo.SecurityCheckURL(url);
          if (!url) { // not a url, search it
            url = tmpurl;
            target = "text";
          }
        }
        else  //it's a text string, so search it
          target = "text";
      }

      easyDragToGo.openURL(url, src, target, relX, relY);
    }

    easyDragToGo.clean();
  },

  getSupportedFlavours: function() {
    var flavourSet = new FlavourSet();
    flavourSet.appendFlavour("text/x-moz-url");
    flavourSet.appendFlavour("text/unicode");
    return flavourSet;
  }
};
easyDragToGo.onLoad();