closedquestion-8.x-3.x-dev/assets/js/libraries/jquery/jquery.soEditor.js

assets/js/libraries/jquery/jquery.soEditor.js
/**
 * soEditor
 * based on boilerplate version 1.3
 * @param {object} $ A jQuery object
 **/
(function ($) {
  "use strict"; //ECMA5 strict modus

  $.soEditor = function (element, settings) {
    /* define vars
     */

    /* this object will be exposed to other objects */
    var publicObj = this;

    //the version number of the plugin
    publicObj.version = "1.0";

    /* this object holds functions used by the plugin boilerplate */
    var _helper = {
      /**
       * Call hooks, additinal parameters will be passed on to registered plugins
       * @param {string} name
       */
      doHook: function (name) {
        var i;
        var pluginFunctionArgs = [];

        /* remove first two arguments */
        for (i = 1; i < arguments.length; i++) {
          pluginFunctionArgs.push(arguments[i]);
        }

        /* call plugin functions */
        if (_globals.plugins !== undefined) {
          /* call plugins */
          $.each(_globals.plugins, function (soEditor, extPlugin) {
            if (
              extPlugin.__hooks !== undefined &&
              extPlugin.__hooks[name] !== undefined
              ) {
              extPlugin.__hooks[name].apply(publicObj, pluginFunctionArgs);
            }
          });
        }

        /* trigger event on main element */
        _globals.$element.trigger(name, pluginFunctionArgs);
      },
      /**
       * Initializes the plugin
       */
      doInit: function () {
        _helper.doHook(
          "soEditor.beforeInit",
          publicObj,
          element,
          settings
          );
        publicObj.init();
        _helper.doHook("soEditor.init", publicObj);
      },
      /**
       * Loads an external script
       * @param {string} libName
       * @param {string} errorMessage
       */
      loadScript: function (libName, errorMessage) {
        /* remember libname */
        _cdnFilesToBeLoaded.push(libName);

        /* load script */
        $.ajax({
          type: "GET",
          url: _globals.dependencies[libName].cdnUrl,
          success: function () {
            /* forget libname */
            _cdnFilesToBeLoaded.splice(_cdnFilesToBeLoaded.indexOf(libName), 1); //remove element from _cdnFilesToBeLoaded array

            /* call init function when all scripts are loaded */
            if (_cdnFilesToBeLoaded.length === 0) {
              _helper.doInit();
            }
          },
          fail: function () {
            console.error(errorMessage);
          },
          dataType: "script",
          cache: "cache"
        });
      },
      /**
       * Checks dependencies based on the _globals.dependencies object
       * @returns {boolean}
       */
      checkDependencies: function () {
        var dependenciesPresent = true;
        for (var libName in _globals.dependencies) {
          var errorMessage =
            "jquery.soEditor: Library " +
            libName +
            " not found! This may give unexpected results or errors.";
          var doesExist = $.isFunction(_globals.dependencies[libName])
            ? _globals.dependencies[libName]
            : _globals.dependencies[libName].doesExist;
          if (doesExist.call() === false) {
            if (
              $.isFunction(_globals.dependencies[libName]) === false &&
              _globals.dependencies[libName].cdnUrl !== undefined
              ) {
              /* cdn url provided: Load script from external source */
              _helper.loadScript(libName, errorMessage);
            }
            else {
              console.error(errorMessage);
              dependenciesPresent = false;
            }
          }
        }
        return dependenciesPresent;
      }
    };
    /* keeps track of external libs loaded via their CDN */
    var _cdnFilesToBeLoaded = [];

    /* this object holds all global variables */
    var _globals = {};

    /* handle settings */
    var defaultSettings = {
      "input": $(''),
      "question_options": [],
      "allowDuplicates": false,
      "horizontalAlignment": false,
      "onlyOrder": -1
    };

    _globals.settings = {};

    if ($.isPlainObject(settings) === true) {
      _globals.settings = $.extend(true, {}, defaultSettings, settings);
    }
    else {
      _globals.settings = defaultSettings;
    }

    /* this object contains a number of functions to test for dependencies,
     * doesExist function should return TRUE if the library/browser/etc is present
     */
    _globals.dependencies = {
      /* check for jQuery 1.6+ to be present */
      "jquery1.6+": {
        doesExist: function () {
          var jqv, jqv_main, jqv_sub;
          if (window.jQuery) {
            jqv = jQuery().jquery.split(".");
            jqv_main = parseInt(jqv[0], 10);
            jqv_sub = parseInt(jqv[1], 10);
            if (jqv_main > 1 || (jqv_main === 1 && jqv_sub >= 6)) {
              return true;
            }
          }
          return false;
        },
        cdnUrl: "http://code.jquery.com/jquery-git1.js"
      },
      "jquery.ui.sortable": {
        doesExist: function () {
          return (jQuery.ui && jQuery.ui.sortable);
        }
      }
    };
    _helper.checkDependencies();

    //this object holds all plugins
    _globals.plugins = {};

    /* register DOM elements
     * jQuerified elements start with $
     */
    _globals.$element = $(element);
    _globals.$attributeWrapper = _globals.$element.closest('.xmlJsonEditor_attribute_container');

    _globals.template = '<div class="soEditor_source soEditor_box">Available items:<ul class="soEditor_sortable" id="soEditor_sortable_source"></ul></div><div class="soEditor_target soEditor_box"></div>';

    /**
     * Init function
     **/
    publicObj.init = function () {
      _globals.$element.append(_globals.template);
      _globals.$element.after('<p class="xmlEditorAttributeFeedback" style="margin-bottom: 0;">The condition as it is stored in the database:</p>');
      _globals.$sourceSortable = _globals.$element.find('#soEditor_sortable_source');
      _globals.$targetSortables = [];

      _globals.$sourceBox = _globals.$element.find('.soEditor_source.soEditor_box');
      _globals.$targetBox = _globals.$element.find('.soEditor_target.soEditor_box');
      _globals.$input = $(cfg('input'));

      /* Add options and target lists */
      $.each(cfg('question_options'), function () {
        if (!this.id) {
          disableEditor();
        }
        if (this.id.match('[a-zA-Z]')) {
          _globals.$sourceSortable.append(getOptionElement(this.id, this.label, ''));
        }
        else if (this.id.match('[0-9]')) {
          var $ul = $('<ul class="soEditor_sortable soEditor_target_sortable" data-id="soEditor_' + this.id + '"></ul>');
          _globals.$targetBox.append('<span>' + formatHTML(this.label) + '</span>');
          _globals.$targetBox.append($ul);
        }
      });

      // Make sure there is always one target box.
      if (_globals.$element.find('.soEditor_target_sortable').length === 0) {
        var $ul = $('<ul class="soEditor_sortable soEditor_target_sortable" data-id="soEditor"></ul>');
        _globals.$targetBox.append('<span>Selected items:</span>');
        _globals.$targetBox.append($ul);
      }

      // Define regular expression items.
      _globals.specialItems = {
        "__dot__": {"re": '.', "label": "One arbitrary item"},
        "__dot____star__": {"re": '.*', "label": "Zero or more arbitrary items"},
        "__star__": {"re": '*', "label": "Repeat previous item zero or more times", "hiddenByDefault": true}
      };

      _globals.idToRe = $.extend({}, _globals.specialItems);

      _globals.reToId = {};
      $.each(_globals.idToRe, function (key, config) {
        config = $.extend({}, config);
        var re = config.re;
        delete config.re;
        config.id = key;
        _globals.reToId[re] = config;
      });

      // Add regular expression items to list.
      if (cfg('onlyOrder') !== 1) {
        $.each(_globals.idToRe, function (key, config) {
          var hiddenClass = (config.hiddenByDefault === true || cfg('allowSpecialElements') === false) ? ' soEditor_hiddenByDefault' : '';
          _globals.$sourceSortable.append(getOptionElement(key, config.label, 'soEditor_special' + hiddenClass));
        });
      }

      // Provide for only order questions
      if (cfg('onlyOrder') === 1) {
        if (_globals.$input.val() === '') {
          $(".soEditor_target_sortable").append(_globals.$sourceSortable.children());
          updateAnswerInput();
        }
        _globals.$sourceBox.hide();
      }

      // Provide for horizontal questions
      if (cfg('horizontalAlignment') === true) {
        _globals.$element.addClass('soEditor_horizontal');
      }

      // Init the drag/drop functionality.
      var copyHelper;
      _globals.$sourceSortable.sortable({
        "connectWith": ".soEditor_sortable",
        "forcePlaceholderSize": false,
        "helper": function (e, li) {
          if (li.hasClass('soEditor_special') || cfg('allowDuplicates')) {
            copyHelper = li.clone().insertAfter(li);
            return li.clone();
          }
          return li;
        },
        "stop": function () {
          if (copyHelper) {
            copyHelper.remove();
          }
          formatAvailableItemsSortable();
          formatTargetItemsSortable();
          updateAnswerInput();
        }
      });

      $(".soEditor_target_sortable").sortable({
        "connectWith": ".soEditor_sortable",
        "receive": function () {
          copyHelper = null;
        },
        "stop": function () {
          formatAvailableItemsSortable();
          formatTargetItemsSortable();
          updateAnswerInput();
        }
      });

      // Keep input and editor in sync
      _globals.$input.on('keyup paste', function () {
        window.clearTimeout(_globals.$input.data('soEditor_timeout'));
        var to = window.setTimeout(function () {
          updateQuestionGUI();
        }, 1000);
        _globals.$input.data('soEditor_timeout', to);
      });

      // Hide preset select.
      $('#xmlEditor_presets_select', _globals.$element.parent()).hide();

      updateQuestionGUI();
    };

    /**
     * Disables the editor.     *
     */
    function disableEditor() {
      $('#soEditor_feedback').remove();

      _globals.$element.addClass('xmlEditorAttributeFeedback_error');
      _globals.$element.after('<p id="soEditor_feedback" class="xmlJsonEditor_feedback_description" style="color:red;">This advanced condition is not (yet) supported by the editor. The editor might not display the condition correctly. Please use below input.</p>');

      $('.xmlEditorAttributeFeedback', _globals.$attributeWrapper).addClass('soEditor_visible');
      $('#xmlEditor_presets_select', _globals.$attributeWrapper).addClass('soEditor_visible');
      _globals.$input.addClass('soEditor_visible');
    }

    /**
     * Enables the editor.
     */
    function enableEditor() {
      $('#soEditor_feedback').remove();
      _globals.$element.removeClass('xmlEditorAttributeFeedback_error');
      $('.xmlEditorAttributeFeedback', _globals.$attributeWrapper).removeClass('soEditor_visible');
      $('#xmlEditor_presets_select', _globals.$attributeWrapper).removeClass('soEditor_visible');
      _globals.$input.removeClass('soEditor_visible');
    }

    /**
     * Creates an option jQuery DOM element.
     *
     * @param {string} dataId
     * @param {string} label
     * @param {string} classAttr
     * @returns {object}
     */
    function getOptionElement(dataId, label, classAttr) {
      classAttr = classAttr ? ' ' + classAttr : '';
      return $('<li data-id="soEditor_' + dataId + '" class="soEditor_option' + classAttr + '">' + formatHTML(label) + '</li>');
    }



    /**
     * Return all anagrams of a number of characters.
     *
     * @param {array} chars
     *   E.g. ['a', 'b', 'c']
     * @returns {array}
     *   E.g. ['abc', 'acb', 'bca', 'bac', 'cab', 'cba']
     */
    function getAnagrams(chars) {
      function swap(chars, i, j) {
        var tmp = chars[i];
        chars[i] = chars[j];
        chars[j] = tmp;
      }

      var counter = [],
        anagrams = [],
        length = chars.length,
        i;

      for (i = 0; i < length; i++) {
        counter[i] = 0;
      }

      anagrams.push(chars.join(""));
      i = 0;
      while (i < length) {
        if (counter[i] < i) {
          swap(chars, i % 2 === 1 ? counter[i] : 0, i);
          counter[i]++;
          i = 0;
          anagrams.push(chars.join(''));
        }
        else {
          counter[i] = 0;
          i++;
        }
      }
      return anagrams;
    }

    /**
     * Updates the answer input based on the question GUI.
     */
    function updateAnswerInput() {
      var re_items_raw = [];
      var re_items = [];

      //Collect regular expression items.
      _globals.$element.find('.soEditor_target_sortable').each(function () {
        var $target_sortable = $(this);
        if ($target_sortable.attr('data-id') !== undefined) {
          re_items_raw.push($target_sortable.attr('data-id'));
        }
        $target_sortable.find('li').each(function () {
          re_items_raw.push($(this).attr('data-id'));
        });
      });

      if (cfg('onlyOrder') === 2) {
        /* 1. Deal with 'Only select, order of options does not matter' */
        var currentTargetListId = '';
        var tl_re_items = {};     // This will hold the items per target box.
        var targetListIds = [""]; // This will remember the order of the target lists.

        // Get items per target list.
        $.each(re_items_raw, function (i, re_item) {
          re_item = re_item.replace(/soEditor_?/g, '');
          tl_re_items[currentTargetListId] = $.isArray(tl_re_items[currentTargetListId]) ? tl_re_items[currentTargetListId] : [];
          var isSpecialItem = typeof _globals.specialItems[re_item] !== 'undefined';

          // Change target?
          if (re_item.match('[0-9]')) {
            currentTargetListId = re_item;
            targetListIds.push(currentTargetListId);
            return;
          }
          else {
            // Get items per target.
            if (_globals.idToRe[re_item]) {
              re_item = _globals.idToRe[re_item].re;
            }

            if (re_item !== '' && (cfg('allowDuplicates') === true || tl_re_items[currentTargetListId].indexOf(re_item) < 0 || isSpecialItem)) {
              tl_re_items[currentTargetListId].push(re_item);
            }
          }
        });

        $.each(targetListIds, function (i, targetListId) {
          if (targetListId !== '') {
            re_items.push(targetListId);
          }
          tl_re_items[targetListId] = $.isArray(tl_re_items[targetListId]) ? tl_re_items[targetListId] : [];
          if (tl_re_items[targetListId].length > 0) {
            re_items.push('(');
            re_items.push('(' + getAnagrams(tl_re_items[targetListId]).join(')|(') + ')');
            re_items.push(')');
          }
        });
      }
      else {
        /* 2. Deal with 'Select & order' and 'Only order': get a flat list of reg exp items. */
        $.each(re_items_raw, function (i, re_item) {
          re_item = re_item.replace(/soEditor_?/g, '');
          if (_globals.idToRe[re_item] !== undefined) {
            re_item = _globals.idToRe[re_item].re;
          }
          re_items[i] = re_item;
        });
      }

      // Fill input
      _globals.$input.val('^' + re_items.join('') + '$');

      // Keep input and editor in sync
      _globals.$input.trigger('change');
    }

    /**
     * Updates the question GUI based on the value of the input.
     */
    function updateQuestionGUI() {
      /* Process regular expression */
      var val = _globals.$input.val();
      var re_targets;
      var re_items_raw;
      var re_items = [];
      var shouldDisableEditor = false;

      if (cfg('onlyOrder') === 2) {
        /* 1. Deal with 'Only select, order of options does not matter' */

        // Get items per target.
        re_items_raw = val.split(/[\d]/).filter(function (str) {
          return (str === "" || str === "^" || str === "$") ? false : true;
        });

        // Get target ids.
        re_targets = val.split(/[^\d]/).filter(function (str) {
          return str === "" ? false : true;
        });
        if (re_targets.length === 0) {
          re_targets.push(""); // In case there is 1 target, fake its id.
        }

        // Rebuild val with unique items per target box.
        val = "";
        $.each(re_items_raw, function (i, re_item_raw) {
          var re_item = re_item_raw.split("|")[0];
          val += re_targets[i] + re_item;
        });
      }

      /* 2. Get a flat list of reg exp items. */
      re_items_raw = val.split('');
      $.each(re_items_raw, function (i, re_item) {
        var removed_item;
        if (_globals.reToId[re_item] !== undefined) {
          re_item = _globals.reToId[re_item].id;
        }

        switch (re_item) {
          case '__negate__':
            if (i > 0) {
              re_items.push(re_item);
            }
            break;

          case '$':
            break;

          case '__star__':
            // Replace '__dot__'-element followed by '__star__'-element
            // by '__dot____star__'-element
            if (re_items[re_items.length - 1] === '__dot__') {
              removed_item = re_items.pop();
              re_items.push(removed_item + re_item);
            }
            break;

          case '__opencurlybrace__':
          case '__closecurlybrace__':
          case '__opensquarebracket__':
          case '__closequarebracket__':
            shouldDisableEditor = true;
            break;

          default:
            re_items.push(re_item);
            break;
        }
      });

      /* Update question */

      // Get a list with unique items in the 'available items' box
      _globals.$sourceSortable.append($(".soEditor_target_sortable").children());
      formatAvailableItemsSortable();

      var $target = $('.soEditor_target_sortable').eq(0);
      $.each(re_items, function (i, re_item) {
        // Change target?
        if (re_item.match('[0-9]')) {
          $target = $('[data-id="soEditor_' + re_item + '"]');
          return;
        }

        // Prevent all strange DOM ids from being generated.
        if (cfg('onlyOrder') !== 2 && re_item.match('[^a-zA-z0-9]')) {
          shouldDisableEditor = true;
        }
        else if (re_item.match('[^a-zA-z0-9_\(\)\|]')) { // Only select can contain more items.
          shouldDisableEditor = true;
          return;
        }

        // Move item.
        var $move_item = $('[data-id="soEditor_' + re_item + '"]', _globals.$sourceSortable);

        if ($move_item.hasClass('soEditor_special') || cfg('allowDuplicates')) {
          $move_item = $move_item.clone(); // Clone if special item or allowDuplicates
        }

        // Append item to target
        $target.append($move_item);
      });

      if (shouldDisableEditor === true) {
        disableEditor();
      }
      else {
        enableEditor();
      }
      formatTargetItemsSortable();
    }

    /**
     * Returns a setting
     *
     * @param {string} paramName
     * @returns {mixed}
     */
    function cfg(paramName) {
      var returnVal;
      if (paramName === 'onlyOrder') {
        returnVal = parseInt(_globals.settings.onlyOrder, 10);
        returnVal = isNaN(returnVal) ? -1 : returnVal;
      }
      else {
        returnVal = _globals.settings[paramName];
      }

      return returnVal;
    }

    /**
     * Remove duplicates from a list.
     *
     * @param {object} $ul
     *  A jQuery DOM list object.
     */
    function sortableUniqueItems($ul, ignoreSpecialItems) {
      ignoreSpecialItems = typeof ignoreSpecialItems === 'undefined' ? false : ignoreSpecialItems;
      var seen = {};
      $ul.find('li').each(function () {
        // Remove duplicates.
        var $li = $(this);
        var id = $li.attr('data-id');
        var re_item = id.replace(/soEditor_?/g, '');
        var isSpecialItem = typeof _globals.specialItems[re_item] !== 'undefined';

        if (seen[id] === true && ((isSpecialItem === true && ignoreSpecialItems === false) || isSpecialItem === false)) {
          $li.remove();
        }
        else {
          seen[id] = true;
        }
      });
    }

    /**
     * Format 'available items' sortable.
     */
    function formatAvailableItemsSortable() {
      // Make sure sortable has unique items.
      sortableUniqueItems(_globals.$sourceSortable);

      // Unset margin left set by formatTargetItemsSortable.
      _globals.$sourceSortable.find('li').css({"margin-left": ''});
    }

    /**
     * Format target item sortables.
     */
    function formatTargetItemsSortable() {
      // Deal with 'Only select, order of options does not matter'
      if (cfg('onlyOrder') === 2 && cfg('allowDuplicates') === false) {
        $(".soEditor_target_sortable").each(function () {
          sortableUniqueItems($(this), true);  // Make sure each target sortable has unique items.
        });
      }
    }

    /**
     * Formats HTML
     *
     * @param {string} html
     * @returns {string}
     *   The formatted contents.
     */
    function formatHTML(html) {
      /* Handle Media tags */
      var inlineTag = '', imgHTML = 'img', mediaObj, i;
      var html, styleAttr;
      var matches = html.match(/\[\[.*?\]\]/g);
      if (matches) {
        for (i = 0; i < matches.length; i++) {
          inlineTag = matches[i];
          inlineTag = inlineTag.replace('[[', '').replace(']]', '');
          mediaObj = JSON.parse(inlineTag);
          if (mediaObj && mediaObj.attributes && (mediaObj.attributes['data-src'] || mediaObj.attributes['src'])) {
            mediaObj.attributes['data-src'] = mediaObj.attributes['data-src'] ? mediaObj.attributes['data-src'] : mediaObj.attributes['src'];
            styleAttr = mediaObj.attributes.style ? ' style="' + mediaObj.attributes.style + '"' : '';
            imgHTML = '<img src="' + mediaObj.attributes['data-src'] + '" width="' + mediaObj.attributes['width'] + '" height="' + mediaObj.attributes['height'] + '"' + styleAttr + ' />';
          }
          html = html.replace(matches[i], imgHTML);
        }
      }
      return html;
    }

    /* initialize soEditor
     */
    if (_cdnFilesToBeLoaded.length === 0) {
      _helper.doInit();
    }
  };

  $.fn.soEditor = function (settings) {
    return this.each(function () {
      if (undefined === $(this).data("soEditor")) {
        var plugin = new $.soEditor(this, settings);
        $(this).data("soEditor", plugin);
      }
    });
  };
})(jQuery);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc