<commit_before>// CKEditor-jQuery EditorBox plugin
// Called on a jQuery selector (should be a single object only)
// to initialize a CKEditor editor box in the place of the
// selected textarea: $("#edit").editorBox().  The textarea
// must have a unique id in order to function properly.
// editorBox():
// Initializes the object.  All other methods should
// only be called on an already-initialized box.
// editorBox('focus', [keepTrying])
//   Passes focus to the selected editor box.  Returns
//   true/false depending on whether the focus attempt was
//   successful.  If the editor box has not completely initialized
//   yet, then the focus will fail.  If keepTrying
//   is defined and true, the method will keep trying until
//   the focus attempt succeeds.
// editorBox('destroy')
//   Removes the CKEditor instance from the textarea.
// editorBox('toggle')
//   Toggles the CKEditor instance.  Switches back and forth between
//   the textarea and the CKEditor WYSIWYG.
// editorBox('get_code')
//   Returns the plaintext code contained in the textarea or WYSIGWYG.
// editorBox('set_code', code)
//   Sets the plaintext code content for the editor box.  Replaces ALL
//   content with the string value of code.
// editorBox('insert_code', code)
//   Inserts the string value of code at the current selection point.
// editorBox('create_link', options)
//   Creates an anchor link at the current selection point.  If anything
//   is selected, makes the selection a link, otherwise creates a link.
//   options.url is used for the href of the link, and options.title
//   will be the body of the link if no text is currently selected.

define([
  'i18nObj',
  'jquery',
  'compiled/editor/editorAccessibility', /* editorAccessibility */
  'INST', // for IE detection; need to handle links in a special way
  'ckeditor-all', // CKEditor
  'jqueryui/draggable' /* /\.draggable/ */,
  'jquery.instructure_misc_plugins' /* /\.indicate/ */,
  'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
  'vendor/jquery.ba-tinypubsub',
  'vendor/scribd.view' /* scribd */
], function(I18nObj, $, EditorAccessibility, INST) {

  var enableBookmarking = !!INST.browser.ie;
  $(document).ready(function() {
    enableBookmarking = !!INST.browser.ie;
  });

  function EditorBoxList() {
    this._textareas = {};
    this._editors = {};
    this._editor_boxes = {};
  }

  $.extend(EditorBoxList.prototype, {
    _addEditorBox: function(id, box) {
      $.publish('editorBox/add', id, box);
      this._editor_boxes[id] = box;
      this._editors[id] = CKEDITOR.instances[id];
      this._textareas[id] = $("textarea#" + id);
    },
    _removeEditorBox: function(id) {
      delete this._editor_boxes[id];
      delete this._editors[id];
      delete this._textareas[id];
      $.publish('editorBox/remove', id);
      if ($.isEmptyObject(this._editors)) $.publish('editorBox/removeAll');
    },
    _getTextArea: function(id) {
      if(!this._textareas[id]) {
        this._textareas[id] = $("textarea#" + id);
      }
      return this._textareas[id];
    },
    _getEditor: function(id) {
      if(!this._editors[id]) {
        this._editors[id] = CKEDITOR.instances[id];
      }
      return this._editors[id];
    },
    _getEditorBox: function(id) {
      return this._editor_boxes[id];
    }
  });

  var $instructureEditorBoxList = new EditorBoxList();

  function EditorBox(id, search_url, submit_url, content_url, options) {
    options = $.extend({}, options);

    var $textarea = $("#" + id);
    $textarea.data('enable_bookmarking', enableBookmarking);
    var width = $textarea.width();
    if(width == 0) {
      width = $textarea.closest(":visible").width();
    }

    var extra_buttons = ['instructure_image'];
    for(var idx in INST.editorButtons) {
      extra_buttons.push("instructure_external_button_" + INST.editorButtons[idx].id);
    }

    var toolbar = [
        {name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize', 'Bold', 'Italic', 'Underline',/*forecolor,backcolor,*/'RemoveFormat']},
        {name: 'paragraph', items: ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'BulletedList', 'NumberedList', 'Outdent', 'Indent', 'Superscript', 'Subscript']},
        {name: 'insert', items: ['Table', 'EqnEditor', 'instructure_links', 'Unlink'].concat(extra_buttons)}
    ];

    var ckOptions = $.extend({
      extraAllowedContent: "iframe[src|width|height|name|align|style|class|sandbox]",
      startupFocus: options.focus,
      toolbar: toolbar,
      pasteFromWordRemoveFontStyles: false,
      pasteFromWordRemoveStyles: false,
      extraPlugins: 'instructure_external_tools,instructure_links,instructure_image',
      removePlugins: 'image,link',
      on: {
        focus: function(evt) {
          var $editor = $(evt.editor.element);
          $(document).triggerHandler('editor_box_focus', $editor);
          $.publish('editorBox/focus', $editor);
        }
      }
    }, options.tinyOptions || {});

    $textarea.ckeditor(ckOptions);

    this._textarea =  $textarea;
    this._editor = null;
    this._id = id;
    this._searchURL = search_url;
    this._submitURL = submit_url;
    this._contentURL = content_url;
    $instructureEditorBoxList._addEditorBox(id, this);
  }

// --------------------------------------------------------------------

  var editorBoxIdCounter = 1;

  $.fn.editorBox = function(options, more_options) {
    var args = arguments;
    if(this.length > 1) {
      return this.each(function() {
        var $this = $(this);
        $this.editorBox.apply($this, args);
      });
    }

    var id = this.attr('id');
    if(typeof(options) == "string" && options != "create") {
      if(options == "get_code") {
        return this._getContentCode(more_options);
      } else if(options == "set_code") {
        this._setContentCode(more_options);
      } else if(options == "insert_code") {
        this._insertHTML(more_options);
      } else if(options == "create_link") {
        this._linkSelection(more_options);
      } else if(options == "focus") {
        return this._editorFocus(more_options);
      } else if(options == "toggle") {
        this._toggleView();
      } else if(options == "execute") {
        var arr = [];
        for(var idx = 1; idx < arguments.length; idx++) {
          arr.push(arguments[idx]);
        }
        return $.fn._execCommand.apply(this, arr);
      } else if(options == "destroy") {
        this._removeEditor(more_options);
      } else if(options == "is_dirty") {
        return $instructureEditorBoxList._getEditor(id).checkDirty();
      } else if(options == 'exists?') {
        return !!$instructureEditorBoxList._getEditor(id);
      }
      return this;
    }
    this.data('rich_text', true);
    if(!id) {
      id = 'editor_box_unique_id_' + editorBoxIdCounter++;
      this.attr('id', id);
    }
    if($instructureEditorBoxList._getEditor(id)) {
      this._setContentCode(this.val());
      return this;
    }
    var search_url = "";
    if(options && options.search_url) {
      search_url = options.search_url;
    }
    var box = new EditorBox(id, search_url, "", "", options);
    return this;
  };

  $.fn._execCommand = function() {
    var id = $(this).attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    if(editor && editor.execCommand) {
      editor.execCommand.apply(editor, arguments);
    }
    return this;
  };

  $.fn._justGetCode = function() {
    var id = this.attr('id') || '';
    var content = '';
    try {
      content = $instructureEditorBoxList._getTextArea(id).val();
    } catch(e) {}
    return content;
  };

  $.fn._getContentCode = function(update) {
    if(update == true) {
      var content = this._justGetCode(); //""
      this._setContentCode(content);
    }
    return this._justGetCode();
  };

  $.fn._getSearchURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._searchURL;
  };

  $.fn._getSubmitURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._submitURL;
  };

  $.fn._getContentURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._contentURL;
  };

  $.fn._toggleView = function() {
    var id = this.attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.setMode(editor.mode === 'source' ? 'wysiwyg' : 'source');
  };

  $.fn._removeEditor = function() {
    var id = this.attr('id');
    this.data('rich_text', false);
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.destroy();
    $instructureEditorBoxList._removeEditorBox(id);
  };

  $.fn._setContentCode = function(val) {
    var id = this.attr('id');
    $instructureEditorBoxList._getTextArea(id).val(val);
    $instructureEditorBoxList._getEditor(id).setData(val);
  };

  $.fn._insertHTML = function(html) {
    var id = this.attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.insertHtml(html, 'unfiltered_html');
  };

  $.fn._editorFocus = function() {
    var $element = this,
        id = $element.attr('id'),
        editor = $instructureEditorBoxList._getEditor(id);
    if(!editor ) {
      return false;
    }
    editor.focus();
    $.publish('editorBox/focus', $element);
    return true;
  };

  $.fn._linkSelection = function(options) {
    if(typeof(options) == "string") {
      options = {url: options};
    }
    var title = options.title;
    var url = options.url || "";
    if(url.match(/@/) && !url.match(/\//) && !url.match(/^mailto:/)) {
      url = "mailto:" + url;
    } else if(!url.match(/^\w+:\/\//) && !url.match(/^mailto:/) && !url.match(/^\//)) {
      url = "http://" + url;
    }
    var classes = options.classes || "";
    var defaultText = options.text || options.title || "Link";
    var target = options.target || null;
    var id = $(this).attr('id');
    if(url.indexOf("@") != -1) {
      options.file = false;
      options.image = false;
      if(url.indexOf("mailto:") != 0) {
        url = "mailto:" + url;
      }
    } else if (url.indexOf("/") == -1) {
      title = url;
      url = url.replace(/\s/g, "");
      url = location.href + url;
    }
    if(options.file) {
      classes += "instructure_file_link ";
    }
    if(options.scribdable) {
      classes += "instructure_scribd_file ";
    }
    var link_id = '';
    if(options.kaltura_entry_id && options.kaltura_media_type) {
      link_id = "media_comment_" + options.kaltura_entry_id;
      if(options.kaltura_media_type == 'video') {
        classes += "instructure_video_link ";
      } else {
        classes += "instructure_audio_link ";
      }
    }
    if(options.image) {
      classes += "instructure_image_thumbnail ";
    }
    classes = $.unique(classes.split(/\s+/)).join(" ");
    var selectionText = "";
    var editor = $instructureEditorBoxList._getEditor(id);
    if(enableBookmarking && this.data('last_bookmark')) {
      editor.getSelection().selectBookmarks(this.data('last_bookmark'));
    }
    var selection = editor.getSelection();
    var anchor = selection.getSelectedElement();
    while(anchor && anchor.getName() != 'A' && anchor.getName() != 'BODY' && anchor.getParent()) {
      anchor = anchor.getParent();
    }
    if(anchor && anchor.getName() != 'A') { anchor = null; }

    var selectedContent = selection.getSelectedText();
    if(!selectedContent || selectedContent == "") {
      if(anchor) {
        $(anchor).attr({
          href: url,
          title: title || '',
          id: link_id,
          'class': classes,
          target: target
        });
      } else {
        selectionText = defaultText;
        var $div = $("<div/>");
        $div.append($("<a/>", {id: link_id, target: target, title: title, href: url, 'class': classes}).text(selectionText));
        editor.insertHtml($div.html(), 'unfiltered_html');
      }
    } else {
      var $div = $("<div/>");
      $div.append($("<a/>", {target: (target || ''), title: (title || ''), href: url, 'class': classes, 'id': link_id}).text(selectedContent));
      editor.insertHtml($div.html(), 'unfiltered_html');
    }
  };

});
<commit_msg>Fix problem with links on CKEditor visual editor<commit_after>// CKEditor-jQuery EditorBox plugin
// Called on a jQuery selector (should be a single object only)
// to initialize a CKEditor editor box in the place of the
// selected textarea: $("#edit").editorBox().  The textarea
// must have a unique id in order to function properly.
// editorBox():
// Initializes the object.  All other methods should
// only be called on an already-initialized box.
// editorBox('focus', [keepTrying])
//   Passes focus to the selected editor box.  Returns
//   true/false depending on whether the focus attempt was
//   successful.  If the editor box has not completely initialized
//   yet, then the focus will fail.  If keepTrying
//   is defined and true, the method will keep trying until
//   the focus attempt succeeds.
// editorBox('destroy')
//   Removes the CKEditor instance from the textarea.
// editorBox('toggle')
//   Toggles the CKEditor instance.  Switches back and forth between
//   the textarea and the CKEditor WYSIWYG.
// editorBox('get_code')
//   Returns the plaintext code contained in the textarea or WYSIGWYG.
// editorBox('set_code', code)
//   Sets the plaintext code content for the editor box.  Replaces ALL
//   content with the string value of code.
// editorBox('insert_code', code)
//   Inserts the string value of code at the current selection point.
// editorBox('create_link', options)
//   Creates an anchor link at the current selection point.  If anything
//   is selected, makes the selection a link, otherwise creates a link.
//   options.url is used for the href of the link, and options.title
//   will be the body of the link if no text is currently selected.

define([
  'i18nObj',
  'jquery',
  'compiled/editor/editorAccessibility', /* editorAccessibility */
  'INST', // for IE detection; need to handle links in a special way
  'ckeditor-all', // CKEditor
  'jqueryui/draggable' /* /\.draggable/ */,
  'jquery.instructure_misc_plugins' /* /\.indicate/ */,
  'vendor/jquery.scrollTo' /* /\.scrollTo/ */,
  'vendor/jquery.ba-tinypubsub',
  'vendor/scribd.view' /* scribd */
], function(I18nObj, $, EditorAccessibility, INST) {

  var enableBookmarking = !!INST.browser.ie;
  $(document).ready(function() {
    enableBookmarking = !!INST.browser.ie;
  });

  function EditorBoxList() {
    this._textareas = {};
    this._editors = {};
    this._editor_boxes = {};
  }

  $.extend(EditorBoxList.prototype, {
    _addEditorBox: function(id, box) {
      $.publish('editorBox/add', id, box);
      this._editor_boxes[id] = box;
      this._editors[id] = CKEDITOR.instances[id];
      this._textareas[id] = $("textarea#" + id);
    },
    _removeEditorBox: function(id) {
      delete this._editor_boxes[id];
      delete this._editors[id];
      delete this._textareas[id];
      $.publish('editorBox/remove', id);
      if ($.isEmptyObject(this._editors)) $.publish('editorBox/removeAll');
    },
    _getTextArea: function(id) {
      if(!this._textareas[id]) {
        this._textareas[id] = $("textarea#" + id);
      }
      return this._textareas[id];
    },
    _getEditor: function(id) {
      if(!this._editors[id]) {
        this._editors[id] = CKEDITOR.instances[id];
      }
      return this._editors[id];
    },
    _getEditorBox: function(id) {
      return this._editor_boxes[id];
    }
  });

  var $instructureEditorBoxList = new EditorBoxList();

  function EditorBox(id, search_url, submit_url, content_url, options) {
    options = $.extend({}, options);

    var $textarea = $("#" + id);
    $textarea.data('enable_bookmarking', enableBookmarking);
    var width = $textarea.width();
    if(width == 0) {
      width = $textarea.closest(":visible").width();
    }

    var extra_buttons = ['instructure_image'];
    for(var idx in INST.editorButtons) {
      extra_buttons.push("instructure_external_button_" + INST.editorButtons[idx].id);
    }

    var toolbar = [
        {name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize', 'Bold', 'Italic', 'Underline',/*forecolor,backcolor,*/'RemoveFormat']},
        {name: 'paragraph', items: ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'BulletedList', 'NumberedList', 'Outdent', 'Indent', 'Superscript', 'Subscript']},
        {name: 'insert', items: ['Table', 'EqnEditor', 'instructure_links', 'Unlink'].concat(extra_buttons)}
    ];

    var ckOptions = $.extend({
      extraAllowedContent: "iframe[src|width|height|name|align|style|class|sandbox]",
      startupFocus: options.focus,
      toolbar: toolbar,
      pasteFromWordRemoveFontStyles: false,
      pasteFromWordRemoveStyles: false,
      extraPlugins: 'instructure_external_tools,instructure_links,instructure_image',
      removePlugins: 'image',
      extraAllowedContent: 'a[!href]',
      on: {
        focus: function(evt) {
          var $editor = $(evt.editor.element);
          $(document).triggerHandler('editor_box_focus', $editor);
          $.publish('editorBox/focus', $editor);
        }
      }
    }, options.tinyOptions || {});

    $textarea.ckeditor(ckOptions);

    this._textarea =  $textarea;
    this._editor = null;
    this._id = id;
    this._searchURL = search_url;
    this._submitURL = submit_url;
    this._contentURL = content_url;
    $instructureEditorBoxList._addEditorBox(id, this);
  }

// --------------------------------------------------------------------

  var editorBoxIdCounter = 1;

  $.fn.editorBox = function(options, more_options) {
    var args = arguments;
    if(this.length > 1) {
      return this.each(function() {
        var $this = $(this);
        $this.editorBox.apply($this, args);
      });
    }

    var id = this.attr('id');
    if(typeof(options) == "string" && options != "create") {
      if(options == "get_code") {
        return this._getContentCode(more_options);
      } else if(options == "set_code") {
        this._setContentCode(more_options);
      } else if(options == "insert_code") {
        this._insertHTML(more_options);
      } else if(options == "create_link") {
        this._linkSelection(more_options);
      } else if(options == "focus") {
        return this._editorFocus(more_options);
      } else if(options == "toggle") {
        this._toggleView();
      } else if(options == "execute") {
        var arr = [];
        for(var idx = 1; idx < arguments.length; idx++) {
          arr.push(arguments[idx]);
        }
        return $.fn._execCommand.apply(this, arr);
      } else if(options == "destroy") {
        this._removeEditor(more_options);
      } else if(options == "is_dirty") {
        return $instructureEditorBoxList._getEditor(id).checkDirty();
      } else if(options == 'exists?') {
        return !!$instructureEditorBoxList._getEditor(id);
      }
      return this;
    }
    this.data('rich_text', true);
    if(!id) {
      id = 'editor_box_unique_id_' + editorBoxIdCounter++;
      this.attr('id', id);
    }
    if($instructureEditorBoxList._getEditor(id)) {
      this._setContentCode(this.val());
      return this;
    }
    var search_url = "";
    if(options && options.search_url) {
      search_url = options.search_url;
    }
    var box = new EditorBox(id, search_url, "", "", options);
    return this;
  };

  $.fn._execCommand = function() {
    var id = $(this).attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    if(editor && editor.execCommand) {
      editor.execCommand.apply(editor, arguments);
    }
    return this;
  };

  $.fn._justGetCode = function() {
    var id = this.attr('id') || '';
    var content = '';
    try {
      content = $instructureEditorBoxList._getTextArea(id).val();
    } catch(e) {}
    return content;
  };

  $.fn._getContentCode = function(update) {
    if(update == true) {
      var content = this._justGetCode(); //""
      this._setContentCode(content);
    }
    return this._justGetCode();
  };

  $.fn._getSearchURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._searchURL;
  };

  $.fn._getSubmitURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._submitURL;
  };

  $.fn._getContentURL = function() {
    return $instructureEditorBoxList._getEditorBox(this.attr('id'))._contentURL;
  };

  $.fn._toggleView = function() {
    var id = this.attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.setMode(editor.mode === 'source' ? 'wysiwyg' : 'source');
  };

  $.fn._removeEditor = function() {
    var id = this.attr('id');
    this.data('rich_text', false);
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.destroy();
    $instructureEditorBoxList._removeEditorBox(id);
  };

  $.fn._setContentCode = function(val) {
    var id = this.attr('id');
    $instructureEditorBoxList._getTextArea(id).val(val);
    $instructureEditorBoxList._getEditor(id).setData(val);
  };

  $.fn._insertHTML = function(html) {
    var id = this.attr('id');
    var editor = $instructureEditorBoxList._getEditor(id);
    editor.insertHtml(html, 'unfiltered_html');
  };

  $.fn._editorFocus = function() {
    var $element = this,
        id = $element.attr('id'),
        editor = $instructureEditorBoxList._getEditor(id);
    if(!editor ) {
      return false;
    }
    editor.focus();
    $.publish('editorBox/focus', $element);
    return true;
  };

  $.fn._linkSelection = function(options) {
    if(typeof(options) == "string") {
      options = {url: options};
    }
    var title = options.title;
    var url = options.url || "";
    if(url.match(/@/) && !url.match(/\//) && !url.match(/^mailto:/)) {
      url = "mailto:" + url;
    } else if(!url.match(/^\w+:\/\//) && !url.match(/^mailto:/) && !url.match(/^\//)) {
      url = "http://" + url;
    }
    var classes = options.classes || "";
    var defaultText = options.text || options.title || "Link";
    var target = options.target || null;
    var id = $(this).attr('id');
    if(url.indexOf("@") != -1) {
      options.file = false;
      options.image = false;
      if(url.indexOf("mailto:") != 0) {
        url = "mailto:" + url;
      }
    } else if (url.indexOf("/") == -1) {
      title = url;
      url = url.replace(/\s/g, "");
      url = location.href + url;
    }
    if(options.file) {
      classes += "instructure_file_link ";
    }
    if(options.scribdable) {
      classes += "instructure_scribd_file ";
    }
    var link_id = '';
    if(options.kaltura_entry_id && options.kaltura_media_type) {
      link_id = "media_comment_" + options.kaltura_entry_id;
      if(options.kaltura_media_type == 'video') {
        classes += "instructure_video_link ";
      } else {
        classes += "instructure_audio_link ";
      }
    }
    if(options.image) {
      classes += "instructure_image_thumbnail ";
    }
    classes = $.unique(classes.split(/\s+/)).join(" ");
    var selectionText = "";
    var editor = $instructureEditorBoxList._getEditor(id);
    if(enableBookmarking && this.data('last_bookmark')) {
      editor.getSelection().selectBookmarks(this.data('last_bookmark'));
    }
    var selection = editor.getSelection();
    var anchor = selection.getSelectedElement();
    while(anchor && anchor.getName() != 'A' && anchor.getName() != 'BODY' && anchor.getParent()) {
      anchor = anchor.getParent();
    }
    if(anchor && anchor.getName() != 'A') { anchor = null; }

    var selectedContent = selection.getSelectedText();
    if(!selectedContent || selectedContent == "") {
      if(anchor) {
        $(anchor).attr({
          href: url,
          title: title || '',
          id: link_id,
          'class': classes,
          target: target
        });
      } else {
        selectionText = defaultText;
        var $div = $("<div/>");
        $div.append($("<a/>", {id: link_id, target: target, title: title, href: url, 'class': classes}).text(selectionText));
        editor.insertHtml($div.html(), 'unfiltered_html');
      }
    } else {
      var $div = $("<div/>");
      $div.append($("<a/>", {target: (target || ''), title: (title || ''), href: url, 'class': classes, 'id': link_id}).text(selectedContent));
      editor.insertHtml($div.html(), 'unfiltered_html');
    }
  };

});
<|endoftext|>


<commit_before>function findValue (parent, dotNot) {

    if (!dotNot) return undefined;

    var splits = dotNot.split(".");
    var value;

    for (var i = 0; i < splits.length; i++) {
        value = parent[splits[i]];
        if (value === undefined) return undefined;
        if (typeof value === "object") parent = value;
    }

    return value;
}

function findFunction (parent, dotNot) {

    var func = findValue(parent, dotNot);

    if (typeof func !== "function") {
        return undefined;
    }

    return func;
}

function addHandlerOnEvent (handler, miid, eventName) {

    var self = this;

    // if the handler is a module function name as string
    if (typeof handler === "string") {
        var handlerFunction = findFunction(self, handler) || findFunction(window, handler);
        if (handlerFunction) {
            self.on(eventName, miid, function() {
                handlerFunction.apply(self, arguments);
            });
        }
        return;
    }

    // else it must be an array of objects
    if (handler.length) {
        self.on(eventName, miid, function() {
            for (var i = 0; i < handler.length; ++i) {
                var step = handler[i];

                // this step is a handler function
                if (step.handler) {
                    var handlerName = step.handler;
                    var configArgs = [];

                    if (typeof step.handler === "object") {
                        handlerName = step.handler.name;
                        configArgs = step.handler.args || [];
                    }

                    var handlerFunction = findFunction(self, handlerName) || findFunction(window, handlerName);
                    if (typeof handlerFunction === "function") {
                        var allArgs = [];
                        // first we push the fixed (application.json arguments)
                        for (var ii = 0; ii < configArgs.length; ++ii) {
                            allArgs.push(configArgs[ii]);
                        }
                        // then come the dynamic ones from the emit arguments (data context, callback, etc.)
                        for (var ii = 0, l = arguments.length; ii < l; ++ii) {
                            allArgs.push(arguments[ii]);
                        }

                        handlerFunction.apply(self, allArgs);
                    }

                    continue;
                }

                // this step is an event emit
                if (step.emit) {
                    var eventName = step.emit;
                    var configArgs = [];

                    if (typeof step.emit === "object") {
                        eventName = step.emit.name;
                        configArgs = step.emit.args || [];
                    }

                    // then come the dynamic ones from the emit arguments (data context, callback, etc.)
                    for (var ii = 0, l = arguments.length; ii < l; ++ii) {
                        configArgs.push(arguments[ii]);
                    }

                    var allArgs = [];
                    allArgs.push(eventName);
                    for (var ii = 0; ii < configArgs.length; ++ii) {
                        allArgs.push(configArgs[ii]);
                    }

                    self.emit.apply(self, allArgs);
                }
            }
        });

        return;
    }
}

module.exports = function(config) {

    var self = this;

    // process only the listen property in the configurations
    for (var miid in config.listen) {
        if (!config.listen.hasOwnProperty(miid)) return;

        var miidEvents = config.listen[miid];

        for (var eventName in miidEvents) {
            if (!miidEvents.hasOwnProperty(eventName)) return;

            addHandlerOnEvent.call(self, miidEvents[eventName], miid, eventName);
        }
    }
};
<commit_msg>Fixed broken hasOwnProperty tests<commit_after>function findValue (parent, dotNot) {

    if (!dotNot) return undefined;

    var splits = dotNot.split(".");
    var value;

    for (var i = 0; i < splits.length; i++) {
        value = parent[splits[i]];
        if (value === undefined) return undefined;
        if (typeof value === "object") parent = value;
    }

    return value;
}

function findFunction (parent, dotNot) {

    var func = findValue(parent, dotNot);

    if (typeof func !== "function") {
        return undefined;
    }

    return func;
}

function addHandlerOnEvent (handler, miid, eventName) {

    var self = this;

    // if the handler is a module function name as string
    if (typeof handler === "string") {
        var handlerFunction = findFunction(self, handler) || findFunction(window, handler);
        if (handlerFunction) {
            self.on(eventName, miid, function() {
                handlerFunction.apply(self, arguments);
            });
        }
        return;
    }

    // else it must be an array of objects
    if (handler.length) {
        self.on(eventName, miid, function() {
            for (var i = 0; i < handler.length; ++i) {
                var step = handler[i];

                // this step is a handler function
                if (step.handler) {
                    var handlerName = step.handler;
                    var configArgs = [];

                    if (typeof step.handler === "object") {
                        handlerName = step.handler.name;
                        configArgs = step.handler.args || [];
                    }

                    var handlerFunction = findFunction(self, handlerName) || findFunction(window, handlerName);
                    if (typeof handlerFunction === "function") {
                        var allArgs = [];
                        // first we push the fixed (application.json arguments)
                        for (var ii = 0; ii < configArgs.length; ++ii) {
                            allArgs.push(configArgs[ii]);
                        }
                        // then come the dynamic ones from the emit arguments (data context, callback, etc.)
                        for (var ii = 0, l = arguments.length; ii < l; ++ii) {
                            allArgs.push(arguments[ii]);
                        }

                        handlerFunction.apply(self, allArgs);
                    }

                    continue;
                }

                // this step is an event emit
                if (step.emit) {
                    var eventName = step.emit;
                    var configArgs = [];

                    if (typeof step.emit === "object") {
                        eventName = step.emit.name;
                        configArgs = step.emit.args || [];
                    }

                    // then come the dynamic ones from the emit arguments (data context, callback, etc.)
                    for (var ii = 0, l = arguments.length; ii < l; ++ii) {
                        configArgs.push(arguments[ii]);
                    }

                    var allArgs = [];
                    allArgs.push(eventName);
                    for (var ii = 0; ii < configArgs.length; ++ii) {
                        allArgs.push(configArgs[ii]);
                    }

                    self.emit.apply(self, allArgs);
                }
            }
        });

        return;
    }
}

module.exports = function(config) {

    var self = this;

    // process only the listen property in the configurations
    for (var miid in config.listen) {
        if (!config.listen.hasOwnProperty(miid)) continue;

        var miidEvents = config.listen[miid];

        for (var eventName in miidEvents) {
            if (!miidEvents.hasOwnProperty(eventName)) continue;

            addHandlerOnEvent.call(self, miidEvents[eventName], miid, eventName);
        }
    }
};

<|endoftext|>


<commit_before>define([], function () {
  return function bootstrapEmbedImage (register) {
    register("image", /\.(gif|png|jpe?g)$/i, {
      title: "Images",
      enabled: true
    },
    function (link) {
      link.div.append("");
    });
  };
});
<commit_msg>Quick fix for issue 202<commit_after>define([], function () {
  return function bootstrapEmbedImage (register) {
    register("image", /\.(gif|png|jpe?g)/i, {
      title: "Images",
      enabled: true
    },
    function (link) {
      link.div.append("");
    });
  };
});
<|endoftext|>


<commit_before>var key = require('../utils/key');
var sync = require('synchronize');
var request = require('request');
var _ = require('underscore');

// The API that returns the in-email representation.
module.exports = function(req, res) {
  var url = req.query.url.trim();

  // Giphy image urls are in the format:
  // http://giphy.com/gifs/<seo-text>-<alphanumeric id>
  var matches = url.match(/\-([a-zA-Z0-9]+)$/);
  if (!matches) {
    res.status(400).send('Invalid URL format');
    return;
  }

  var id = matches[1];

  var response;
  try {
    response = sync.await(request({
      url: 'http://api.giphy.com/v1/gifs/' + encodeURIComponent(id),
      qs: {
        api_key: key
      },
      gzip: true,
      json: true,
      timeout: 15 * 1000
    }, sync.defer()));
  } catch (e) {
    res.status(500).send('Error');
    return;
  }

  var image = response.body.data.images.original;
  var width = image.width > 600 ? 600 : image.width;
  var html = '';
  res.json({
    body: html
  });
};<commit_msg>Add note about raw:true.<commit_after>var key = require('../utils/key');
var sync = require('synchronize');
var request = require('request');
var _ = require('underscore');

// The API that returns the in-email representation.
module.exports = function(req, res) {
  var url = req.query.url.trim();

  // Giphy image urls are in the format:
  // http://giphy.com/gifs/<seo-text>-<alphanumeric id>
  var matches = url.match(/\-([a-zA-Z0-9]+)$/);
  if (!matches) {
    res.status(400).send('Invalid URL format');
    return;
  }

  var id = matches[1];

  var response;
  try {
    response = sync.await(request({
      url: 'http://api.giphy.com/v1/gifs/' + encodeURIComponent(id),
      qs: {
        api_key: key
      },
      gzip: true,
      json: true,
      timeout: 15 * 1000
    }, sync.defer()));
  } catch (e) {
    res.status(500).send('Error');
    return;
  }

  var image = response.body.data.images.original;
  var width = image.width > 600 ? 600 : image.width;
  var html = '';
  res.json({
    body: html
    // Add raw:true if you're returning content that you want the user to be able to edit
  });
};<|endoftext|>


<commit_before>import GenericResolver from 'api/resolver/generic/genericResolver'
import {ObjectId} from 'mongodb'
import Hook from '../../util/hook'

const UtilCms = {
    resolveData: async (db, context, dataResolver, scope) => {
        const resolvedData = {}, subscriptions = []

        if (dataResolver) {
            let debugInfo = null
            try {
                const tpl = new Function('const {' + Object.keys(scope).join(',') + '} = this; return `' + dataResolver + '`;')
                const dataResolverReplaced = tpl.call(scope)
                const json = JSON.parse(dataResolverReplaced)

                for (let i = 0; i < json.length; i++) {
                    debugInfo = ''
                    const {t, f, l, o, p, d, KeyValues, restriction} = json[i]
                    /*
                     f = filter for the query
                     t = type
                     d = the data / fields you want to access
                     l = limit of results
                     o = offset
                     p = page (if no offset is defined, offset is limit * (page -1) )
                     restriction = if it is set to "user" only entries that belongs to the user are returned
                     */
                    if (t) {
                        let type, match
                        if (t.indexOf('$') === 0) {
                            type = t.substring(1)
                            subscriptions.push(type)
                        } else {
                            type = t
                        }

                        if (restriction && restriction === 'user') {
                            match = {createdBy: ObjectId(context.id)}
                        } else {
                            match = {}
                        }

                        debugInfo += ' type=' + type
                        const result = await GenericResolver.entities(db, context, type, d, {
                            filter: f,
                            limit: l,
                            page: p,
                            offset: o,
                            match
                        })

                        debugInfo += ' result=true'
                        //TODO: only return fields that are request and remove sensitiv data

                        if (result.results) {
                            result.results.map(e => {
                                // return only user _id and username
                                if (e.createdBy) {
                                    e.createdBy = {_id: e.createdBy._id, username: e.createdBy.username}
                                }
                            })
                        }

                        resolvedData[json[i].key || type] = result
                    } else if (KeyValues) {

                        const map = {}

                        const match = {createdBy: ObjectId(context.id), key: {$in: KeyValues}}

                        const result = await GenericResolver.entities(db, context, 'KeyValue', ["key", "value"], {
                            match
                        })

                        if (result.results) {
                            result.results.map(e => {
                                try {
                                    map[e.key] = JSON.parse(e.value);
                                } catch (e) {
                                    map[e.key] = e.value
                                }
                            })
                        }

                        resolvedData[json[i].key || 'KeyValues'] = map

                    } else {
                        console.log('call cmsCustomResolver', json[i])
                        Hook.call('cmsCustomResolver', {resolvedData, resolver: json[i]})
                    }

                }
            } catch (e) {
                resolvedData.error = e.message + ' -> scope=' + JSON.stringify(scope) + debugInfo
            }
        }
        return {resolvedData, subscriptions}
    }
}

export default UtilCms<commit_msg>Small improvments<commit_after>import GenericResolver from 'api/resolver/generic/genericResolver'
import {ObjectId} from 'mongodb'
import Hook from '../../util/hook'

const UtilCms = {
    createSegments: (json, scope) => {
        let inSegment = false, segments = [], count = 0, buffer = ''
        json.split('\n').forEach(line => {
            const lineTrimmed = line.trim()
            if (lineTrimmed.indexOf('{') === 0) {
                count++
                if (!inSegment) {
                    inSegment = true
                }
            } else if (lineTrimmed.indexOf('}') === 0) {
                count--
                if (count === 0 && inSegment) {
                    segments.push(buffer + '}')
                    buffer = ''
                    inSegment = false
                }
            }
            if (inSegment) {
                buffer += lineTrimmed
            }
        })
        return segments
    },
    replaceSegment: (str, data) => {

    },
    resolveData: async (db, context, dataResolver, scope) => {
        const resolvedData = {}, subscriptions = []

        if (dataResolver) {
            let debugInfo = null
            try {
                const segments = UtilCms.createSegments(dataResolver, scope)

                for (let i = 0; i < segments.length; i++) {
                    debugInfo = ''

                    const tpl = new Function('const {' + Object.keys(scope).join(',') + '} = this.scope;const {data} = this; return `' + segments[i] + '`;')
                    const replacedSegmentStr = tpl.call({scope, data: resolvedData})
                    const segment = JSON.parse(replacedSegmentStr)

                    if (segment.t) {
                        const {t, f, l, o, p, d} = segment
                        /*
                         f = filter for the query
                         t = type
                         d = the data / fields you want to access
                         l = limit of results
                         o = offset
                         p = page (if no offset is defined, offset is limit * (page -1) )
                         */

                        let type, match
                        if (t.indexOf('$') === 0) {
                            type = t.substring(1)
                            subscriptions.push(type)
                        } else {
                            type = t
                        }

                        // restriction = if it is set to "user" only entries that belongs to the user are returned
                        if (segment.restriction && segment.restriction === 'user') {
                            match = {createdBy: ObjectId(context.id)}
                        } else {
                            match = {}
                        }

                        debugInfo += ' type=' + type
                        const result = await GenericResolver.entities(db, context, type, d, {
                            filter: f,
                            limit: l,
                            page: p,
                            offset: o,
                            match
                        })

                        debugInfo += ' result=true'
                        //TODO: only return fields that are request and remove sensitiv data

                        if (result.results) {
                            result.results.map(e => {
                                // return only user _id and username
                                if (e.createdBy) {
                                    e.createdBy = {_id: e.createdBy._id, username: e.createdBy.username}
                                }
                            })
                        }

                        resolvedData[segment.key || type] = result
                    } else if (segment.eval) {
                        const tpl = new Function('const {' + Object.keys(scope).join(',') + '} = this.scope; const {data} = this;' + segment.eval)
                        tpl.call({data: resolvedData, scope})
                    } else if (segment.keyValues) {

                        const map = {}

                        const match = {createdBy: ObjectId(context.id), key: {$in: segment.keyValues}}

                        const result = await GenericResolver.entities(db, context, 'KeyValue', ["key", "value"], {
                            match
                        })

                        if (result.results) {
                            result.results.map(e => {
                                try {
                                    map[e.key] = JSON.parse(e.value);
                                } catch (e) {
                                    map[e.key] = e.value
                                }
                            })
                        }

                        resolvedData[segment.key || 'keyValues'] = map

                    } else {
                        console.log('call cmsCustomResolver', segment)
                        Hook.call('cmsCustomResolver', {resolvedData, resolver: segment})
                    }

                }
            } catch (e) {
                resolvedData.error = e.message + ' -> scope=' + JSON.stringify(scope) + debugInfo
            }
        }
        return {resolvedData, subscriptions}
    }
}

export default UtilCms<|endoftext|>


<commit_before>var chai = require('chai');
var gravatarProvider = require('../../src/gravatarProvider');

chai.should();

describe('gravatarProvider', function() {
    var providerInstance;

    beforeEach(function() {
        providerInstance = new gravatarProvider();
    });

    describe('$get', function() {
        it('should exist', function() {
            providerInstance.should.respondTo('$get');
        });

        it('should return a gravatar service', function() {
            var gravatarSvc = providerInstance.$get();

            gravatarSvc.should.be.an('object');
            gravatarSvc.should.respondTo('shouldBindOnce');
            gravatarSvc.should.respondTo('generateUrl');
        });
    });

    describe('bindOnce', function() {
        it('should exist', function() {
            providerInstance.should.respondTo('bindOnce');
        });

        it('should default to binding once', function() {
            var gravatarSvc = providerInstance.$get();

            gravatarSvc.shouldBindOnce().should.be.true;
        });

        it('should be able to disable binding once', function() {
            var gravatarSvc;

            providerInstance.bindOnce(false);
            gravatarSvc = providerInstance.$get();

            gravatarSvc.shouldBindOnce().should.be.false;
        });
    });
});
<commit_msg>Add unit tests for gravatarProvider<commit_after>var chai = require('chai');
var gravatarProvider = require('../../src/gravatarProvider');

chai.should();

describe('gravatarProvider', function() {
    var providerInstance;

    beforeEach(function() {
        providerInstance = new gravatarProvider();
    });

    describe('$get', function() {
        it('should exist', function() {
            providerInstance.should.respondTo('$get');
        });

        it('should return a gravatar service', function() {
            var gravatarSvc = providerInstance.$get();

            gravatarSvc.should.be.an('object');
            gravatarSvc.should.respondTo('shouldBindOnce');
            gravatarSvc.should.respondTo('generateUrl');
        });
    });

    describe('bindOnce', function() {
        it('should exist', function() {
            providerInstance.should.respondTo('bindOnce');
        });

        it('should default to binding once', function() {
            var gravatarSvc = providerInstance.$get();

            gravatarSvc.shouldBindOnce().should.be.true;
        });

        it('should be able to disable binding once', function() {
            var gravatarSvc;

            providerInstance.bindOnce(false);
            gravatarSvc = providerInstance.$get();

            gravatarSvc.shouldBindOnce().should.be.false;
        });
    });

    describe('configure', function() {
        it('should exist', function() {
            providerInstance.should.respondTo('configure');
        });
    });

    describe('gravatar', function() {
        var gravatarSvc;

        beforeEach(function() {
            gravatarSvc = providerInstance.$get();
        });

        describe('generateUrl', function() {
            it('should generate a minimal url', function() {
                var testHash = '01234';
                var urlPattern = new RegExp('^//www.gravatar.com/avatar/' + testHash + '$');
                var url;

                url = gravatarSvc.generateUrl({ hash: testHash });

                url.should.match(urlPattern);
            });

            xit('should support size', function() {

            });

            xit('should support a default image', function() {

            });

            xit('should support forcing default image', function() {

            });

            xit('should support rating filter', function() {

            });
        });
    });
});
<|endoftext|>


<commit_before>const Wreck = require('wreck');
const Schema = require('./schema.js');
const ResponseMessages = require('../responseMessages');

const config = require('config');
// const authors = config.get('data.authors');
// const keywords = config.get('data.keywords');
// const taxa = config.get('data.taxa');
// const families = config.get('data.families');

const Database = require('better-sqlite3');
const db = new Database(config.get('data.facets'));

// const data = {
//     authors: require(authors),
//     keywords: require(keywords),
//     taxa: require(taxa),
//     families: require(families)
// };

const facets = {
    authors: 'author',
    families: 'family',
    keywords: 'keyword',
    taxa: 'taxon'
};

module.exports = {
    // findjs: function(pattern, source) {
    //     const re = new RegExp(`^${pattern}`, 'i');
    //     const res = data[source].filter(function(element) {
    //         return (element.search(re) > -1)
    //     });

    //     return(res);
    // },

    find: function(pattern, source) {
        console.log(`looking for ${pattern} in ${source}`)
        return db.prepare(`SELECT ${facets[source]} FROM ${source} WHERE ${facets[source]} LIKE ?`)
            .raw()
            .all(`%${pattern}%`)
            .map(r => r[0]);
    },

    getImages: async function(treatmentId) {

        const _getRecords = async (query) => {

            let recordId = '';
            if (query.indexOf('id=') > -1) {
                recordId = query.substr(3)
            }

            let Zenodo = 'https://zenodo.org/api/records/';

            // ignore all other query params if id is present
            if (recordId) {
                Zenodo = Zenodo + recordId;

                try {
                    console.log('querying ' + Zenodo);
                    const {res, payload} =  await Wreck.get(Zenodo);
                    return await JSON.parse(payload);
                }
                catch(err) {
                    console.error(err);
                }
            }
            else {
                Zenodo = Zenodo + '?' + query;

                try {
                    console.log('querying ' + Zenodo);
                    const {res, payload} =  await Wreck.get(Zenodo);
                    const result = await JSON.parse(payload);
                    const total = result.hits.total;

                    if (total) {

                        console.log(`found ${total} open records… now getting their images`);
                        console.log(`number of hits: ${result.hits.hits.length}`);

                        let imagesOfRecords = {};
                        await Promise.all(result.hits.hits.map(async (record) => {

                            const bucket = await _getBuckets(record.links.self);
                            if (bucket) {
                                const contents = await _getImageFiles(bucket);

                                imagesOfRecords[record.links.self] = {
                                    title: record.metadata.title,
                                    creators: record.metadata.creators,
                                    images: contents.map(function(el) {
                                        return el.links.self;
                                    }),
                                    thumb250: record.links.thumb250 ? record.links.thumb250 : 'na'
                                };
                            }

                        }));

                        return {"total": total, "images": imagesOfRecords};
                    }
                    else {
                        console.log('nothing found');
                        return {"total": 0, "images": {}};
                        //return Utils.errorMsg;
                    }
                }
                catch(err) {
                    console.error(err);
                }
            }

        };

        const _getImageFiles = async function(uri) {

            if (uri) {
                const { res, payload } = await Wreck.get(uri);
                const contents = JSON.parse(payload.toString()).contents;
                return contents;
            }

        };

        const _getBuckets = async function(uri) {

            const { res, payload } = await Wreck.get(uri);
            const bucket = JSON.parse(payload.toString()).links.bucket;
            return bucket;
        };

        return await _getRecords(`access_right=open&page=1&q=${treatmentId}&size=30&type=image`);
    }

}<commit_msg>fixed Utils.find<commit_after>const Wreck = require('wreck');
const Schema = require('./schema.js');
const ResponseMessages = require('../responseMessages');

const config = require('config');
// const authors = config.get('data.authors');
// const keywords = config.get('data.keywords');
// const taxa = config.get('data.taxa');
// const families = config.get('data.families');

const Database = require('better-sqlite3');
const db = new Database(config.get('data.facets'));

// const data = {
//     authors: require(authors),
//     keywords: require(keywords),
//     taxa: require(taxa),
//     families: require(families)
// };

const facets = {
    authors: 'author',
    families: 'family',
    keywords: 'keyword',
    taxa: 'taxon'
};

module.exports = {
    // findjs: function(pattern, source) {
    //     const re = new RegExp(`^${pattern}`, 'i');
    //     const res = data[source].filter(function(element) {
    //         return (element.search(re) > -1)
    //     });

    //     return(res);
    // },

    find: function(pattern, source) {
        //console.log(`looking for ${pattern} in ${source}`)
        return db.prepare(`SELECT ${facets[source]} FROM ${source} WHERE ${facets[source]} LIKE ?`)
            .raw()
            .all(`%${pattern}%`)
            .map(r => r[0]);
    },

    getImages: async function(treatmentId) {

        const _getRecords = async (query) => {

            let recordId = '';
            if (query.indexOf('id=') > -1) {
                recordId = query.substr(3)
            }

            let Zenodo = 'https://zenodo.org/api/records/';

            // ignore all other query params if id is present
            if (recordId) {
                Zenodo = Zenodo + recordId;

                try {
                    console.log('querying ' + Zenodo);
                    const {res, payload} =  await Wreck.get(Zenodo);
                    return await JSON.parse(payload);
                }
                catch(err) {
                    console.error(err);
                }
            }
            else {
                Zenodo = Zenodo + '?' + query;

                try {
                    console.log('querying ' + Zenodo);
                    const {res, payload} =  await Wreck.get(Zenodo);
                    const result = await JSON.parse(payload);
                    const total = result.hits.total;

                    if (total) {

                        console.log(`found ${total} open records… now getting their images`);
                        console.log(`number of hits: ${result.hits.hits.length}`);

                        let imagesOfRecords = {};
                        await Promise.all(result.hits.hits.map(async (record) => {

                            const bucket = await _getBuckets(record.links.self);
                            if (bucket) {
                                const contents = await _getImageFiles(bucket);

                                imagesOfRecords[record.links.self] = {
                                    title: record.metadata.title,
                                    creators: record.metadata.creators,
                                    images: contents.map(function(el) {
                                        return el.links.self;
                                    }),
                                    thumb250: record.links.thumb250 ? record.links.thumb250 : 'na'
                                };
                            }

                        }));

                        return {"total": total, "images": imagesOfRecords};
                    }
                    else {
                        console.log('nothing found');
                        return {"total": 0, "images": {}};
                        //return Utils.errorMsg;
                    }
                }
                catch(err) {
                    console.error(err);
                }
            }

        };

        const _getImageFiles = async function(uri) {

            if (uri) {
                const { res, payload } = await Wreck.get(uri);
                const contents = JSON.parse(payload.toString()).contents;
                return contents;
            }

        };

        const _getBuckets = async function(uri) {

            const { res, payload } = await Wreck.get(uri);
            const bucket = JSON.parse(payload.toString()).links.bucket;
            return bucket;
        };

        return await _getRecords(`access_right=open&page=1&q=${treatmentId}&size=30&type=image`);
    }

}<|endoftext|>


<commit_before>// vim: set ts=2 sw=2 et
// Credit for the solution goes to http://stackoverflow.com/a/11694229

var target = document.querySelector('head > title');

var observer = new window.WebKitMutationObserver(
  function(mutations) {
    mutations.forEach(
      function(mutation){
        notify(mutation.target.textContent, true);
      }
    );
  }
);

observer.observe(target, { subtree: true, characterData: true, childList: true });

notify(target.textContent, false);

function notify(title, changed) {
  var count = -1;

  var match = /^\((\d+)\)/.exec(title);
  var match_zero = /^The Old Reader$/.exec(title); // Does not fire on navigation

  if (match && match[1]) {
    count = match[1];
  } else if (match_zero && changed) {
    count = 0;
  }

  if (count >= 0) {
    try {
      chrome.runtime.sendMessage({'count' : count});
      console.log("Observer reported "+count+" to extension");
    } catch(e) { // Happens when parent extension is no longer available or was reloaded
      console.warn("Could not communicate with parent extension, deregistering observer");
      observer.disconnect();
    }
  }
}
<commit_msg>Remove webkit prefix from mutation observer<commit_after>// vim: set ts=2 sw=2 et
// Credit for the solution goes to http://stackoverflow.com/a/11694229

var target = document.querySelector('head > title');

var observer = new window.MutationObserver(
  function(mutations) {
    mutations.forEach(
      function(mutation){
        notify(mutation.target.textContent, true);
      }
    );
  }
);

observer.observe(target, { subtree: true, characterData: true, childList: true });

notify(target.textContent, false);

function notify(title, changed) {
  var count = -1;

  var match = /^\((\d+)\)/.exec(title);
  var match_zero = /^The Old Reader$/.exec(title); // Does not fire on navigation

  if (match && match[1]) {
    count = match[1];
  } else if (match_zero && changed) {
    count = 0;
  }

  if (count >= 0) {
    try {
      chrome.runtime.sendMessage({'count' : count});
      console.log("Observer reported "+count+" to extension");
    } catch(e) { // Happens when parent extension is no longer available or was reloaded
      console.warn("Could not communicate with parent extension, deregistering observer");
      observer.disconnect();
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

angular.module('eKlub.members', ['ngRoute', 'eKlub.groups'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/members', {
    templateUrl: 'members/members.html',
    controller: 'MembersController'
  });
}])

.factory('membersFactory', function($http) {

	var getAllMembersUrl = "http://localhost:8080/members";
	var getMembersUrl = "http://localhost:8080/members/search";
	var getMemberByIdUrl = "";
	var deleteMemberUrl = "";
	var saveMemberUrl = "http://localhost:8080/members";

	var membersFactory = {};

	membersFactory.getAllMembers = function () {
		return $http.get(getAllMembersUrl);
	};

	membersFactory.getMembers = function(searchCriteria) {
		var data = {
				"nameSurname": searchCriteria,
				"idCard": searchCriteria,
				"address": searchCriteria,
				"email": searchCriteria,
				"phone": searchCriteria
		};
		return $http({
			url: getMembersUrl,
		 	method: 'POST',
		 	data: data
		});
	}

	membersFactory.saveMember = function(member) {
		return $http({
			url: saveMemberUrl,
			method: 'POST',
			data: member
		});
	}

	return membersFactory;
})

.controller('MembersController', function($scope, membersFactory, groupsFactory) {

	var table;

	init();

	$scope.reset = function() {
		membersFactory.getAllMembers()
		.then(function(response) {
			$('#members_table_processing').show();
			table.clear().draw();
			table.rows.add(response.data.payload);
   			table.columns.adjust().draw();
		}, function(error) {
			alert("Error: " + error);
		}).finally(function (response) {
			$scope.searchCriteria = "";
			$('#members_table_processing').hide();
		});
	}

	$scope.searchMembers = function() {
		var searchCriteria = $scope.searchCriteria;
		membersFactory.getMembers(searchCriteria)
		.then(function(response) {
			$('#members_table_processing').show();
			table.clear().draw();
			table.rows.add(response.data.payload);
   			table.columns.adjust().draw();
		}, function(error) {
			alert("Error: " + JSON.stringify(error));
		}).finally(function (response) {
			$('#members_table_processing').hide();
		});
	}

	$scope.getAllGroups = function() {
		groupsFactory.getAllGroups()
		.then(function(response) {
			$scope.groups = response.data.payload;
			$scope.memberDialog = { newMember: {}};
			$scope.memberDialog.newMember.group = response.data.payload[0];
			$scope.memberDialog.newMember.gender = 'M';
		}, function(error) {
			alert("Error: " + JSON.stringify(error));
		}).finally(function (response) {

		});
	}

	$scope.saveMember = function() {
		if ($scope.newMemberForm.$valid) {
			// alert(JSON.stringify($scope.memberDialog.newMember));
			// alert($scope.memberDialog.newMember);
			var member = JSON.stringify($scope.memberDialog.newMember);
			console.log(JSON.stringify(member));
			membersFactory.saveMember(member)
			.then(function(response) {
				alert(JSON.stringify(response.data.payload));
				// exit dialog
			}, function(error) {
				alert("Error: " + JSON.stringify(error));
			}).finally(function(response) {
				// refresh table
			});
		} else {
			alert("Morate popuniti sva obavezna polja.");
		}
	}

	$scope.resetMemberDialog = function() {
		$scope.memberDialog.newMember = {};
		$scope.newMemberForm.$setPristine();
		$scope.newMemberForm.$setUntouched();
	}

	function init() {
		membersFactory.getAllMembers()
		.then(function(response) {
			$('#members_table_processing').show();
			initializeMembersDataTable(response.data.payload);
		}, function(error) {
			alert("Error: " + error)
		}).finally(function (response) {
			$('#members_table_processing').hide();
		});

	};

	function initializeMembersDataTable(members) {
		table = $('#members_table').DataTable({
			data: members,
			processing: true,
			filter: false,
			autoWidth: false,
			columns: [
            { "data": "id" },
            { "data": "idCard" },
            { "data": "nameSurname" },
            { "data": "gender" },
            { "data": "email" },
            { "data": "address" },
            { "data": "phone" },
            { "data": "dateOfBirth" },
            { "data": "dateOfMembership" },
            { "data": "group.name", "defaultContent": "" },
            { "data": "group.id", "defaultContent": "" }],

            language: {
			    "sProcessing":   "Procesiranje u toku...",
			    "sLengthMenu":   "Prikaži _MENU_ elemenata",
			    "sZeroRecords":  "Nije pronađen nijedan rezultat",
			    "sInfo":         "Prikaz _START_ do _END_ od ukupno _TOTAL_ elemenata",
			    "sInfoEmpty":    "Prikaz 0 do 0 od ukupno 0 elemenata",
			    "sInfoFiltered": "(filtrirano od ukupno _MAX_ elemenata)",
			    "sInfoPostFix":  "",
			    "sSearch":       "Pretraga:",
			    "sUrl":          "",
			    "oPaginate": {
			        "sFirst":    "Početna",
			        "sPrevious": "Prethodna",
			        "sNext":     "Sledeća",
			        "sLast":     "Poslednja"
			    }
			}
		});
	}

});<commit_msg>Added buttons to table<commit_after>'use strict';

angular.module('eKlub.members', ['ngRoute', 'eKlub.groups'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/members', {
    templateUrl: 'members/members.html',
    controller: 'MembersController'
  });
}])

.factory('membersFactory', function($http) {

	var getAllMembersUrl = "http://localhost:8080/members";
	var getMembersUrl = "http://localhost:8080/members/search";
	var getMemberByIdUrl = "";
	var deleteMemberUrl = "";
	var saveMemberUrl = "http://localhost:8080/members";

	var membersFactory = {};

	membersFactory.getAllMembers = function () {
		return $http.get(getAllMembersUrl);
	};

	membersFactory.getMembers = function(searchCriteria) {
		var data = {
				"nameSurname": searchCriteria,
				"idCard": searchCriteria,
				"address": searchCriteria,
				"email": searchCriteria,
				"phone": searchCriteria
		};
		return $http({
			url: getMembersUrl,
		 	method: 'POST',
		 	data: data
		});
	}

	membersFactory.saveMember = function(member) {
		return $http({
			url: saveMemberUrl,
			method: 'POST',
			data: member
		});
	}

	return membersFactory;
})

.controller('MembersController', function($scope, membersFactory, groupsFactory) {

	var table;

	init();

	$scope.reset = function() {
		membersFactory.getAllMembers()
		.then(function(response) {
			$('#members_table_processing').show();
			table.clear().draw();
			table.rows.add(response.data.payload);
   			table.columns.adjust().draw();
		}, function(error) {
			alert("Error: " + error);
		}).finally(function (response) {
			$scope.searchCriteria = "";
			$('#members_table_processing').hide();
		});
	}

	$scope.searchMembers = function() {
		var searchCriteria = $scope.searchCriteria;
		membersFactory.getMembers(searchCriteria)
		.then(function(response) {
			$('#members_table_processing').show();
			table.clear().draw();
			table.rows.add(response.data.payload);
   			table.columns.adjust().draw();
		}, function(error) {
			alert("Error: " + JSON.stringify(error));
		}).finally(function (response) {
			$('#members_table_processing').hide();
		});
	}

	$scope.getAllGroups = function() {
		groupsFactory.getAllGroups()
		.then(function(response) {
			$scope.groups = response.data.payload;
			$scope.memberDialog = { newMember: {}};
			$scope.memberDialog.newMember.group = response.data.payload[0];
			$scope.memberDialog.newMember.gender = 'M';
		}, function(error) {
			alert("Error: " + JSON.stringify(error));
		}).finally(function (response) {

		});
	}

	$scope.saveMember = function() {
		if ($scope.newMemberForm.$valid) {
			// alert(JSON.stringify($scope.memberDialog.newMember));
			// alert($scope.memberDialog.newMember);
			var member = JSON.stringify($scope.memberDialog.newMember);
			console.log(JSON.stringify(member));
			membersFactory.saveMember(member)
			.then(function(response) {
				alert(JSON.stringify(response.data.payload));
				// exit dialog
			}, function(error) {
				alert("Error: " + JSON.stringify(error));
			}).finally(function(response) {
				// refresh table
			});
		} else {
			alert("Morate popuniti sva obavezna polja.");
		}
	}

	$scope.resetMemberDialog = function() {
		$scope.memberDialog.newMember = {};
		$scope.newMemberForm.$setPristine();
		$scope.newMemberForm.$setUntouched();
	}

	function init() {
		membersFactory.getAllMembers()
		.then(function(response) {
			$('#members_table_processing').show();
			initializeMembersDataTable(response.data.payload);
		}, function(error) {
			alert("Error: " + error)
		}).finally(function (response) {
			$('#members_table_processing').hide();
		});

	};

	function initializeMembersDataTable(members) {
		table = $('#members_table').DataTable({
			data: members,
			processing: true,
			filter: false,
			autoWidth: false,
			columns: [
            { "data": "id" },
            { "data": "idCard" },
            { "data": "nameSurname" },
            { "data": "gender" },
            { "data": "email" },
            { "data": "address" },
            { "data": "phone" },
            { "data": "dateOfBirth" },
            { "data": "dateOfMembership" },
            { "data": "group.name", "defaultContent": "" },
            { "data": null, "render":function(data, type, row) {
            							return '<button class="btn btn-default" onclick="alert(' + data.id + ');" style="margin-right: 5%;"></button>'
            									+ '<button class="btn btn-default" onclick="alert(' + data.id + ');"></button>';
            						}}],

            language: {
			    "sProcessing":   "Procesiranje u toku...",
			    "sLengthMenu":   "Prikaži _MENU_ elemenata",
			    "sZeroRecords":  "Nije pronađen nijedan rezultat",
			    "sInfo":         "Prikaz _START_ do _END_ od ukupno _TOTAL_ elemenata",
			    "sInfoEmpty":    "Prikaz 0 do 0 od ukupno 0 elemenata",
			    "sInfoFiltered": "(filtrirano od ukupno _MAX_ elemenata)",
			    "sInfoPostFix":  "",
			    "sSearch":       "Pretraga:",
			    "sUrl":          "",
			    "oPaginate": {
			        "sFirst":    "Početna",
			        "sPrevious": "Prethodna",
			        "sNext":     "Sledeća",
			        "sLast":     "Poslednja"
			    }
			}
		});
	}

});<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash')
  , raf = require('raf/polyfill')
  , Ribcage
  , extendView;

Ribcage = {
  initialize: function (opts){

    opts = opts || {};

    // if we need to load data before rendering, do it
    if (typeof this.loadData === 'function') {
      this._dataLoaded = false
    }
    // otherwise, assume we have the data already
    else {
      this._dataLoaded = true
    }

    this._nukeOnRender = opts._nukeOnRender
    // delete would turn this into a "slow object" in V8, so just set to null
    opts._nukeOnRender = null

    // backbone 1.1+ doesn't auto-set this.options
    this.options = opts

    if(this.beforeInit) {
      this.beforeInit(opts);
    }

    this.template = this.template || opts.template;

    if (!this.template) {
      this.template = function () { return ''; };
    }

    if (this.afterInit) {
      this.afterInit(opts);
    }

    this.render();
  }

, context: function () {
    return _.extend({}, this.options, this.model);
  }

, render: function () {
    var self = this
      , model = this.model;

    if(typeof this.bindEvents === 'function') {
      this.bindEvents()
    }

    if (!this._dataLoaded && typeof this.loadData === 'function') {
      return this.loadData(function () {
        self._dataLoaded = true
        self.render()
      })
    }

    // closing destroys the el, might need to recreate it
    if (!this.el) {
      this._ensureElement()
    }

    if (this.beforeRender) {
      this.beforeRender();
    }

    // Maintainers of legacy code might want to use this
    // but certainly not YOU, young grasshopper!
    if (this._nukeOnRender) {
      this.closeSubviews();
      this.$el.empty();
    }

    if (this.beforeTemplating) {
      model = this.beforeTemplating();
    }

    if (this.context) {
      model = this.context();
    }

    this.$el.html(this.template(model));

    // As soon as the view is bound to DOM, we need to delegate events and re-render all
    // subviews to keep events intact. -cstumph
    this.delegateEvents()
    this.eachSubview( function eachSubviewInRender (view){
      view.render()
    })

    // I'm not happy deferring here, but backbone's
    // event system is based on the DOM, so my hands
    // are tied. - Daniel
    _.defer(function () {
      self.trigger('afterRender')
    });

    if (this.afterRender) {
      this.afterRender();
    }

    return this;
  }

, proxy: function (name, view){
    this.listenTo(view, name, function proxiedEventCallback(){
      var args = Array.prototype.slice.call(arguments, 0);
      args.splice(0, 0, name);
      this.trigger.apply(this, args);
    })
  }

, eachSubview: function (iterator, context){
    _.each(this.subviews, iterator, context || this);
  }

, _attachSubView: function (view){
    this.subviews = this.subviews || {};
    this.subviewByModelId = this.subviewByModelId || {};

    this.subviews[view.cid] = view;
    if (view.model) {
      if (!this.subviewByModelId[view.model.id]) this.subviewByModelId[view.model.id] = [];
      this.subviewByModelId[view.model.id].push(view);
    }

    return view;
  }

, appendSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.render) view.render()
    el.append(view.el);

    _.defer(function (){
      view.trigger('afterAppend', view);
    });

    if (view.afterAppend) {
      view.afterAppend();
    }
  }

, prependSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.renderOnAppend) view.render()
    el.prepend(view.el);

    _.defer(function () {
      view.trigger('afterPrepend', view);
    });

    if (view.afterPrepend) {
      view.afterPrepend();
    }
  }

, appendSubviews: function (views, el, callback){
    el || (el = this.$el);

    var fragment = document.createDocumentFragment();

    _.each(views, function(view){
      this._attachSubView(view);

      if (view.options.renderOnAppend) view.render()
      fragment.appendChild(view.el);

      _.defer(function () {
        view.trigger('afterAppend', view);
      });

      if (view.afterAppend) {
        view.afterAppend();
      }
    }, this);

    raf.call(window, function(){
      el[0].appendChild(fragment);
      if (_.isFunction(callback)) callback(views);
    });
  }

, batchAppendSubviews: function (views, el, batchCount, batchCallback, callback){
    var self = this
      , batches = _(views)
                  .groupBy( function (view, index){
                    return Math.floor(index / batchCount);
                  })
                  .toArray()
                  .valueOf();

    function appendNextBatch () {
      // with no batches left, just return early
      if(!batches.length) {
        // if we have a final callback, call that.
        return typeof callback === 'function'
          ? void callback()
          : void 0
      }

      var viewBatch = batches.shift();

      self.appendSubviews(viewBatch, el, function (){
        if (typeof batchCallback === 'function') {
          batchCallback(viewBatch);
        }

        appendNextBatch();
      });
    }

    appendNextBatch();
  }

, close: function close(options, callback) {
    options || (options = {})

    if (this.beforeClose) {
      this.beforeClose();
    }

    this.closeSubviews({keepDom: true}, _.bind(function onSubviewsClosed(){
      this.stopListening();

      // when the subviews are closed, close the parent
      // keep the DOM elements, b/c it's save to assume that all child DOM
      // is containted by the parent DOM container. This allows us to reduce
      // many DOM removal calls to just one.
      if (!options.keepDom) {
        raf.call(window, function closeRequestAnimationFrame(){
          // remove the from the DOM
          if (this.$el) this.$el.remove()
          // destroy our reference to the DOM node
          this.$el = null
          this.el = null
          if (_.isFunction(callback)) callback()
        }.bind(this))
      }
      else if (_.isFunction(callback)) callback()
    }, this));
  }

, closeSubviews: function (options, callback){
    var subviewCount = _.size(this.subviews)
      , done

    if (_.isFunction(callback)){
      // if there are no subviews to close, just call the callback
      if (!subviewCount) return void callback()
      else done = _.after(subviewCount, callback)
    }

    this.eachSubview( function (subview){
      subview.close(options, done);
    });

    // empty out our lists of subviews
    this.subviews = {}
    this.subviewByModelId = {}
  }

, detachSubview: function detachSubview(view){
    var msg

    if (this.subviews) {

      if (view && !this.subviews[view.cid]) {
        msg = 'View not found in ' + this.className + '\'s subviews: ' + view.className

        if(view.model)
          msg += '\n\n'+ view.model.toJSON()

        throw new Error(msg)
      }

      // delete would turn this into a "slow object" in V8, so just set to null
      this.subviews[view.cid] = null
    }

    view.$el.detach();
  }

, _removeSubviewByModel: function _removeSubviewByModel(model, method){
    var id = model.id
    if (this.subviewByModelId){
      if (!id || !this.subviewByModelId[id]){
        throw new Error('No views with ' + id + ' model id found in ' + this.className + ' subviews');
      }

      _.each(this.subviewByModelId[id], function(view){
        method.call(this, view);
      }, this);

      // delete would turn this into a "slow object" in V8, so just set to null
      this.subviewByModelId[id] = null
    }

  }

, closeSubviewsByModel: function closeSubviewsByModel(model){
    this._removeSubviewByModel(model, this.close)
  }

, detachSubviewByModel: function detachSubviewByModel(model){
    this._removeSubviewByModel(model, this.detachSubview)
  }

};//end Ribcage{}

extendView = function (view){
  return view.extend(Ribcage);
};

module.exports = extendView;
<commit_msg>Fix breakage in subclassed views using states rejecting extra properties<commit_after>'use strict';

var _ = require('lodash')
  , raf = require('raf/polyfill')
  , Ribcage
  , extendView;

Ribcage = {
  initialize: function (opts){

    opts = opts || {};

    // if we need to load data before rendering, do it
    if (typeof this.loadData === 'function') {
      this._dataLoaded = false
    }
    // otherwise, assume we have the data already
    else {
      this._dataLoaded = true
    }

    this._nukeOnRender = opts._nukeOnRender
    // delete would turn this into a "slow object" in V8
    // however, leaving setting it to null makes subclassed states
    // throw if extraProperties is set to deny
    delete opts._nukeOnRender

    // backbone 1.1+ doesn't auto-set this.options
    this.options = opts

    if(this.beforeInit) {
      this.beforeInit(opts);
    }

    this.template = this.template || opts.template;

    if (!this.template) {
      this.template = function () { return ''; };
    }

    if (this.afterInit) {
      this.afterInit(opts);
    }

    this.render();
  }

, context: function () {
    return _.extend({}, this.options, this.model);
  }

, render: function () {
    var self = this
      , model = this.model;

    if(typeof this.bindEvents === 'function') {
      this.bindEvents()
    }

    if (!this._dataLoaded && typeof this.loadData === 'function') {
      return this.loadData(function () {
        self._dataLoaded = true
        self.render()
      })
    }

    // closing destroys the el, might need to recreate it
    if (!this.el) {
      this._ensureElement()
    }

    if (this.beforeRender) {
      this.beforeRender();
    }

    // Maintainers of legacy code might want to use this
    // but certainly not YOU, young grasshopper!
    if (this._nukeOnRender) {
      this.closeSubviews();
      this.$el.empty();
    }

    if (this.beforeTemplating) {
      model = this.beforeTemplating();
    }

    if (this.context) {
      model = this.context();
    }

    this.$el.html(this.template(model));

    // As soon as the view is bound to DOM, we need to delegate events and re-render all
    // subviews to keep events intact. -cstumph
    this.delegateEvents()
    this.eachSubview( function eachSubviewInRender (view){
      view.render()
    })

    // I'm not happy deferring here, but backbone's
    // event system is based on the DOM, so my hands
    // are tied. - Daniel
    _.defer(function () {
      self.trigger('afterRender')
    });

    if (this.afterRender) {
      this.afterRender();
    }

    return this;
  }

, proxy: function (name, view){
    this.listenTo(view, name, function proxiedEventCallback(){
      var args = Array.prototype.slice.call(arguments, 0);
      args.splice(0, 0, name);
      this.trigger.apply(this, args);
    })
  }

, eachSubview: function (iterator, context){
    _.each(this.subviews, iterator, context || this);
  }

, _attachSubView: function (view){
    this.subviews = this.subviews || {};
    this.subviewByModelId = this.subviewByModelId || {};

    this.subviews[view.cid] = view;
    if (view.model) {
      if (!this.subviewByModelId[view.model.id]) this.subviewByModelId[view.model.id] = [];
      this.subviewByModelId[view.model.id].push(view);
    }

    return view;
  }

, appendSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.render) view.render()
    el.append(view.el);

    _.defer(function (){
      view.trigger('afterAppend', view);
    });

    if (view.afterAppend) {
      view.afterAppend();
    }
  }

, prependSubview: function (view, el){
    el || (el = this.$el);

    this._attachSubView(view);

    if (view.options.renderOnAppend) view.render()
    el.prepend(view.el);

    _.defer(function () {
      view.trigger('afterPrepend', view);
    });

    if (view.afterPrepend) {
      view.afterPrepend();
    }
  }

, appendSubviews: function (views, el, callback){
    el || (el = this.$el);

    var fragment = document.createDocumentFragment();

    _.each(views, function(view){
      this._attachSubView(view);

      if (view.options.renderOnAppend) view.render()
      fragment.appendChild(view.el);

      _.defer(function () {
        view.trigger('afterAppend', view);
      });

      if (view.afterAppend) {
        view.afterAppend();
      }
    }, this);

    raf.call(window, function(){
      el[0].appendChild(fragment);
      if (_.isFunction(callback)) callback(views);
    });
  }

, batchAppendSubviews: function (views, el, batchCount, batchCallback, callback){
    var self = this
      , batches = _(views)
                  .groupBy( function (view, index){
                    return Math.floor(index / batchCount);
                  })
                  .toArray()
                  .valueOf();

    function appendNextBatch () {
      // with no batches left, just return early
      if(!batches.length) {
        // if we have a final callback, call that.
        return typeof callback === 'function'
          ? void callback()
          : void 0
      }

      var viewBatch = batches.shift();

      self.appendSubviews(viewBatch, el, function (){
        if (typeof batchCallback === 'function') {
          batchCallback(viewBatch);
        }

        appendNextBatch();
      });
    }

    appendNextBatch();
  }

, close: function close(options, callback) {
    options || (options = {})

    if (this.beforeClose) {
      this.beforeClose();
    }

    this.closeSubviews({keepDom: true}, _.bind(function onSubviewsClosed(){
      this.stopListening();

      // when the subviews are closed, close the parent
      // keep the DOM elements, b/c it's save to assume that all child DOM
      // is containted by the parent DOM container. This allows us to reduce
      // many DOM removal calls to just one.
      if (!options.keepDom) {
        raf.call(window, function closeRequestAnimationFrame(){
          // remove the from the DOM
          if (this.$el) this.$el.remove()
          // destroy our reference to the DOM node
          this.$el = null
          this.el = null
          if (_.isFunction(callback)) callback()
        }.bind(this))
      }
      else if (_.isFunction(callback)) callback()
    }, this));
  }

, closeSubviews: function (options, callback){
    var subviewCount = _.size(this.subviews)
      , done

    if (_.isFunction(callback)){
      // if there are no subviews to close, just call the callback
      if (!subviewCount) return void callback()
      else done = _.after(subviewCount, callback)
    }

    this.eachSubview( function (subview){
      subview.close(options, done);
    });

    // empty out our lists of subviews
    this.subviews = {}
    this.subviewByModelId = {}
  }

, detachSubview: function detachSubview(view){
    var msg

    if (this.subviews) {

      if (view && !this.subviews[view.cid]) {
        msg = 'View not found in ' + this.className + '\'s subviews: ' + view.className

        if(view.model)
          msg += '\n\n'+ view.model.toJSON()

        throw new Error(msg)
      }

      // delete would turn this into a "slow object" in V8, so just set to null
      this.subviews[view.cid] = null
    }

    view.$el.detach();
  }

, _removeSubviewByModel: function _removeSubviewByModel(model, method){
    var id = model.id
    if (this.subviewByModelId){
      if (!id || !this.subviewByModelId[id]){
        throw new Error('No views with ' + id + ' model id found in ' + this.className + ' subviews');
      }

      _.each(this.subviewByModelId[id], function(view){
        method.call(this, view);
      }, this);

      // delete would turn this into a "slow object" in V8, so just set to null
      this.subviewByModelId[id] = null
    }

  }

, closeSubviewsByModel: function closeSubviewsByModel(model){
    this._removeSubviewByModel(model, this.close)
  }

, detachSubviewByModel: function detachSubviewByModel(model){
    this._removeSubviewByModel(model, this.detachSubview)
  }

};//end Ribcage{}

extendView = function (view){
  return view.extend(Ribcage);
};

module.exports = extendView;
<|endoftext|>


<commit_before>/* eslint-env jasmine */
var nock = require('nock')
var VoucherifyClient = require('./client-loader')
var fixtures = require('./fixtures')
var reqWithoutBody = fixtures.reqWithoutBody
var reqWithBody = fixtures.reqWithBody
nock.disableNetConnect()

describe('Validation Rules API', function () {
    var client = new VoucherifyClient({
        applicationId: 'node-sdk-test-id',
        clientSecretKey: 'node-sdk-test-secret'
    })

    it('should create validation rule', function (done) {
        var server = nock('https://api.voucherify.io', reqWithBody)
            .post('/v1/validation-rules', {
                voucher_code: 'test-code',
                redemptions: {
                    junction: 'AND',
                    count_per_customer: {
                        conditions: {
                            $is: [
                                1
                            ]
                        }
                    }
                }
            })
            .reply(200, {})

        client.validationRules.create({
                voucher_code: 'test-code',
                redemptions: {
                    junction: 'AND',
                    count_per_customer: {
                        conditions: {
                            $is: [
                                1
                            ]
                        }
                    }
                }
            })
            .then(function () {
                server.done()
                done()
            })
    })

    it('should get validation rule', function (done) {
        var server = nock('https://api.voucherify.io', reqWithoutBody)
            .get('/v1/validation-rules/cust_test-id')
            .reply(200, {})

        client.validationRules.get('cust_test-id')
            .then(function () {
                server.done()
                done()
            })
    })

    it('should update validation rule', function (done) {
        var server = nock('https://api.voucherify.io', reqWithBody)
            .put('/v1/validation-rules/cust_test-id', {
                redemptions: {
                    junction: 'AND',
                    count_per_customer: {
                        conditions: {
                            $is: [
                                2
                            ]
                        }
                    }
                }
            })
            .reply(200, {})

        client.validationRules.update({
                id: 'cust_test-id',
                redemptions: {
                    junction: 'AND',
                    count_per_customer: {
                        conditions: {
                            $is: [
                                2
                            ]
                        }
                    }
                }
            })
            .then(function () {
                server.done()
                done()
            })
    })

    it('should delete validation rule', function (done) {
        var server = nock('https://api.voucherify.io', reqWithoutBody)
            .delete('/v1/validation-rules/cust_test-id')
            .reply(200, {})

        client.validationRules.delete('cust_test-id')
            .then(function () {
                server.done()
                done()
            })
    })
})
<commit_msg>standard --fix<commit_after>/* eslint-env jasmine */
var nock = require('nock')
var VoucherifyClient = require('./client-loader')
var fixtures = require('./fixtures')
var reqWithoutBody = fixtures.reqWithoutBody
var reqWithBody = fixtures.reqWithBody
nock.disableNetConnect()

describe('Validation Rules API', function () {
  var client = new VoucherifyClient({
    applicationId: 'node-sdk-test-id',
    clientSecretKey: 'node-sdk-test-secret'
  })

  it('should create validation rule', function (done) {
    var server = nock('https://api.voucherify.io', reqWithBody)
            .post('/v1/validation-rules', {
              voucher_code: 'test-code',
              redemptions: {
                junction: 'AND',
                count_per_customer: {
                  conditions: {
                    $is: [
                      1
                    ]
                  }
                }
              }
            })
            .reply(200, {})

    client.validationRules.create({
      voucher_code: 'test-code',
      redemptions: {
        junction: 'AND',
        count_per_customer: {
          conditions: {
            $is: [
              1
            ]
          }
        }
      }
    })
            .then(function () {
              server.done()
              done()
            })
  })

  it('should get validation rule', function (done) {
    var server = nock('https://api.voucherify.io', reqWithoutBody)
            .get('/v1/validation-rules/cust_test-id')
            .reply(200, {})

    client.validationRules.get('cust_test-id')
            .then(function () {
              server.done()
              done()
            })
  })

  it('should update validation rule', function (done) {
    var server = nock('https://api.voucherify.io', reqWithBody)
            .put('/v1/validation-rules/cust_test-id', {
              redemptions: {
                junction: 'AND',
                count_per_customer: {
                  conditions: {
                    $is: [
                      2
                    ]
                  }
                }
              }
            })
            .reply(200, {})

    client.validationRules.update({
      id: 'cust_test-id',
      redemptions: {
        junction: 'AND',
        count_per_customer: {
          conditions: {
            $is: [
              2
            ]
          }
        }
      }
    })
            .then(function () {
              server.done()
              done()
            })
  })

  it('should delete validation rule', function (done) {
    var server = nock('https://api.voucherify.io', reqWithoutBody)
            .delete('/v1/validation-rules/cust_test-id')
            .reply(200, {})

    client.validationRules.delete('cust_test-id')
            .then(function () {
              server.done()
              done()
            })
  })
})
<|endoftext|>