<commit_before>/**
 * Mail autocomplete
 * Author: nuintun
 * $(selector).mailtip({
 *   mails: [], // mails
 *   onselected： function(mail){}, // callback on selected
 *   width: 'auto', // popup tip's width
 *   offsetTop: -1, // offset top relative default position
 *   offsetLeft: 0, // offset left relative default position
 *   zIndex: 10 // popup tip's z-index
 * });
 */

'use strict';

(function ($){
  // is ie 9
  var ISIE9 = /MSIE 9.0/i.test(window.navigator.userAgent);
  // is support oninput event
  var hasInputEvent = 'oninput' in document.createElement('input');

  /**
   * is a number
   * @param val
   * @returns {boolean}
   */
  function isNumber(val){
    return typeof val === 'number' && isFinite(val);
  }

  /**
   * parse string to regexp
   * @param pattern
   * @param attributes
   * @returns {RegExp}
   */
  function parseRegExp(pattern, attributes){
    var imp = /[\^\.\\\|\(\)\*\+\-\$\[\]\?]/igm;

    pattern = pattern.replace(imp, function (match){
      return '\\' + match;
    });

    return new RegExp(pattern, attributes);
  }

  /**
   * create popup tip
   * @param input
   * @param config
   * @returns {*}
   */
  function createTip(input, config){
    var tip = null;

    // only create tip and binding event once
    if (!input.data('data-mailtip')) {
      var wrap = input.parent();

      // set parent node position
      !/absolute|relative/i.test(wrap.css('position')) && wrap.css('position', 'relative');
      // off input autocomplete
      input.attr('autocomplete', 'off');

      var offset = input.offset();
      var wrapOffset = wrap.offset();

      tip = $('

');

      // insert tip after input
      input.after(tip);

      // set tip style
      tip.css({
        top: offset.top - wrapOffset.top + input.outerHeight() + config.offsetTop,
        left: offset.left - wrapOffset.left + config.offsetLeft,
        width: config.width === 'input' ? input.outerWidth() - tip.outerWidth() + tip.width() : config.width
      });

      // when width is auto, set min width equal input width
      if (config.width === 'auto') {
        tip.css('min-width', input.outerWidth() - tip.outerWidth() + tip.width());
      }

      // binding event
      tip.on('mouseenter mouseleave click', 'li', function (e){
        var selected = $(this);

        switch (e.type) {
          case 'mouseenter':
            selected.addClass('hover');
            break;
          case 'click':
            var mail = selected.attr('title');

            input.val(mail).focus();
            config.onselected.call(input[0], mail);
            break;
          case 'mouseleave':
            selected.removeClass('hover');
            break;
          default:
            break;
        }
      });

      // when on click if the target element not input, hide tip
      $(document).on('click', function (e){
        if (e.target === input[0]) return;

        tip.hide();
      });

      input.data('data-mailtip', tip);
    }

    return tip || input.data('data-mailtip');
  }

  /**
   * create mail list
   * @param value
   * @param mails
   * @returns {*}
   */
  function createLists(value, mails){
    var arr;
    var regx;
    var lists = '';
    var hasAt = /@/.test(value);

    if (hasAt) {
      arr = value.split('@');

      if (arr.length > 2) return lists;

      value = arr[0];
      regx = parseRegExp('^' + arr[1], 'i');
    }

    for (var i = 0, len = mails.length; i < len; i++) {
      if (hasAt && !regx.test(mails[i])) continue;

      lists += '

'
        + value + '@' + mails[i] + '

';
    }

    return lists.replace(/^
]*)>/, '
');
  }

  /**
   * change list active state
   * @param panle
   * @param up
   */
  function changeActive(panle, up){
    // if tip is visible do nothing
    if (panle.css('display') === 'none') return;

    var liActive = panle.find('li.active');

    if (up) {
      var liPrev = liActive.prev();

      liPrev = liPrev.length ? liPrev : panle.find('li:last');
      liActive.removeClass('active');
      liPrev.addClass('active');
    } else {
      var liNext = liActive.next();

      liNext = liNext.length ? liNext : panle.find('li:first');
      liActive.removeClass('active');
      liNext.addClass('active');
    }
  }

  /**
   * toggle tip
   * @param val
   * @param tip
   * @param mails
   */
  function toggleTip(val, tip, mails){
    // if input text is empty or has space char, chinese char, comma or begin with @ or more than two @, hide tip
    //如果输入为空，带空格，中文字符，英文逗号，@开头，或者两个以上@直接隐藏提示
    if (!val || /[,]|[\u4e00-\u9fa5]|\s|^@/.test(val) || val.split('@').length > 2) {
      tip.hide();
    } else {
      var lists = createLists(val, mails);

      // if has match mails show tip
      if (lists) {
        tip.html(lists).show();
      } else {
        tip.hide();
      }
    }
  }

  /**
   * exports
   * @param config
   * @returns {*}
   */
  $.fn.mailtip = function (config){
    var defaults = {
      mails: [
        'qq.com', '163.com', 'sina.com',
        'gmail.com', '126.com', '139.com',
        '189.com', 'sohu.com', 'msn.com',
        'hotmail.com', 'yahoo.com', 'yahoo.com.cn'
      ],
      onselected: $.noop,
      width: 'auto',
      offsetTop: -1,
      offsetLeft: 0,
      zIndex: 10
    };

    config = $.extend({}, defaults, config);
    config.zIndex = isNumber(config.zIndex) ? config.zIndex : defaults.zIndex;
    config.offsetTop = isNumber(config.offsetTop) ? config.offsetTop : defaults.offsetTop;
    config.offsetLeft = isNumber(config.offsetLeft) ? config.offsetLeft : defaults.offsetLeft;
    config.onselected = $.isFunction(config.onselected) ? config.onselected : defaults.onselected;
    config.width = config.width === 'input' || isNumber(config.width) ? config.width : defaults.width;

    return this.each(function (){
      // input
      var input = $(this);
      // tip
      var tip = createTip(input, config);

      // binding event
      input.on('keydown input propertychange', function (e){
        if (e.type === 'keydown') {
          switch (e.keyCode) {
            // backspace
            case 8:
              // shit! ie9 input event has a bug, backspace do not trigger input event
              ISIE9 && input.trigger('input');
              break;
            case 9:
              tip.hide();
              break;
            // up
            case 38:
              changeActive(tip, true);
              break;
            // down
            case 40:
              changeActive(tip);
              break;
            // enter
            case 13:
              if (tip.css('display') === 'none') return;

              e.preventDefault();

              var mail = tip.find('li.active').attr('title');

              tip.hide();
              input.val(mail).focus();
              config.onselected.call(input[0], mail);
              break;
            default:
              break;
          }
        } else {
          if (hasInputEvent) {
            toggleTip(input.val(), tip, config.mails);
          } else if (e.originalEvent.propertyName === 'value') {
            toggleTip(input.val(), tip, config.mails);
          }
        }
      });

      // shit! ie9 input event has a bug, backspace do not trigger input event
      ISIE9 && input.on('keyup', function (e){
        e.keyCode === 8 && toggleTip(input.val(), tip, config.mails);
      });
    });
  };
}(jQuery));
<commit_msg>bug fixed<commit_after>/**
 * Mail autocomplete
 * Author: nuintun
 * $(selector).mailtip({
 *   mails: [], // mails
 *   onselected： function(mail){}, // callback on selected
 *   width: 'auto', // popup tip's width
 *   offsetTop: -1, // offset top relative default position
 *   offsetLeft: 0, // offset left relative default position
 *   zIndex: 10 // popup tip's z-index
 * });
 */

'use strict';

(function ($){
  // escape text regexp
  var IMPRE = /[\^\.\\\|\(\)\*\+\-\$\[\]\?]/gm;
  // is ie 9
  var ISIE9 = /MSIE 9.0/i.test(window.navigator.userAgent);
  // is support oninput event
  var hasInputEvent = 'oninput' in document.createElement('input');

  /**
   * is a number
   * @param value
   * @returns {boolean}
   */
  function isNumber(value){
    return typeof value === 'number' && isFinite(value);
  }

  /**
   * escape text
   * @param text
   * @returns {string}
   */
  function escapeText(text){
    return text.replace(IMPRE, function (match){
      return '\\' + match;
    });
  }

  /**
   * create popup tip
   * @param input
   * @param config
   * @returns {*}
   */
  function createTip(input, config){
    var tip = null;

    // only create tip and binding event once
    if (!input.data('data-mailtip')) {
      var wrap = input.parent();

      // set parent node position
      !/absolute|relative/i.test(wrap.css('position')) && wrap.css('position', 'relative');
      // off input autocomplete
      input.attr('autocomplete', 'off');

      var offset = input.offset();
      var wrapOffset = wrap.offset();

      tip = $('

');

      // insert tip after input
      input.after(tip);

      // set tip style
      tip.css({
        top: offset.top - wrapOffset.top + input.outerHeight() + config.offsetTop,
        left: offset.left - wrapOffset.left + config.offsetLeft,
        width: config.width === 'input' ? input.outerWidth() - tip.outerWidth() + tip.width() : config.width
      });

      // when width is auto, set min width equal input width
      if (config.width === 'auto') {
        tip.css('min-width', input.outerWidth() - tip.outerWidth() + tip.width());
      }

      // binding event
      tip.on('mouseenter mouseleave click', 'li', function (e){
        var selected = $(this);

        switch (e.type) {
          case 'mouseenter':
            selected.addClass('hover');
            break;
          case 'click':
            var mail = selected.attr('title');

            input.val(mail).focus();
            config.onselected.call(input[0], mail);
            break;
          case 'mouseleave':
            selected.removeClass('hover');
            break;
          default:
            break;
        }
      });

      // when on click if the target element not input, hide tip
      $(document).on('click', function (e){
        if (e.target === input[0]) return;

        tip.hide();
      });

      input.data('data-mailtip', tip);
    }

    return tip || input.data('data-mailtip');
  }

  /**
   * create mail list
   * @param value
   * @param mails
   * @returns {*}
   */
  function createLists(value, mails){
    var regx;
    var lists = '';
    var atIndex = value.indexOf('@');
    var hasAt = atIndex !== -1;

    if (hasAt) {
      regx = new RegExp('^' + escapeText(value.substring(atIndex + 1)), 'i');
      value = value.substring(0, atIndex);
    }

    for (var i = 0, len = mails.length; i < len; i++) {
      if (hasAt && !regx.test(mails[i])) continue;

      lists += '

'
        + value + '@' + mails[i] + '

';
    }

    return lists.replace(/^
]*)>/, '
');
  }

  /**
   * change list active state
   * @param panle
   * @param up
   */
  function changeActive(panle, up){
    // if tip is visible do nothing
    if (panle.css('display') === 'none') return;

    var liActive = panle.find('li.active');

    if (up) {
      var liPrev = liActive.prev();

      liPrev = liPrev.length ? liPrev : panle.find('li:last');
      liActive.removeClass('active');
      liPrev.addClass('active');
    } else {
      var liNext = liActive.next();

      liNext = liNext.length ? liNext : panle.find('li:first');
      liActive.removeClass('active');
      liNext.addClass('active');
    }
  }

  /**
   * toggle tip
   * @param value
   * @param tip
   * @param mails
   */
  function toggleTip(value, tip, mails){
    // if input text is empty or has space char, chinese char, comma or begin with @ or more than two @, hide tip
    //如果输入为空，带空格，中文字符，英文逗号，@开头，或者两个以上@直接隐藏提示
    if (!value || /[,]|[\u4e00-\u9fa5]|\s|^@/.test(value) || value.indexOf('@') !== value.lastIndexOf('@')) {
      tip.hide();
    } else {
      var lists = createLists(value, mails);

      // if has match mails show tip
      if (lists) {
        tip.html(lists).show();
      } else {
        tip.hide();
      }
    }
  }

  /**
   * exports
   * @param config
   * @returns {*}
   */
  $.fn.mailtip = function (config){
    var defaults = {
      mails: [
        'qq.com', '163.com', 'sina.com',
        'gmail.com', '126.com', '139.com',
        '189.com', 'sohu.com', 'msn.com',
        'hotmail.com', 'yahoo.com', 'yahoo.com.cn'
      ],
      onselected: $.noop,
      width: 'auto',
      offsetTop: -1,
      offsetLeft: 0,
      zIndex: 10
    };

    config = $.extend({}, defaults, config);
    config.zIndex = isNumber(config.zIndex) ? config.zIndex : defaults.zIndex;
    config.offsetTop = isNumber(config.offsetTop) ? config.offsetTop : defaults.offsetTop;
    config.offsetLeft = isNumber(config.offsetLeft) ? config.offsetLeft : defaults.offsetLeft;
    config.onselected = $.isFunction(config.onselected) ? config.onselected : defaults.onselected;
    config.width = config.width === 'input' || isNumber(config.width) ? config.width : defaults.width;

    return this.each(function (){
      // input
      var input = $(this);
      // tip
      var tip = createTip(input, config);

      // binding event
      input.on('keydown input propertychange', function (e){
        if (e.type === 'keydown') {
          switch (e.keyCode) {
            // backspace
            case 8:
              // shit! ie9 input event has a bug, backspace do not trigger input event
              ISIE9 && input.trigger('input');
              break;
            case 9:
              tip.hide();
              break;
            // up
            case 38:
              changeActive(tip, true);
              break;
            // down
            case 40:
              changeActive(tip);
              break;
            // enter
            case 13:
              if (tip.css('display') === 'none') return;

              e.preventDefault();

              var mail = tip.find('li.active').attr('title');

              tip.hide();
              input.val(mail).focus();
              config.onselected.call(input[0], mail);
              break;
            default:
              break;
          }
        } else {
          if (hasInputEvent) {
            toggleTip(input.val(), tip, config.mails);
          } else if (e.originalEvent.propertyName === 'value') {
            toggleTip(input.val(), tip, config.mails);
          }
        }
      });

      // shit! ie9 input event has a bug, backspace do not trigger input event
      ISIE9 && input.on('keyup', function (e){
        e.keyCode === 8 && toggleTip(input.val(), tip, config.mails);
      });
    });
  };
}(jQuery));
<|endoftext|>


<commit_before>/* global console, MashupPlatform, moment */

var CalendarAPI = (function () {
    "use strict";

    var url = "http://130.206.113.159:8085/api/v1";

    /*****************************************************************
    *                     C O N S T R U C T O R                      *
    *****************************************************************/

    function CalendarAPI () {

    }

    /******************************************************************/
    /*                P R I V A T E   F U N C T I O N S               */
    /******************************************************************/
    function getAllEvents(success, error) {
      MashupPlatform.http.makeRequest(url + "/events", {
        method: 'GET',
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function addEvent(event, success, error) {
      MashupPlatform.http.makeRequest(url + "/events", {
        method: 'POST',
        contentType: "application/json",
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        postBody: JSON.stringify(event),
        onSuccess: success,
        onError: error
      });
    }

    function deleteEvent(event, success, error) {
      MashupPlatform.http.makeRequest(url + "/events/" + event.uid, {
        method: 'DELETE',
        requestHeaders: {
        "X-FI-WARE-OAuth-Token": "true",
        "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function updateEvent(event, success, error) {
      //TODO: Implementar con la API cuando este disponible.
      deleteEvent(event, function() {
        console.log("Event removed (Update) successfully.");
        event.uid = null;
        addEvent(event, success, error);
      }, error);
    }

    function test(event, success, error) {
      MashupPlatform.http.makeRequest(url, {
        method: 'GET',
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function createEventObject(uid, description, summary, location, dtstart, dtend, dtstamp) {
      var event = {};
      if (uid !== null) event.uid = uid;
      if (description !== null) event.description = description;
      if (summary !== null) event.summary = summary;
      if (location !== null) event.location = location;
      if (dtstart !== null) event.dtstart = dtstart;
      if (dtend !== null) event.dtend = dtend;
      if (dtstamp !== null) event.dtstamp = dtstamp;
      return event;
    }

    /******************************************************************/
    /*                 P U B L I C   F U N C T I O N S                */
    /******************************************************************/

    CalendarAPI.prototype = {
      getAllEvents: function (success, error) {
        getAllEvents(success, error);
      },
      addEvent: function (event, success, error) {
        addEvent(event, success, error);
      },
      deleteEvent: function (event, success, error) {
        deleteEvent(event, success, error);
      },
      updateEvent: function (event, success, error) {
        updateEvent(event, success, error);
      },
      test: function (success, error) {
        test(success, error);
      }
    };

    return CalendarAPI;
})();<commit_msg>Remove unnecesary global variable<commit_after>/* global console, MashupPlatform */

var CalendarAPI = (function () {
    "use strict";

    var url = "http://130.206.113.159:8085/api/v1";

    /*****************************************************************
    *                     C O N S T R U C T O R                      *
    *****************************************************************/

    function CalendarAPI () {

    }

    /******************************************************************/
    /*                P R I V A T E   F U N C T I O N S               */
    /******************************************************************/
    function getAllEvents(success, error) {
      MashupPlatform.http.makeRequest(url + "/events", {
        method: 'GET',
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function addEvent(event, success, error) {
      MashupPlatform.http.makeRequest(url + "/events", {
        method: 'POST',
        contentType: "application/json",
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        postBody: JSON.stringify(event),
        onSuccess: success,
        onError: error
      });
    }

    function deleteEvent(event, success, error) {
      MashupPlatform.http.makeRequest(url + "/events/" + event.uid, {
        method: 'DELETE',
        requestHeaders: {
        "X-FI-WARE-OAuth-Token": "true",
        "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function updateEvent(event, success, error) {
      //TODO: Implementar con la API cuando este disponible.
      deleteEvent(event, function() {
        console.log("Event removed (Update) successfully.");
        event.uid = null;
        addEvent(event, success, error);
      }, error);
    }

    function test(event, success, error) {
      MashupPlatform.http.makeRequest(url, {
        method: 'GET',
        requestHeaders: {
          "X-FI-WARE-OAuth-Token": "true",
          "X-FI-WARE-OAuth-Header-Name": "X-Auth-Token"
        },
        onSuccess: success,
        onError: error
      });
    }

    function createEventObject(uid, description, summary, location, dtstart, dtend, dtstamp) {
      var event = {};
      if (uid !== null) event.uid = uid;
      if (description !== null) event.description = description;
      if (summary !== null) event.summary = summary;
      if (location !== null) event.location = location;
      if (dtstart !== null) event.dtstart = dtstart;
      if (dtend !== null) event.dtend = dtend;
      if (dtstamp !== null) event.dtstamp = dtstamp;
      return event;
    }

    /******************************************************************/
    /*                 P U B L I C   F U N C T I O N S                */
    /******************************************************************/

    CalendarAPI.prototype = {
      getAllEvents: function (success, error) {
        getAllEvents(success, error);
      },
      addEvent: function (event, success, error) {
        addEvent(event, success, error);
      },
      deleteEvent: function (event, success, error) {
        deleteEvent(event, success, error);
      },
      updateEvent: function (event, success, error) {
        updateEvent(event, success, error);
      },
      test: function (success, error) {
        test(success, error);
      }
    };

    return CalendarAPI;
})();<|endoftext|>


<commit_before>(function() {
  var sha = localStorage.getItem("tsitu-latest-sha");
  if (sha && sha !== 0) {
    var jsonTimestamp = new Promise(function(resolve, reject) {
      var cdn =
        "https://cdn.jsdelivr.net/gh/tsitu/MH-Tools@" +
        sha +
        "/data/json/bookmarklet-timestamps.json";
      var xhr = new XMLHttpRequest();
      xhr.open("GET", cdn);
      xhr.onload = function() {
        resolve(xhr.responseText);
      };
      xhr.onerror = function() {
        reject(xhr.statusText);
      };
      xhr.send();
    });

    jsonTimestamp.then(function(response) {
      // Use cached SHA to eliminate excessive requests in loadBookmarklet()
      // Must reload/refresh to get individually updated bookmarklet versions
      buildUI(JSON.parse(response), sha);
    });
  }

  function buildUI(timestamps, sha) {
    var mainDiv = document.createElement("div");
    mainDiv.id = "mht-bookmarklet-loader";
    var loaderTime = "Last updated: " + (timestamps["menu"] || "N/A");
    var creTime = "Last updated: " + (timestamps["cre"] || "N/A");
    var mapTime = "Last updated: " + (timestamps["map"] || "N/A");
    var setupItTime = "Last updated: " + (timestamps["setup_items"] || "N/A");
    var setupFiTime = "Last updated: " + (timestamps["setup_fields"] || "N/A");
    var analyzerTime = "Last updated: " + (timestamps["analyzer"] || "N/A");
    var crownTime = "Last updated: " + (timestamps["crown"] || "N/A");
    var craftingTime = "Last updated: " + (timestamps["crafting"] || "N/A");
    var powersTime = "Last updated: " + (timestamps["powers"] || "N/A");

    var closeButton = document.createElement("button", { id: "close-button" });
    closeButton.textContent = "x";
    closeButton.onclick = function() {
      document.body.removeChild(mainDiv);
    };

    var titleSpan = document.createElement("span");
    titleSpan.style.fontSize = "15px";
    titleSpan.style.fontWeight = "bold";
    titleSpan.appendChild(document.createTextNode("MH Tools Bookmarklets"));

    var descriptionSpan = document.createElement("span");
    descriptionSpan.innerHTML =
      "Version 1.6.0 / Using jsDelivr";
    var loaderSpanTimestamp = document.createElement("span");
    loaderSpanTimestamp.style.fontSize = "10px";
    loaderSpanTimestamp.style.fontStyle = "italic";
    loaderSpanTimestamp.innerHTML = loaderTime;

    var creButton = document.createElement("button", { id: "cre-button" });
    creButton.textContent = "Catch Rate Estimator";
    creButton.onclick = function() {
      loadBookmarklet("cre");
    };
    var creSpanTimestamp = document.createElement("span");
    creSpanTimestamp.style.fontSize = "10px";
    creSpanTimestamp.style.fontStyle = "italic";
    creSpanTimestamp.innerHTML = creTime;

    var mapButton = document.createElement("button", { id: "map-button" });
    mapButton.textContent = "Map Solver";
    mapButton.onclick = function() {
      loadBookmarklet("map");
    };
    var mapSpanTimestamp = document.createElement("span");
    mapSpanTimestamp.style.fontSize = "10px";
    mapSpanTimestamp.style.fontStyle = "italic";
    mapSpanTimestamp.innerHTML = mapTime;

    var setupButton = document.createElement("button", { id: "setup-button" });
    setupButton.textContent = "Best Setup: Load Items";
    setupButton.onclick = function() {
      loadBookmarklet("setup-items");
    };
    var setupSpanTimestamp = document.createElement("span");
    setupSpanTimestamp.style.fontSize = "10px";
    setupSpanTimestamp.style.fontStyle = "italic";
    setupSpanTimestamp.innerHTML = setupItTime;

    var setupFieldsButton = document.createElement("button", {
      id: "setup-fields-button"
    });
    setupFieldsButton.textContent = "Best Setup: Fields";
    setupFieldsButton.onclick = function() {
      loadBookmarklet("setup-fields");
    };
    var setupFieldsSpanTimestamp = document.createElement("span");
    setupFieldsSpanTimestamp.style.fontSize = "10px";
    setupFieldsSpanTimestamp.style.fontStyle = "italic";
    setupFieldsSpanTimestamp.innerHTML = setupFiTime;

    var analyzerButton = document.createElement("button", {
      id: "analyzer-button"
    });
    analyzerButton.textContent = "Marketplace Analyzer";
    analyzerButton.onclick = function() {
      loadBookmarklet("analyzer");
    };
    var analyzerSpanTimestamp = document.createElement("span");
    analyzerSpanTimestamp.style.fontSize = "10px";
    analyzerSpanTimestamp.style.fontStyle = "italic";
    analyzerSpanTimestamp.innerHTML = analyzerTime;

    var crownButton = document.createElement("button", { id: "crown-button" });
    crownButton.textContent = "Crown Solver";
    crownButton.onclick = function() {
      loadBookmarklet("crown");
    };
    var crownSpanTimestamp = document.createElement("span");
    crownSpanTimestamp.style.fontSize = "10px";
    crownSpanTimestamp.style.fontStyle = "italic";
    crownSpanTimestamp.innerHTML = crownTime;

    var craftingButton = document.createElement("button", {
      id: "crafting-button"
    });
    craftingButton.textContent = "Crafting Wizard";
    craftingButton.onclick = function() {
      loadBookmarklet("crafting");
    };
    var craftingSpanTimestamp = document.createElement("span");
    craftingSpanTimestamp.style.fontSize = "10px";
    craftingSpanTimestamp.style.fontStyle = "italic";
    craftingSpanTimestamp.innerHTML = craftingTime;

    var powersButton = document.createElement("button", {
      id: "powers-button"
    });
    powersButton.textContent = "Powers: Worksheet";
    powersButton.onclick = function() {
      loadBookmarklet("powers");
    };
    var powersSpanTimestamp = document.createElement("span");
    powersSpanTimestamp.style.fontSize = "10px";
    powersSpanTimestamp.style.fontStyle = "italic";
    powersSpanTimestamp.innerHTML = powersTime;

    mainDiv.appendChild(closeButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(titleSpan);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(descriptionSpan);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(loaderSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(creButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(creSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(mapButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(mapSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupFieldsButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupFieldsSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(analyzerButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(analyzerSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(crownButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(crownSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(craftingButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(craftingSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(powersButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(powersSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createTextNode("(Drag me around on a PC)"));

    mainDiv.style.backgroundColor = "#F5F5F5";
    mainDiv.style.position = "fixed";
    mainDiv.style.zIndex = "9999";
    // Allow customizable left position property
    mainDiv.style.left =
      typeof window.tsitu_loader_offset != "undefined"
        ? window.tsitu_loader_offset.concat("%")
        : "80%";
    mainDiv.style.top = "25px";
    mainDiv.style.border = "solid 3px #696969";
    mainDiv.style.borderRadius = "20px";
    mainDiv.style.padding = "10px";
    mainDiv.style.textAlign = "center";
    document.body.appendChild(mainDiv);
    dragElement(document.getElementById("mht-bookmarklet-loader"));
    locationQuickWiki();
  }

  /**
   * Adds a click handler to HUD location name that opens corresponding MHWiki link
   */
  function locationQuickWiki() {
    const locationEl = document.querySelector(".mousehuntHud-environmentName");
    if (locationEl && !locationEl.onclick) {
      locationEl.onclick = function() {
        const locationName = user.environment_name;
        var newWindow = window.open("");
        newWindow.location = `https://mhwiki.hitgrab.com/wiki/index.php/${locationName.replace(
          " ",
          "_"
        )}`;
      };
      locationEl.style.cursor = "pointer";

      // innerHTML is more elegant but impractical since the game updates it on location changes
      // locationEl.innerHTML = `${locationEl.innerHTML}`;
    }
  }

  /**
   * Fetches & executes specified bookmarklet variant
   * @param {string} type
   */
  function loadBookmarklet(type) {
    var el = document.createElement("script");
    var cdn =
      "https://cdn.jsdelivr.net/gh/tsitu/MH-Tools@" +
      sha +
      "/src/bookmarklet/bm-" +
      type +
      ".min.js";
    el.src = cdn;
    document.body.appendChild(el);
    el.onload = function() {
      el.remove();
    };
  }

  /**
   * Element dragging functionality
   * @param {HTMLElement} el
   */
  function dragElement(el) {
    var pos1 = 0,
      pos2 = 0,
      pos3 = 0,
      pos4 = 0;
    if (document.getElementById(el.id + "header")) {
      document.getElementById(el.id + "header").onmousedown = dragMouseDown;
    } else {
      el.onmousedown = dragMouseDown;
    }

    function dragMouseDown(e) {
      e = e || window.event;
      pos3 = e.clientX;
      pos4 = e.clientY;
      document.onmouseup = closeDragElement;
      document.onmousemove = elementDrag;
    }

    function elementDrag(e) {
      e = e || window.event;
      pos1 = pos3 - e.clientX;
      pos2 = pos4 - e.clientY;
      pos3 = e.clientX;
      pos4 = e.clientY;
      el.style.top = el.offsetTop - pos2 + "px";
      el.style.left = el.offsetLeft - pos1 + "px";
    }

    function closeDragElement() {
      document.onmouseup = null;
      document.onmousemove = null;
    }
  }
})();
<commit_msg>Moved bm-menu wiki link handler to location icon<commit_after>(function() {
  var sha = localStorage.getItem("tsitu-latest-sha");
  if (sha && sha !== 0) {
    var jsonTimestamp = new Promise(function(resolve, reject) {
      var cdn =
        "https://cdn.jsdelivr.net/gh/tsitu/MH-Tools@" +
        sha +
        "/data/json/bookmarklet-timestamps.json";
      var xhr = new XMLHttpRequest();
      xhr.open("GET", cdn);
      xhr.onload = function() {
        resolve(xhr.responseText);
      };
      xhr.onerror = function() {
        reject(xhr.statusText);
      };
      xhr.send();
    });

    jsonTimestamp.then(function(response) {
      // Use cached SHA to eliminate excessive requests in loadBookmarklet()
      // Must reload/refresh to get individually updated bookmarklet versions
      buildUI(JSON.parse(response), sha);
    });
  }

  function buildUI(timestamps, sha) {
    var mainDiv = document.createElement("div");
    mainDiv.id = "mht-bookmarklet-loader";
    var loaderTime = "Last updated: " + (timestamps["menu"] || "N/A");
    var creTime = "Last updated: " + (timestamps["cre"] || "N/A");
    var mapTime = "Last updated: " + (timestamps["map"] || "N/A");
    var setupItTime = "Last updated: " + (timestamps["setup_items"] || "N/A");
    var setupFiTime = "Last updated: " + (timestamps["setup_fields"] || "N/A");
    var analyzerTime = "Last updated: " + (timestamps["analyzer"] || "N/A");
    var crownTime = "Last updated: " + (timestamps["crown"] || "N/A");
    var craftingTime = "Last updated: " + (timestamps["crafting"] || "N/A");
    var powersTime = "Last updated: " + (timestamps["powers"] || "N/A");

    var closeButton = document.createElement("button", { id: "close-button" });
    closeButton.textContent = "x";
    closeButton.onclick = function() {
      document.body.removeChild(mainDiv);
    };

    var titleSpan = document.createElement("span");
    titleSpan.style.fontSize = "15px";
    titleSpan.style.fontWeight = "bold";
    titleSpan.appendChild(document.createTextNode("MH Tools Bookmarklets"));

    var descriptionSpan = document.createElement("span");
    descriptionSpan.innerHTML =
      "Version 1.6.0 / Using jsDelivr";
    var loaderSpanTimestamp = document.createElement("span");
    loaderSpanTimestamp.style.fontSize = "10px";
    loaderSpanTimestamp.style.fontStyle = "italic";
    loaderSpanTimestamp.innerHTML = loaderTime;

    var creButton = document.createElement("button", { id: "cre-button" });
    creButton.textContent = "Catch Rate Estimator";
    creButton.onclick = function() {
      loadBookmarklet("cre");
    };
    var creSpanTimestamp = document.createElement("span");
    creSpanTimestamp.style.fontSize = "10px";
    creSpanTimestamp.style.fontStyle = "italic";
    creSpanTimestamp.innerHTML = creTime;

    var mapButton = document.createElement("button", { id: "map-button" });
    mapButton.textContent = "Map Solver";
    mapButton.onclick = function() {
      loadBookmarklet("map");
    };
    var mapSpanTimestamp = document.createElement("span");
    mapSpanTimestamp.style.fontSize = "10px";
    mapSpanTimestamp.style.fontStyle = "italic";
    mapSpanTimestamp.innerHTML = mapTime;

    var setupButton = document.createElement("button", { id: "setup-button" });
    setupButton.textContent = "Best Setup: Load Items";
    setupButton.onclick = function() {
      loadBookmarklet("setup-items");
    };
    var setupSpanTimestamp = document.createElement("span");
    setupSpanTimestamp.style.fontSize = "10px";
    setupSpanTimestamp.style.fontStyle = "italic";
    setupSpanTimestamp.innerHTML = setupItTime;

    var setupFieldsButton = document.createElement("button", {
      id: "setup-fields-button"
    });
    setupFieldsButton.textContent = "Best Setup: Fields";
    setupFieldsButton.onclick = function() {
      loadBookmarklet("setup-fields");
    };
    var setupFieldsSpanTimestamp = document.createElement("span");
    setupFieldsSpanTimestamp.style.fontSize = "10px";
    setupFieldsSpanTimestamp.style.fontStyle = "italic";
    setupFieldsSpanTimestamp.innerHTML = setupFiTime;

    var analyzerButton = document.createElement("button", {
      id: "analyzer-button"
    });
    analyzerButton.textContent = "Marketplace Analyzer";
    analyzerButton.onclick = function() {
      loadBookmarklet("analyzer");
    };
    var analyzerSpanTimestamp = document.createElement("span");
    analyzerSpanTimestamp.style.fontSize = "10px";
    analyzerSpanTimestamp.style.fontStyle = "italic";
    analyzerSpanTimestamp.innerHTML = analyzerTime;

    var crownButton = document.createElement("button", { id: "crown-button" });
    crownButton.textContent = "Crown Solver";
    crownButton.onclick = function() {
      loadBookmarklet("crown");
    };
    var crownSpanTimestamp = document.createElement("span");
    crownSpanTimestamp.style.fontSize = "10px";
    crownSpanTimestamp.style.fontStyle = "italic";
    crownSpanTimestamp.innerHTML = crownTime;

    var craftingButton = document.createElement("button", {
      id: "crafting-button"
    });
    craftingButton.textContent = "Crafting Wizard";
    craftingButton.onclick = function() {
      loadBookmarklet("crafting");
    };
    var craftingSpanTimestamp = document.createElement("span");
    craftingSpanTimestamp.style.fontSize = "10px";
    craftingSpanTimestamp.style.fontStyle = "italic";
    craftingSpanTimestamp.innerHTML = craftingTime;

    var powersButton = document.createElement("button", {
      id: "powers-button"
    });
    powersButton.textContent = "Powers: Worksheet";
    powersButton.onclick = function() {
      loadBookmarklet("powers");
    };
    var powersSpanTimestamp = document.createElement("span");
    powersSpanTimestamp.style.fontSize = "10px";
    powersSpanTimestamp.style.fontStyle = "italic";
    powersSpanTimestamp.innerHTML = powersTime;

    mainDiv.appendChild(closeButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(titleSpan);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(descriptionSpan);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(loaderSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(creButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(creSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(mapButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(mapSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupFieldsButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(setupFieldsSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(analyzerButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(analyzerSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(crownButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(crownSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(craftingButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(craftingSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(powersButton);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(powersSpanTimestamp);
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createElement("br"));
    mainDiv.appendChild(document.createTextNode("(Drag me around on a PC)"));

    mainDiv.style.backgroundColor = "#F5F5F5";
    mainDiv.style.position = "fixed";
    mainDiv.style.zIndex = "9999";
    // Allow customizable left position property
    mainDiv.style.left =
      typeof window.tsitu_loader_offset != "undefined"
        ? window.tsitu_loader_offset.concat("%")
        : "80%";
    mainDiv.style.top = "25px";
    mainDiv.style.border = "solid 3px #696969";
    mainDiv.style.borderRadius = "20px";
    mainDiv.style.padding = "10px";
    mainDiv.style.textAlign = "center";
    document.body.appendChild(mainDiv);
    dragElement(document.getElementById("mht-bookmarklet-loader"));
    locationQuickWiki();
  }

  /**
   * Adds a click handler to HUD location name that opens corresponding MHWiki link
   */
  function locationQuickWiki() {
    const locationEl = document.querySelector(".mousehuntHud-environmentIcon");
    if (locationEl && !locationEl.onclick) {
      locationEl.onclick = function() {
        const locationName = user.environment_name;
        var newWindow = window.open("");
        newWindow.location = `https://mhwiki.hitgrab.com/wiki/index.php/${locationName.replace(
          " ",
          "_"
        )}`;
      };
      locationEl.style.cursor = "pointer";

      // innerHTML is more elegant but impractical since the game updates it on location changes
      // locationEl.innerHTML = `${locationEl.innerHTML}`;
    }
  }

  /**
   * Fetches & executes specified bookmarklet variant
   * @param {string} type
   */
  function loadBookmarklet(type) {
    var el = document.createElement("script");
    var cdn =
      "https://cdn.jsdelivr.net/gh/tsitu/MH-Tools@" +
      sha +
      "/src/bookmarklet/bm-" +
      type +
      ".min.js";
    el.src = cdn;
    document.body.appendChild(el);
    el.onload = function() {
      el.remove();
    };
  }

  /**
   * Element dragging functionality
   * @param {HTMLElement} el
   */
  function dragElement(el) {
    var pos1 = 0,
      pos2 = 0,
      pos3 = 0,
      pos4 = 0;
    if (document.getElementById(el.id + "header")) {
      document.getElementById(el.id + "header").onmousedown = dragMouseDown;
    } else {
      el.onmousedown = dragMouseDown;
    }

    function dragMouseDown(e) {
      e = e || window.event;
      pos3 = e.clientX;
      pos4 = e.clientY;
      document.onmouseup = closeDragElement;
      document.onmousemove = elementDrag;
    }

    function elementDrag(e) {
      e = e || window.event;
      pos1 = pos3 - e.clientX;
      pos2 = pos4 - e.clientY;
      pos3 = e.clientX;
      pos4 = e.clientY;
      el.style.top = el.offsetTop - pos2 + "px";
      el.style.left = el.offsetLeft - pos1 + "px";
    }

    function closeDragElement() {
      document.onmouseup = null;
      document.onmousemove = null;
    }
  }
})();
<|endoftext|>


<commit_before>;(function ($, window, undefined) {

  var pluginName = 'neoTabs',
      document = window.document,

      defaults = {
        wrapperClass: 'content',
        activeClass: 'active',
        tabHeadClass: 'tabhead',
        tabBodyClass: 'tabbody',
        firstTabClass: 'first',
        lastTabClass: 'last',
        clearfixClass: 'group',
        tabsListClass: 'tabs-list',
        tabHeadElement: 'h4',
        tabsPosition: 'top',
        cssClassAvailable: true,
        fx: 'show',
        fxSpeed: 0,
        autoAnchor: true,
        wrapInnerTabs: '',
        dropdownTabLabel: '▾',
        dropdownTabClass: 'dropdown',
        dropdownTabsListClass: 'tabs-list',
        dropdownTabsClearfixClass: 'group'
      },
      tabsCount = 0,
      keyCodes = {
        37: -1,
        38: -1,
        39: +1,
        40: +1
      },
      positions = {
        top: 'prepend',
        bottom: 'append'
      };

  // Helper function to check exclusively for an HTML5 data-attribute. Thanks to
  // @cburgdorf for typing down this gist for me: (https://gist.github.com/3979912)
  $.fn.hasDataAttr = function (attr) {
    var value = this.attr('data-' + attr);
    return typeof value !== 'undefined' && value !== false;
  };

  if ($('body').data('accessibleTabsCount') !== undefined) {
    tabsCount = $('body').data('accessibleTabsCount');
  }

  $('body').data('accessibleTabsCount', tabsCount);

  function NeoTabs(element, options) {
    var _this = this;

    $.extend(_this, {
      $el: element,
      opts: $.extend({}, defaults, options),
      keyCodes: keyCodes,
      hasDropdown: false,
      hasPreActiveTab: false,
      tabsList: new TabsList(),
      dropdownTabsList: null
    });

    _this.$el.wrapInner('

');

    _this.$el.find(_this.options.tabHeadElement).each(function (i) {

      var $tabHeadElement = $(this),
          tab = new Tab({
            label: $tabHeadElement.html(),
            id: _this.generateTabId('accessibletabscontent', tabsCount, i),
            tabsList: null,
            cssClass: (_this.opts.cssClassAvailable) ?
              (($tabHeadElement.attr('class') + ' ' || '') + _this.opts.tabHeadClass) :
              _this.opts.tabHeadClass
          });

      if (!_this.hasDropdown && $tabHeadElement.hasDataAttr('neotabs-dropdown')) {
        _this.dropdownTabsList = new TabsList({
          clearfixClass: _this.opts.dropdownTabsClearfixClass,
          tabsListClass: _this.opts.dropdownTabsListClass
        });
        _this.hasDropdown = true;
      }

      if (!_this.hasPreActiveTab && $tabHeadElement.hasDataAttr('neotabs-active')) {
        $tabHeadElement.addClass(_this.opts.activeClass);
        _this.hasPreActiveTab = true;
      }

      if (_this.hasDropdown) {
        _this.dropdownTabsList.addTab(tab);
      } else {
        _this.tabsList.addTab(tab);
      }

      $tabHeadElement.attr({
        'id': tab.id,
        'class': _this.opts.tabHeadClass,
        'tab-index': '-1'
      });
    });

    if (_this.hasDropdown) {
      tabsList.addTab(new Tab({
        label: _this.opts.dropdownTabLabel,
        id: '',
        tabsList: dropdownTabsList,
        cssClass: _this.opts.tabHeadClass + ' ' + _this.opts.dropdownTabClass
      }));
    }

    if (!_this.$el.fin('.' + _this.opts.tabsListClass).length) {
      _this.$el[positions[_this.opts.tabsPosition]](tabsList.toHtml());
    }
  }

  NeoTabs.prototype.generateTabId = function (name, tabsCount, tabCount) {
    return 'accessibletabscontent' + tabsCount + '-' tabCount;
  };

  function TabsList(options) {
    this.tabs = [];
    this.clearfixClass = options.clearfixClass;
    this.tabsListClass = options.tabsListClass;
  }

  TabsList.prototype.addTab = function (tab) {
    this.tabs.push(tab);
  };

  TabsList.prototype.toHtml = function () {
    var len = this.tabs.length,
        i = 0,
        html = '
';

    for (; i < len; i++) {
      html += this.tabs[i].toHtml();
    }
    return html;
  };

  function Tab(options) {
    this.label = options.label;
    this.id = options.id;
    this.tabsList = options.tabsList;
    this.cssClass = options.cssClass;
  }

  Tab.prototype.toHtml = function () {
    var html = '
' + this.label + '';

    if (this.tabList) {
      html += this.tabList.toHtml();
    }
    html += '
';
    return html;
  };

}(jQuery, window));

/**
 * NeoTabs - jQuery plugin
 *
 * source: http://github.com/PascalPrecht/jquery.neotabs.js/
 * site: http://pascalprecht.github.com/jquery.neotabs.js/
 *
 * @author: Pascal Precht <pascal.precht@gmail.com>
 * Released under the MIT and GPL Licenses.
 */
;(function ($, window, undefined) {

  var pluginName = 'neoTabs',
      document = window.document,

      defaults = {
        wrapperClass: 'content',
        activeClass: 'active',
        tabHeadClass: 'tabhead',
        tabBodyClass: 'tabbody',
        firstTabClass: 'first',
        lastTabClass: 'last',
        clearfixClass: 'group',
        tabsListClass: 'tabs-list',
        tabHeadElement: 'h4',
        tabsPosition: 'top',
        cssClassAvailable: true,
        fx: 'show',
        fxSpeed: 0,
        autoAnchor: true,
        wrapInnerTabs: '',
        dropdownTabLabel: '▾',
        dropdownTabClass: 'dropdown',
        dropdownTabsListClass: 'tabs-list',
        dropdownTabsClearfixClass: 'group'
      },

      tabCount = 0,
      keyCodes = {
        37: -1,
        38: -1,
        39: +1,
        40: +1
      },
      positions = {
        top: 'prepend',
        bottom: 'append'
      };

  if ($('body').data('accessibleTabsCount') !== undefined) {
    tabCount = $('body').data('accessibleTabsCount');
  }

  $('body').data('accessibleTabsCount', tabCount);

  function NeoTabs(element, options) {
    var _this = this,
        dropdownTabs = null,
        hasDropdown = false,
        preActive = false,
        count = 0;

    $.extend(_this, {
      $el: element,
      options: $.extend({}, defaults, options)
    });

    var tabs = new TabList({
          clearfixClass: _this.options.clearfixClass,
          tabsListClass: _this.options.tabsListClass
        });

    _this.$el.wrapInner('

');

    _this.$el.find(_this.options.tabHeadElement).each(function (i) {

      var $tabHeadElement = $(this);

      if (!hasDropdown && typeof($tabHeadElement.data('neotabs-dropdown')) !== 'undefined') {
        hasDropdown = true;
        dropdownTabs = new TabList({
          clearfixClass: _this.options.dropdownTabsClearfixClass,
          tabsListClass: _this.options.dropdownTabsListClass
        });
      }

      if (!preActive && typeof($tabHeadElement.data('neotabs-active')) !== 'undefined') {
        $tabHeadElement.addClass(_this.options.activeClass);
        preActive = true;
      }

      var tab = new Tab({
        label:  $tabHeadElement.html(),
        id: 'accessibletabscontent' + tabCount + '-' + i,
        tabList: null,
        cssClass: (_this.options.cssClassAvailable) ?
          (($tabHeadElement.attr('class') || '') + ' ' + _this.options.tabHeadClass):
          _this.options.tabHeadClass
      });

      if (hasDropdown) {
        dropdownTabs.addTab(tab);
      } else {
        tabs.addTab(tab);
      }

      $tabHeadElement.attr({
        'id': tab.id,
        'class': _this.options.tabheadClass,
        'tabindex': '-1'
      });

      count = i;
    });

    if (hasDropdown) {
      tabs.addTab(new Tab({
        label: _this.options.dropdownTabLabel,
        id: '',
        tabList: dropdownTabs,
        cssClass: _this.options.tabHeadClass + ' ' + _this.options.dropdownTabClass
      }));
    }

    if (!_this.$el.find('.' + _this.options.tabsListClass).length) {
      _this.$el[positions[_this.options.tabsPosition]](tabs.toHtml());
    }

    var $content = _this.$el.find('.' + _this.options.tabBodyClass),
        $tabsList = _this.$el.find('.' + _this.options.tabsListClass);

    if ($content.length > 0) {
      $content.hide();
      $($content[0]).show();
    }

    $tabsList.find(' > li:first')
      .addClass(_this.options.firstTabClass + ((!preActive) ? ' ' + _this.options.activeClass : ''))
      .closest('ul').find('> li:last').addClass(_this.options.lastTabClass);

    if (_this.options.wrapInnerTabs) {
      $tabsList.find('> li > a').wrapInner(_this.options.wrapInnerTabs);
    }

    $tabsList.find('> li a').each(function (i) {
      var $tab = $(this);

      $tab.on('click', function (e) {
        e.preventDefault();

        $tabsList
          .find('>li.' + _this.options.activeClass)
          .removeClass(_this.options.activeClass);

        $(this).parent().addClass(_this.options.activeClass);
        if (!$(this).parent().hasClass(_this.options.dropdownTabClass)) {
          _this.$el.find('.' + _this.options.tabBodyClass + ':visible').hide();

          var j = i;

          if ($(this).closest('.' + _this.options.dropdownTabClass).length) {
            j = i-1;
          }

          _this.$el.find('.' + _this.options.tabBodyClass).eq(j)[_this.options.fx](_this.options.fxSpeed);
        }
      });

      $tab.focus(function (e) {
        $(document).keyup(function (e) {
          if (keyCodes[e.keyCode]) {
            console.log(e.keyCode);
          }
        });
      });
    });

    if (_this.options.autoAnchor && window.location.hash) {
      var $anchorTab = $('.' + _this.options.tabsListClass).find(window.location.hash);
      if ($anchorTab.size()) {
        $anchorTab.click();
      }
    }

    if (preActive) {
      $('.' + _this.options.tabsListClass + ' .' + _this.options.activeClass + ' a').click();
    }

    tabCount++;
  }

  function Tab(options) {
    this.label = options.label;
    this.id = options.id;
    this.tabList = options.tabList;
    this.cssClass = options.cssClass;
  }

  Tab.prototype.toHtml = function () {
    var html = '
' + this.label + '';

    if (this.tabList) {
      html += this.tabList.toHtml();
    }

    html += '
';
    return html;
  };

  function TabList(options) {
    this.clearfixClass = options.clearfixClass;
    this.tabsListClass = options.tabsListClass;
    this.tabs = [];
  }

  TabList.prototype.addTab = function (tab) {
    this.tabs.push(tab);
  };

  TabList.prototype.toHtml = function () {
    var len = this.tabs.length,
        i = 0,
        html = '
';

    for (; i < len; i++) {
      html += this.tabs[i].toHtml();
    }

    return html;
  };

  $.fn[pluginName] = function (options) {
    return this.each(function () {
      if (!$.data(this, 'plugin_' + pluginName)) {
        $.data(this, 'plugin_' + pluginName, new NeoTabs($(this), options ));
      }
    });
  };
}(jQuery, window));
<commit_msg>Code cleanup<commit_after>/**
 * NeoTabs - jQuery plugin
 *
 * source: http://github.com/PascalPrecht/jquery.neotabs.js/
 * site: http://pascalprecht.github.com/jquery.neotabs.js/
 *
 * @author: Pascal Precht <pascal.precht@gmail.com>
 * Released under the MIT and GPL Licenses.
 */
;(function ($, window, undefined) {

  var pluginName = 'neoTabs',
      document = window.document,

      defaults = {
        wrapperClass: 'content',
        activeClass: 'active',
        tabHeadClass: 'tabhead',
        tabBodyClass: 'tabbody',
        firstTabClass: 'first',
        lastTabClass: 'last',
        clearfixClass: 'group',
        tabsListClass: 'tabs-list',
        tabHeadElement: 'h4',
        tabsPosition: 'top',
        cssClassAvailable: true,
        fx: 'show',
        fxSpeed: 0,
        autoAnchor: true,
        wrapInnerTabs: '',
        dropdownTabLabel: '▾',
        dropdownTabClass: 'dropdown',
        dropdownTabsListClass: 'tabs-list',
        dropdownTabsClearfixClass: 'group'
      },
      tabsCount = 0,
      keyCodes = {
        37: -1,
        38: -1,
        39: +1,
        40: +1
      },
      positions = {
        top: 'prepend',
        bottom: 'append'
      };

  // Helper function to check exclusively for an HTML5 data-attribute. Thanks to
  // @cburgdorf for typing down this gist for me: (https://gist.github.com/3979912)
  $.fn.hasDataAttr = function (attr) {
    var value = this.attr('data-' + attr);
    return typeof value !== 'undefined' && value !== false;
  };

  if ($('body').data('accessibleTabsCount') !== undefined) {
    tabsCount = $('body').data('accessibleTabsCount');
  }

  $('body').data('accessibleTabsCount', tabsCount);

  function NeoTabs(element, options) {
    var _this = this;

    $.extend(_this, {
      $el: element,
      opts: $.extend({}, defaults, options),
      keyCodes: keyCodes,
      hasDropdown: false,
      hasPreActiveTab: false,
      tabsList: null,
      dropdownTabsList: null
    });

    _this.tabsList = new TabsList({
      clearfixClass: _this.opts.clearfixClass,
      tabsListClass: _this.opts.tabsListClass
    });

    _this.$el.wrapInner('

');

    _this.$el.find(_this.opts.tabHeadElement).each(function (i) {

      var $tabHeadElement = $(this);

      if (!_this.hasDropdown && $tabHeadElement.hasDataAttr('neotabs-dropdown')) {
        _this.dropdownTabsList = new TabsList({
          clearfixClass: _this.opts.dropdownTabsClearfixClass,
          tabsListClass: _this.opts.dropdownTabsListClass
        });
        _this.hasDropdown = true;
      }

      if (!_this.hasPreActiveTab && $tabHeadElement.hasDataAttr('neotabs-active')) {
        $tabHeadElement.addClass(_this.opts.activeClass);
        _this.hasPreActiveTab = true;
      }

      var tab = new Tab({
        label: $tabHeadElement.html(),
        id: _this.generateTabId('accessibletabscontent', tabsCount, i),
        tabsList: null,
        cssClass: (_this.opts.cssClassAvailable) ?
          (($tabHeadElement.attr('class') || '') + ' ' + _this.opts.tabHeadClass) :
          _this.opts.tabHeadClass
      });

      if (_this.hasDropdown) {
        _this.dropdownTabsList.addTab(tab);
      } else {
        _this.tabsList.addTab(tab);
      }

      $tabHeadElement.attr({
        'id': tab.id,
        'class': _this.opts.tabHeadClass,
        'tab-index': '-1'
      });
    });

    // Generate dropdown tab if hasDropdown flag is true
    if (_this.hasDropdown) {
      _this.tabsList.addTab(new Tab({
        label: _this.opts.dropdownTabLabel,
        id: '',
        tabsList: _this.dropdownTabsList,
        cssClass: _this.opts.tabHeadClass + ' ' + _this.opts.dropdownTabClass
      }));
    }

    if (!_this.$el.find('.' + _this.opts.tabsListClass).length) {
      _this.$el[positions[_this.opts.tabsPosition]](_this.tabsList.toHtml());
    }

    var $content = _this.$el.find('.' + _this.opts.tabBodyClass),
        $tabsList = _this.$el.find('.' + _this.opts.tabsListClass);

    // Show the first tab content by default
    if ($content.length > 0) {
      $content.hide();
      $($content[0]).show();
    }

    // Which tab should be active?
    $tabsList.find(' > li:first')
      .addClass(_this.opts.firstTabClass + ((!_this.hasPreActiveTab) ? ' ' + _this.opts.activeClass : ''))
      .closest('ul').find('> li:last')
      .addClass(_this.opts.lastTabClass);

    if (_this.opts.wrapInnerTabs) {
      $tabsList.find('> li > a').wrapInner(_this.opts.wrapInnerTabs);
    }

    $tabsList.find('> li a').each(function (i) {
      var $tab = $(this);

      $tab.on('click', function (e) {
        e.preventDefault();

        $tabsList
          .find('> li.' + _this.opts.activeClass)
          .removeClass(_this.opts.activeClass);

        $(this).parent().addClass(_this.opts.activeClass);

        if (!$(this).parent().hasClass(_this.opts.dropdownTabClass)) {
          _this.$el.find('.' + _this.opts.tabBodyClass + ':visible').hide();

          var j = i;

          // little hack to because we have more tabHead then tabBodys
          if ($(this).closest('.' + _this.opts.dropdownTabClass).length) {
            j = i-1;
          }

          _this.$el.find('.' + _this.opts.tabBodyClass).eq(j)[_this.opts.fx](_this.opts.fxSpeed);
        }
      });
    });

    // If we have an anchor in our url, trigger click event on the right tab
    if (_this.opts.autoAnchor && window.location.hash) {
      var $anchorTab = $('.' + _this.opts.tabsListClass).find(window.location.hash);
      if ($anchorTab.size()) {
        $anchorTab.click();
      }
    }

    if (_this.hasPreActiveTab) {
      $('.' + _this.opts.tabsListClass + ' .' + _this.opts.activeClass + ' a').click();
    }
    tabsCount++;
  }

  NeoTabs.prototype.generateTabId = function (name, tabsCount, tabCount) {
    return 'accessibletabscontent' + tabsCount + '-' + tabCount;
  };

  function TabsList(options) {
    this.tabs = [];
    this.clearfixClass = options.clearfixClass;
    this.tabsListClass = options.tabsListClass;
  }

  TabsList.prototype.addTab = function (tab) {
    this.tabs.push(tab);
  };

  TabsList.prototype.toHtml = function () {
    var len = this.tabs.length,
        i = 0,
        html = '
';

    for (; i < len; i++) {
      html += this.tabs[i].toHtml();
    }
    return html;
  };

  function Tab(options) {
    this.label = options.label;
    this.id = options.id;
    this.tabsList = options.tabsList;
    this.cssClass = options.cssClass;
  }

  Tab.prototype.toHtml = function () {
    var html = '
' + this.label + '';

    if (this.tabsList) {
      html += this.tabsList.toHtml();
    }
    html += '
';
    return html;
  };

  $.fn[pluginName] = function (options) {
    return this.each(function () {
      if (!$.data(this, 'plugin_' + pluginName)) {
        $.data(this, 'plugin_' + pluginName, new NeoTabs($(this), options ));
      }
    });
  };
}(jQuery, window));
<|endoftext|>


<commit_before>define(["Construct/DublinCore", "PubData"], function( DublinCore, PubData ) {

    //压缩成zip的工具:   JSZIP;
    var EpubBuilder = function () {
        this.init();
        this.dublinCore = new DublinCore;
    };

    $.extend(EpubBuilder.prototype, {
        "init" : function () {
            var _this = this;
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/mimetype", "success":function(data) {
                _this.mimetype = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/META-INF/container.xml", "success":function(data) {
                _this.container = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/toc.ncx", "success":function(data) {
                _this.toc = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/content.opf", "success":function(data) {
                _this.contentOpt = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/coverpage.html", "success":function(data) {
                _this.coverpage = data;
            }});
        },

        /**
         * @desc 转化为绝对的地址;
         * @param {String}
         * @return {String}
         * */
        toRelativeUrl: function( url ) {
            if( url ) {
                /*
                * 处理 /text/../这种情况
                * */
                url = url.replace(/\/[^\/]+\/\.\./g,"");
                /*
                * 处理 //// , //这种情况
                * */
                url = url.replace(/\/+/g,"/");
            };
            return url || "";
        },
        /**
         * @desc 获取image图片的类型;
         * @param {String} fileurl
         * */
        "getImageType" : function ( fileurl ) {
            if(!fileurl)return ;
            return fileurl.split(".").pop() || "";
        },
        /**
         * @desc 导入epub文件
         * @param {Object} pubData把数据导入到pubdata中;
         * */
        "importEpub" : function( pubData ) {

            var _this = this;
            var $input = $("<input type='file' >");
            $input.bind("change", function( ev ) {
                util.readArrayBuffer( ev.target.files[0]).done(function( arrayBuffer ) {
                    var unzip = JSZip(arrayBuffer);
                    _this.readEpub( unzip, pubData );
                });
            });
            $input.trigger("click");

        },

        /**
         * @desc 读取epub文件并展示到界面中;
         * */
        "readEpub" : function ( unzip, pubData ) {

            var _this = this;
            var containerXml = unzip.file("META-INF/container.xml").asText();
            var domParser = new DOMParser;
            var xmlDoc = domParser.parseFromString(containerXml, 'text/xml');
            var fullPath  = xmlDoc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
            var OEBPSFolderName = fullPath.split("/")[0];
            //读取contentOpt 文件;
            var contentOpt = unzip.file(fullPath).asText();
            //读取tocNcx 文件;
            var tocNcx =  unzip.file(OEBPSFolderName +"/toc.ncx").asText();
            var $tocNcx = $(tocNcx);

            var contentOptXmlDoc = domParser.parseFromString(contentOpt, 'text/xml');
            var elSpine = contentOptXmlDoc.getElementsByTagName("spine")[0];
            var getTocEl = function( href ) {
                var els = $tocNcx.find("content");
                for(var i=0; i<els.length ; i++ ) {
                    if(els[i].getAttribute("src") === href) {
                        return els[i];
                    };
                };
            };

            //设置dublinCore
            this.dublinCore.setDublinCore(contentOptXmlDoc);
            var navArray = [];
            var contentArray = [];
            var def = $.Deferred();
            var orginDef = def;
            $.each(elSpine.children, function( contentOpfSpinIndex , itemref ) {
                var idref = itemref.getAttribute("idref");
                var href = contentOptXmlDoc.getElementById( idref ).getAttribute("href");
                //获取文件的目录;
                var dir = href.split("/");
                dir.pop();
                var nav = getTocEl(href);
                var navText = "";
                var content = "";

                if(nav) {

                    navText = $(nav).prev("navlabel").text();
                    //console.log(navText);

                    content = unzip.file(OEBPSFolderName+"/"+href).asText();

                    var $content = $(content);
                    //获取content的image, 并转化为base64的格式;
                    var imgs = $content.find("image").add( $content.find("img") );
                    $.each(imgs, function (i,  img ) {
                        //把处理图片的罗家添加的延迟对象中;
                        def = def.then(function() {
                            var _def = $.Deferred();
                            var href = $(img).attr("xlink:href");
                            var src =  $(img).attr("src");
                            var url = OEBPSFolderName+"/"+dir.join("/")+"/"+ href || src;
                            var jpg = unzip.file( _this.toRelativeUrl(url) );
                            var imageType = _this.getImageType( url );
                            var oFReader = new FileReader();
                            oFReader.onload = function (oFREvent) {
                                //设置属性;
                                $(img).attr(href ? "xlink:href" : "src", oFREvent.target.result );
                                _def.resolve();
                            };
                            oFReader.readAsDataURL(new Blob([jpg.asArrayBuffer()], {type : 'image/'+imageType}));
                            return _def;
                        });
                    });

                    def.then(function() {
                        var $divContent =  $("<div>").append($content);
                        navArray[contentOpfSpinIndex] = navText
                        contentArray[contentOpfSpinIndex] =  $divContent.html();
                    });
                };
            });
            //最后， 当所有的数据处理成字符串以后， 把数据灌入view;
            def.then(function() {
                pubData.setData(navArray, contentArray);
            });
            //延迟对象马上开始迭代;
            orginDef.resolve();
        },

        /**
         * @desc 把base64的图片转化为文件流;
         * @return html;
         * */
        "base64toImage" : function ( content, zipImageFolder ) {
            var $html = $(content);
            $html.find("image").add( $html.find("img")).each(function(i, e) {
                var href = $(e).attr("xlink:href") || $(e).attr("src");
                var dataUrl = href.split(",").pop();
                var imageType = href.match(/data:image\/([\w\W]+);/i).pop();
                var uuid = util.uuid()+"."+imageType;
                zipImageFolder.file(  uuid , dataUrl  , {base64: true});
                $(e).attr("xlink:href",  "images/" + uuid );
                $(e).attr("src", "images/" + uuid )
            });
            return $("<div>").html( $html).html();
        },

        /**
         * @param {OBJECT} OPTIONS
         *       @param {String} coverpage
         *       @param {Blob} coverImage
         *       @param {String} publisher
         *       @param {String} description
         *       @param {String} language
         *       @param {String} creator
         *       @param {String} author
         *       @param {String} title
         *       @param {String} date
         *       @param {String} contributor
         *       @param {String} ISBN
         *       @param {Array} tocArray
         *       @param {Array} contentArray
         *       @param {String} name
         * */
        "exportToEpub" : function ( options ) {
            options = $.extend({
                tocArray : [
                    "章节0",
                    "章节1"
                ],
                contentArray : [
                    "测试数组1",
                    "测试数据2"
                ],
                fileName : "default",
                coverpage : "coverpage",
                publisher : "publisher",
                description : "description",
                language : "language",
                coverImage : "data:image/png;base64,R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=",
                creator : "creator",
                author : "author",
                title : "title",
                contributor : "contributor",
                ISBN : "xxxx-xxxx"
            }, options);

            var zip = new JSZip();
            var OPSFolder = zip.folder("OPS");
            var imagesFolder = OPSFolder.folder("images");

            //循环contentArray和tocArray， 生成html字符串
            var chapterLength = options.contentArray.length;
            var tocItem = [];
            var contentItem = [];

            for(var i=0; i< chapterLength; i++) {
                //生成章节数据
                tocItem.push({
                    name : options.tocArray[i],
                    href : "chapter" + i + ".html"
                });
                options.contentArray[i] = this.base64toImage(options.contentArray[i], imagesFolder);
                //生成html数据;
                OPSFolder.file("chapter" + i + ".html",options.contentArray[i] );
            };

            var MeTaFolder = zip.folder("META-INF");
            MeTaFolder.file("container.xml", this.container);
            zip.file("mimetype", this.mimetype);

            //生成toc和opt文件
            OPSFolder.file("content.opf", Handlebars.compile(this.contentOpt)({ tocItem : tocItem, options : options}) );
            OPSFolder.file("toc.ncx", Handlebars.compile(this.toc)(tocItem));
            OPSFolder.file("coverpage.html", Handlebars.compile(this.coverpage)( {options : options}  ));

            var content = zip.generate({type:"blob"});
            // see FileSaver.js
            saveAs(content, options.fileName + '.epub');

            return ;
        }
    });

    return EpubBuilder;
})<commit_msg>Update EpubBuilder.js<commit_after>define(["Construct/DublinCore", "PubData"], function( DublinCore, PubData ) {

    //压缩成zip的工具:   JSZIP;
    var EpubBuilder = function () {
        this.init();
        this.dublinCore = new DublinCore;
    };

    $.extend(EpubBuilder.prototype, {
        "init" : function () {
            var _this = this;
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/mimetype", "success":function(data) {
                _this.mimetype = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/META-INF/container.xml", "success":function(data) {
                _this.container = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/toc.ncx", "success":function(data) {
                _this.toc = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/content.opf", "success":function(data) {
                _this.contentOpt = data;
            }});
            $.ajax({async:false, type:"get", dataType : "text" ,url:"./epub/OPS/coverpage.html", "success":function(data) {
                _this.coverpage = data;
            }});
        },

        /**
         * @desc 转化为绝对的地址;
         * @param {String}
         * @return {String}
         * */
        toRelativeUrl: function( url ) {
            if( url ) {
                /*
                * 处理 /text/../这种情况
                * */
                url = url.replace(/\/[^\/]+\/\.\./g,"");
                /*
                * 处理 //// , //这种情况
                * */
                url = url.replace(/\/+/g,"/");
            };
            return url || "";
        },
        /**
         * @desc 获取image图片的类型;
         * @param {String} fileurl
         * */
        "getImageType" : function ( fileurl ) {
            if(!fileurl)return ;
            return fileurl.split(".").pop() || "";
        },
        /**
         * @desc 导入epub文件
         * @param {Object} pubData把数据导入到pubdata中;
         * */
        "importEpub" : function( pubData ) {

            var _this = this;
            var $input = $("<input type='file' >");
            $input.bind("change", function( ev ) {
                util.readArrayBuffer( ev.target.files[0]).done(function( arrayBuffer ) {
                    var unzip = JSZip(arrayBuffer);
                    _this.readEpub( unzip, pubData );
                });
            });
            $input.trigger("click");

        },

        /**
         * @desc 读取epub文件并展示到界面中;
         * */
        "readEpub" : function ( unzip, pubData ) {

            var _this = this;
            var containerXml = unzip.file("META-INF/container.xml").asText();
            var domParser = new DOMParser;
            var xmlDoc = domParser.parseFromString(containerXml, 'text/xml');
            var fullPath  = xmlDoc.getElementsByTagName("rootfile")[0].getAttribute("full-path");
            var OEBPSFolderName = fullPath.split("/")[0];
            //读取contentOpt 文件;
            var contentOpt = unzip.file(fullPath).asText();
            //读取tocNcx 文件;
            var tocNcx =  unzip.file(OEBPSFolderName +"/toc.ncx").asText();
            var $tocNcx = $(tocNcx);

            var contentOptXmlDoc = domParser.parseFromString(contentOpt, 'text/xml');
            var elSpine = contentOptXmlDoc.getElementsByTagName("spine")[0];
            var getTocEl = function( href ) {
                var els = $tocNcx.find("content");
                for(var i=0; i<els.length ; i++ ) {
                    if(els[i].getAttribute("src") === href) {
                        return els[i];
                    };
                };
            };

            //设置dublinCore
            this.dublinCore.setDublinCore(contentOptXmlDoc);
            var navArray = [];
            var contentArray = [];
            var def = $.Deferred();
            var orginDef = def;
            $.each(elSpine.children, function( contentOpfSpinIndex , itemref ) {
                var idref = itemref.getAttribute("idref");
                var href = contentOptXmlDoc.getElementById( idref ).getAttribute("href");
                //获取文件的目录;
                var dir = href.split("/");
                dir.pop();
                var nav = getTocEl(href);
                var navText = "";
                var content = "";

                if(nav) {

                    navText = $(nav).prev("navlabel").text();
                    //console.log(navText);

                    content = unzip.file(OEBPSFolderName+"/"+href).asText();

                    var $content = $(content);
                    //获取content的image, 并转化为base64的格式;
                    var imgs = $content.find("image").add( $content.find("img") );
                    $.each(imgs, function (i,  img ) {
                        //把处理图片的罗家添加的延迟对象中;
                        def = def.then(function() {
                            var _def = $.Deferred();
                            var href = $(img).attr("xlink:href");
                            var src =  $(img).attr("src");
                            var url = OEBPSFolderName+"/"+dir.join("/")+"/"+ href || src;
                            var jpg = unzip.file( _this.toRelativeUrl(url) );
                            var imageType = _this.getImageType( url );
                            var oFReader = new FileReader();
                            oFReader.onload = function (oFREvent) {
                                //设置属性;
                                $(img).attr(href ? "xlink:href" : "src", oFREvent.target.result );
                                _def.resolve();
                            };
                            oFReader.readAsDataURL(new Blob([jpg.asArrayBuffer()], {type : 'image/'+imageType}));
                            return _def;
                        });
                    });

                    def.then(function() {
                        var $divContent =  $("<div>").append($content);
                        navArray[contentOpfSpinIndex] = navText
                        contentArray[contentOpfSpinIndex] =  $divContent.html();
                    });
                };
            });
            //最后， 当所有的数据处理成字符串以后， 把数据灌入view;
            def.then(function() {
                pubData.setData(navArray, contentArray);
            });
            //延迟对象马上开始迭代;
            orginDef.resolve();
        },

        /**
         * @desc 把base64的图片转化为文件流;
         * @return html;
         * */
        "base64toImage" : function ( content, zipImageFolder ) {
            var $html = $(content);
            $html.find("image").add( $html.find("img")).each(function(i, e) {
                var href = $(e).attr("xlink:href") || $(e).attr("src");
                var dataUrl = href.split(",").pop();
                var imageType = href.match(/data:image\/([\w\W]+);/i).pop();
                var uuid = util.uuid()+"."+imageType;
                zipImageFolder.file(  uuid , dataUrl  , {base64: true});
                $(e).attr("xlink:href",  "images/" + uuid );
                $(e).attr("src", "images/" + uuid );
                //百度编辑器会设置一个_src属性和src一样； src如果为base64的话， 文件会很大;
                $(e).attr("_src","");
            });
            return $("<div>").html( $html).html();
        },

        /**
         * @param {OBJECT} OPTIONS
         *       @param {String} coverpage
         *       @param {Blob} coverImage
         *       @param {String} publisher
         *       @param {String} description
         *       @param {String} language
         *       @param {String} creator
         *       @param {String} author
         *       @param {String} title
         *       @param {String} date
         *       @param {String} contributor
         *       @param {String} ISBN
         *       @param {Array} tocArray
         *       @param {Array} contentArray
         *       @param {String} name
         * */
        "exportToEpub" : function ( options ) {
            options = $.extend({
                tocArray : [
                    "章节0",
                    "章节1"
                ],
                contentArray : [
                    "测试数组1",
                    "测试数据2"
                ],
                fileName : "default",
                coverpage : "coverpage",
                publisher : "publisher",
                description : "description",
                language : "language",
                coverImage : "data:image/png;base64,R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=",
                creator : "creator",
                author : "author",
                title : "title",
                contributor : "contributor",
                ISBN : "xxxx-xxxx"
            }, options);

            var zip = new JSZip();
            var OPSFolder = zip.folder("OPS");
            var imagesFolder = OPSFolder.folder("images");

            //循环contentArray和tocArray， 生成html字符串
            var chapterLength = options.contentArray.length;
            var tocItem = [];
            var contentItem = [];

            for(var i=0; i< chapterLength; i++) {
                //生成章节数据
                tocItem.push({
                    name : options.tocArray[i],
                    href : "chapter" + i + ".html"
                });
                options.contentArray[i] = this.base64toImage(options.contentArray[i], imagesFolder);
                //生成html数据;
                OPSFolder.file("chapter" + i + ".html",options.contentArray[i] );
            };

            var MeTaFolder = zip.folder("META-INF");
            MeTaFolder.file("container.xml", this.container);
            zip.file("mimetype", this.mimetype);

            //生成toc和opt文件
            OPSFolder.file("content.opf", Handlebars.compile(this.contentOpt)({ tocItem : tocItem, options : options}) );
            OPSFolder.file("toc.ncx", Handlebars.compile(this.toc)(tocItem));
            OPSFolder.file("coverpage.html", Handlebars.compile(this.coverpage)( {options : options}  ));

            var content = zip.generate({type:"blob"});
            // see FileSaver.js
            saveAs(content, options.fileName + '.epub');

            return ;
        }
    });

    return EpubBuilder;
})
<|endoftext|>