<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Utility methods.
 * These methods are not specific to Blockly, and could be factored out into
 * a JavaScript framework such as Closure.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.utils');

goog.require('goog.events.BrowserFeature');
goog.require('goog.userAgent');

/**
 * Add a CSS class to a element.
 * Similar to Closure's goog.dom.classes.add, except it handles SVG elements.
 * @param {!Element} element DOM element to add class to.
 * @param {string} className Name of class to add.
 * @private
 */
Blockly.addClass_ = function(element, className) {
  var classes = element.getAttribute('class') || '';
  if ((' ' + classes + ' ').indexOf(' ' + className + ' ') == -1) {
    if (classes) {
      classes += ' ';
    }
    element.setAttribute('class', classes + className);
  }
};

/**
 * Remove a CSS class from a element.
 * Similar to Closure's goog.dom.classes.remove, except it handles SVG elements.
 * @param {!Element} element DOM element to remove class from.
 * @param {string} className Name of class to remove.
 * @private
 */
Blockly.removeClass_ = function(element, className) {
  var classes = element.getAttribute('class');
  if ((' ' + classes + ' ').indexOf(' ' + className + ' ') != -1) {
    var classList = classes.split(/\s+/);
    for (var i = 0; i < classList.length; i++) {
      if (!classList[i] || classList[i] == className) {
        classList.splice(i, 1);
        i--;
      }
    }
    if (classList.length) {
      element.setAttribute('class', classList.join(' '));
    } else {
      element.removeAttribute('class');
    }
  }
};

/**
 * Checks if an element has the specified CSS class.
 * Similar to Closure's goog.dom.classes.has, except it handles SVG elements.
 * @param {!Element} element DOM element to check.
 * @param {string} className Name of class to check.
 * @return {boolean} True if class exists, false otherwise.
 * @private
 */
Blockly.hasClass_ = function(element, className) {
  var classes = element.getAttribute('class');
  return (' ' + classes + ' ').indexOf(' ' + className + ' ') != -1;
};

/**
 * Bind an event to a function call.
 * @param {!Node} node Node upon which to listen.
 * @param {string} name Event name to listen to (e.g. 'mousedown').
 * @param {Object} thisObject The value of 'this' in the function.
 * @param {!Function} func Function to call when event is triggered.
 * @return {!Array.<!Array>} Opaque data that can be passed to unbindEvent_.
 * @private
 */
Blockly.bindEvent_ = function(node, name, thisObject, func) {
  if (thisObject) {
    var wrapFunc = function(e) {
      func.call(thisObject, e);
    };
  } else {
    var wrapFunc = func;
  }
  node.addEventListener(name, wrapFunc, false);
  var bindData = [[node, name, wrapFunc]];
  // Add equivalent touch event.
  if (name in Blockly.bindEvent_.TOUCH_MAP) {
    wrapFunc = function(e) {
      // Punt on multitouch events.
      if (e.changedTouches.length == 1) {
        // Map the touch event's properties to the event.
        var touchPoint = e.changedTouches[0];
        e.clientX = touchPoint.clientX;
        e.clientY = touchPoint.clientY;
      }
      func.call(thisObject, e);
      // Stop the browser from scrolling/zooming the page.
      e.preventDefault();
    };
    for (var i = 0, eventName;
         eventName = Blockly.bindEvent_.TOUCH_MAP[name][i]; i++) {
      node.addEventListener(eventName, wrapFunc, false);
      bindData.push([node, eventName, wrapFunc]);
    }
  }
  return bindData;
};

/**
 * The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
 * in conjunction with mouse events.
 * @type {Object}
 */
Blockly.bindEvent_.TOUCH_MAP = {};
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
  Blockly.bindEvent_.TOUCH_MAP = {
    'mousedown': ['touchstart'],
    'mousemove': ['touchmove'],
    'mouseup': ['touchend', 'touchcancel']
  };
}

/**
 * Unbind one or more events event from a function call.
 * @param {!Array.<!Array>} bindData Opaque data from bindEvent_.  This list is
 *     emptied during the course of calling this function.
 * @return {!Function} The function call.
 * @private
 */
Blockly.unbindEvent_ = function(bindData) {
  while (bindData.length) {
    var bindDatum = bindData.pop();
    var node = bindDatum[0];
    var name = bindDatum[1];
    var func = bindDatum[2];
    node.removeEventListener(name, func, false);
  }
  return func;
};

/**
 * Fire a synthetic event synchronously.
 * @param {!EventTarget} node The event's target node.
 * @param {string} eventName Name of event (e.g. 'click').
 */
Blockly.fireUiEventNow = function(node, eventName) {
  // Remove the event from the anti-duplicate database.
  var list = Blockly.fireUiEvent.DB_[eventName];
  if (list) {
    var i = list.indexOf(node);
    if (i != -1) {
      list.splice(i, 1);
    }
  }
  // Fire the event in a browser-compatible way.
  if (document.createEvent) {
    // W3
    var evt = document.createEvent('UIEvents');
    evt.initEvent(eventName, true, true);  // event type, bubbling, cancelable
    node.dispatchEvent(evt);
  } else if (document.createEventObject) {
    // MSIE
    var evt = document.createEventObject();
    node.fireEvent('on' + eventName, evt);
  } else {
    throw 'FireEvent: No event creation mechanism.';
  }
};

/**
 * Fire a synthetic event asynchronously.  Groups of simultaneous events (e.g.
 * a tree of blocks being deleted) are merged into one event.
 * @param {!EventTarget} node The event's target node.
 * @param {string} eventName Name of event (e.g. 'click').
 */
Blockly.fireUiEvent = function(node, eventName) {
  var list = Blockly.fireUiEvent.DB_[eventName];
  if (list) {
    if (list.indexOf(node) != -1) {
      // This event is already scheduled to fire.
      return;
    }
    list.push(node);
  } else {
    Blockly.fireUiEvent.DB_[eventName] = [node];
  }
  var fire = function() {
    Blockly.fireUiEventNow(node, eventName);
  };
  setTimeout(fire, 0);
};

/**
 * Database of upcoming firing event types.
 * Used to fire only one event after multiple changes.
 * @type {!Object}
 * @private
 */
Blockly.fireUiEvent.DB_ = {};

/**
 * Don't do anything for this event, just halt propagation.
 * @param {!Event} e An event.
 */
Blockly.noEvent = function(e) {
  // This event has been handled.  No need to bubble up to the document.
  e.preventDefault();
  e.stopPropagation();
};

/**
 * Is this event targeting a text input widget?
 * @param {!Event} e An event.
 * @return {boolean} True if text input.
 * @private
 */
Blockly.isTargetInput_ = function(e) {
  return e.target.type == 'textarea' || e.target.type == 'text' ||
         e.target.type == 'number' || e.target.type == 'email' ||
         e.target.type == 'password' || e.target.type == 'search' ||
         e.target.type == 'tel' || e.target.type == 'url';
};

/**
 * Return the coordinates of the top-left corner of this element relative to
 * its parent.  Only for SVG elements and children (e.g. rect, g, path).
 * @param {!Element} element SVG element to find the coordinates of.
 * @return {!Object} Object with .x and .y properties.
 * @private
 */
Blockly.getRelativeXY_ = function(element) {
  var xy = {x: 0, y: 0};
  // First, check for x and y attributes.
  var x = element.getAttribute('x');
  if (x) {
    xy.x = parseInt(x, 10);
  }
  var y = element.getAttribute('y');
  if (y) {
    xy.y = parseInt(y, 10);
  }
  // Second, check for transform="translate(...)" attribute.
  var transform = element.getAttribute('transform');
  // Note that Firefox and IE (9,10) return 'translate(12)' instead of
  // 'translate(12, 0)'.
  // Note that IE (9,10) returns 'translate(16 8)' instead of
  // 'translate(16, 8)'.
  var r = transform &&
          transform.match(/translate\(\s*([-\d.]+)([ ,]\s*([-\d.]+)\s*\))?/);
  if (r) {
    xy.x += parseFloat(r[1]);
    if (r[3]) {
      xy.y += parseFloat(r[3]);
    }
  }
  return xy;
};

/**
 * Return the absolute coordinates of the top-left corner of this element.
 * The origin (0,0) is the top-left corner of the nearest SVG.
 * @param {!Element} element Element to find the coordinates of.
 * @return {!Object} Object with .x and .y properties.
 * @private
 */
Blockly.getSvgXY_ = function(element) {
  var x = 0;
  var y = 0;
  do {
    // Loop through this block and every parent.
    var xy = Blockly.getRelativeXY_(element);
    x += xy.x;
    y += xy.y;
    element = element.parentNode;
  } while (element && element.nodeName.toLowerCase() != 'svg');
  return {x: x, y: y};
};

/**
 * Helper method for creating SVG elements.
 * @param {string} name Element's tag name.
 * @param {!Object} attrs Dictionary of attribute names and values.
 * @param {Element=} opt_parent Optional parent on which to append the element.
 * @return {!SVGElement} Newly created SVG element.
 */
Blockly.createSvgElement = function(name, attrs, opt_parent) {
  var e = /** @type {!SVGElement} */ (
      document.createElementNS(Blockly.SVG_NS, name));
  for (var key in attrs) {
    e.setAttribute(key, attrs[key]);
  }
  // IE defines a unique attribute "runtimeStyle", it is NOT applied to
  // elements created with createElementNS. However, Closure checks for IE
  // and assumes the presence of the attribute and crashes.
  if (document.body.runtimeStyle) {  // Indicates presence of IE-only attr.
    e.runtimeStyle = e.currentStyle = e.style;
  }
  if (opt_parent) {
    opt_parent.appendChild(e);
  }
  return e;
};

/**
 * Is this event a right-click?
 * @param {!Event} e Mouse event.
 * @return {boolean} True if right-click.
 */
Blockly.isRightButton = function(e) {
  if (e.ctrlKey && goog.userAgent.MAC) {
    // Control-clicking on Mac OS X is treated as a right-click.
    // WebKit on Mac OS X fails to change button to 2 (but Gecko does).
    return true;
  }
  return e.button == 2;
};

/**
 * Return the converted coordinates of the given mouse event.
 * The origin (0,0) is the top-left corner of the Blockly svg.
 * @param {!Event} e Mouse event.
 * @param {!Element} svg SVG element.
 * @return {!Object} Object with .x and .y properties.
 */
Blockly.mouseToSvg = function(e, svg) {
  var svgPoint = svg.createSVGPoint();
  svgPoint.x = e.clientX;
  svgPoint.y = e.clientY;
  var matrix = svg.getScreenCTM();
  matrix = matrix.inverse();
  return svgPoint.matrixTransform(matrix);
};

/**
 * Given an array of strings, return the length of the shortest one.
 * @param {!Array.<string>} array Array of strings.
 * @return {number} Length of shortest string.
 */
Blockly.shortestStringLength = function(array) {
  if (!array.length) {
    return 0;
  }
  var len = array[0].length;
  for (var i = 1; i < array.length; i++) {
    len = Math.min(len, array[i].length);
  }
  return len;
};

/**
 * Given an array of strings, return the length of the common prefix.
 * Words may not be split.  Any space after a word is included in the length.
 * @param {!Array.<string>} array Array of strings.
 * @param {?number} opt_shortest Length of shortest string.
 * @return {number} Length of common prefix.
 */
Blockly.commonWordPrefix = function(array, opt_shortest) {
  if (!array.length) {
    return 0;
  } else if (array.length == 1) {
    return array[0].length;
  }
  var wordPrefix = 0;
  var max = opt_shortest || Blockly.shortestStringLength(array);
  for (var len = 0; len < max; len++) {
    var letter = array[0][len];
    for (var i = 1; i < array.length; i++) {
      if (letter != array[i][len]) {
        return wordPrefix;
      }
    }
    if (letter == ' ') {
      wordPrefix = len + 1;
    }
  }
  for (var i = 1; i < array.length; i++) {
    var letter = array[i][len];
    if (letter && letter != ' ') {
      return wordPrefix;
    }
  }
  return max;
};

/**
 * Given an array of strings, return the length of the common suffix.
 * Words may not be split.  Any space after a word is included in the length.
 * @param {!Array.<string>} array Array of strings.
 * @param {?number} opt_shortest Length of shortest string.
 * @return {number} Length of common suffix.
 */
Blockly.commonWordSuffix = function(array, opt_shortest) {
  if (!array.length) {
    return 0;
  } else if (array.length == 1) {
    return array[0].length;
  }
  var wordPrefix = 0;
  var max = opt_shortest || Blockly.shortestStringLength(array);
  for (var len = 0; len < max; len++) {
    var letter = array[0].substr(-len - 1, 1);
    for (var i = 1; i < array.length; i++) {
      if (letter != array[i].substr(-len - 1, 1)) {
        return wordPrefix;
      }
    }
    if (letter == ' ') {
      wordPrefix = len + 1;
    }
  }
  for (var i = 1; i < array.length; i++) {
    var letter = array[i].charAt(array[i].length - len - 1);
    if (letter && letter != ' ') {
      return wordPrefix;
    }
  }
  return max;
};

/**
 * Is the given string a number (includes negative and decimals).
 * @param {string} str Input string.
 * @return {boolean} True if number, false otherwise.
 */
Blockly.isNumber = function(str) {
  return !!str.match(/^\s*-?\d+(\.\d+)?\s*$/);
};
<commit_msg>Support content editable (issue 113).<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2012 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Utility methods.
 * These methods are not specific to Blockly, and could be factored out into
 * a JavaScript framework such as Closure.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.provide('Blockly.utils');

goog.require('goog.events.BrowserFeature');
goog.require('goog.userAgent');

/**
 * Add a CSS class to a element.
 * Similar to Closure's goog.dom.classes.add, except it handles SVG elements.
 * @param {!Element} element DOM element to add class to.
 * @param {string} className Name of class to add.
 * @private
 */
Blockly.addClass_ = function(element, className) {
  var classes = element.getAttribute('class') || '';
  if ((' ' + classes + ' ').indexOf(' ' + className + ' ') == -1) {
    if (classes) {
      classes += ' ';
    }
    element.setAttribute('class', classes + className);
  }
};

/**
 * Remove a CSS class from a element.
 * Similar to Closure's goog.dom.classes.remove, except it handles SVG elements.
 * @param {!Element} element DOM element to remove class from.
 * @param {string} className Name of class to remove.
 * @private
 */
Blockly.removeClass_ = function(element, className) {
  var classes = element.getAttribute('class');
  if ((' ' + classes + ' ').indexOf(' ' + className + ' ') != -1) {
    var classList = classes.split(/\s+/);
    for (var i = 0; i < classList.length; i++) {
      if (!classList[i] || classList[i] == className) {
        classList.splice(i, 1);
        i--;
      }
    }
    if (classList.length) {
      element.setAttribute('class', classList.join(' '));
    } else {
      element.removeAttribute('class');
    }
  }
};

/**
 * Checks if an element has the specified CSS class.
 * Similar to Closure's goog.dom.classes.has, except it handles SVG elements.
 * @param {!Element} element DOM element to check.
 * @param {string} className Name of class to check.
 * @return {boolean} True if class exists, false otherwise.
 * @private
 */
Blockly.hasClass_ = function(element, className) {
  var classes = element.getAttribute('class');
  return (' ' + classes + ' ').indexOf(' ' + className + ' ') != -1;
};

/**
 * Bind an event to a function call.
 * @param {!Node} node Node upon which to listen.
 * @param {string} name Event name to listen to (e.g. 'mousedown').
 * @param {Object} thisObject The value of 'this' in the function.
 * @param {!Function} func Function to call when event is triggered.
 * @return {!Array.<!Array>} Opaque data that can be passed to unbindEvent_.
 * @private
 */
Blockly.bindEvent_ = function(node, name, thisObject, func) {
  if (thisObject) {
    var wrapFunc = function(e) {
      func.call(thisObject, e);
    };
  } else {
    var wrapFunc = func;
  }
  node.addEventListener(name, wrapFunc, false);
  var bindData = [[node, name, wrapFunc]];
  // Add equivalent touch event.
  if (name in Blockly.bindEvent_.TOUCH_MAP) {
    wrapFunc = function(e) {
      // Punt on multitouch events.
      if (e.changedTouches.length == 1) {
        // Map the touch event's properties to the event.
        var touchPoint = e.changedTouches[0];
        e.clientX = touchPoint.clientX;
        e.clientY = touchPoint.clientY;
      }
      func.call(thisObject, e);
      // Stop the browser from scrolling/zooming the page.
      e.preventDefault();
    };
    for (var i = 0, eventName;
         eventName = Blockly.bindEvent_.TOUCH_MAP[name][i]; i++) {
      node.addEventListener(eventName, wrapFunc, false);
      bindData.push([node, eventName, wrapFunc]);
    }
  }
  return bindData;
};

/**
 * The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
 * in conjunction with mouse events.
 * @type {Object}
 */
Blockly.bindEvent_.TOUCH_MAP = {};
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
  Blockly.bindEvent_.TOUCH_MAP = {
    'mousedown': ['touchstart'],
    'mousemove': ['touchmove'],
    'mouseup': ['touchend', 'touchcancel']
  };
}

/**
 * Unbind one or more events event from a function call.
 * @param {!Array.<!Array>} bindData Opaque data from bindEvent_.  This list is
 *     emptied during the course of calling this function.
 * @return {!Function} The function call.
 * @private
 */
Blockly.unbindEvent_ = function(bindData) {
  while (bindData.length) {
    var bindDatum = bindData.pop();
    var node = bindDatum[0];
    var name = bindDatum[1];
    var func = bindDatum[2];
    node.removeEventListener(name, func, false);
  }
  return func;
};

/**
 * Fire a synthetic event synchronously.
 * @param {!EventTarget} node The event's target node.
 * @param {string} eventName Name of event (e.g. 'click').
 */
Blockly.fireUiEventNow = function(node, eventName) {
  // Remove the event from the anti-duplicate database.
  var list = Blockly.fireUiEvent.DB_[eventName];
  if (list) {
    var i = list.indexOf(node);
    if (i != -1) {
      list.splice(i, 1);
    }
  }
  // Fire the event in a browser-compatible way.
  if (document.createEvent) {
    // W3
    var evt = document.createEvent('UIEvents');
    evt.initEvent(eventName, true, true);  // event type, bubbling, cancelable
    node.dispatchEvent(evt);
  } else if (document.createEventObject) {
    // MSIE
    var evt = document.createEventObject();
    node.fireEvent('on' + eventName, evt);
  } else {
    throw 'FireEvent: No event creation mechanism.';
  }
};

/**
 * Fire a synthetic event asynchronously.  Groups of simultaneous events (e.g.
 * a tree of blocks being deleted) are merged into one event.
 * @param {!EventTarget} node The event's target node.
 * @param {string} eventName Name of event (e.g. 'click').
 */
Blockly.fireUiEvent = function(node, eventName) {
  var list = Blockly.fireUiEvent.DB_[eventName];
  if (list) {
    if (list.indexOf(node) != -1) {
      // This event is already scheduled to fire.
      return;
    }
    list.push(node);
  } else {
    Blockly.fireUiEvent.DB_[eventName] = [node];
  }
  var fire = function() {
    Blockly.fireUiEventNow(node, eventName);
  };
  setTimeout(fire, 0);
};

/**
 * Database of upcoming firing event types.
 * Used to fire only one event after multiple changes.
 * @type {!Object}
 * @private
 */
Blockly.fireUiEvent.DB_ = {};

/**
 * Don't do anything for this event, just halt propagation.
 * @param {!Event} e An event.
 */
Blockly.noEvent = function(e) {
  // This event has been handled.  No need to bubble up to the document.
  e.preventDefault();
  e.stopPropagation();
};

/**
 * Is this event targeting a text input widget?
 * @param {!Event} e An event.
 * @return {boolean} True if text input.
 * @private
 */
Blockly.isTargetInput_ = function(e) {
  return e.target.type == 'textarea' || e.target.type == 'text' ||
         e.target.type == 'number' || e.target.type == 'email' ||
         e.target.type == 'password' || e.target.type == 'search' ||
         e.target.type == 'tel' || e.target.type == 'url' ||
         e.target.isContentEditable;
};

/**
 * Return the coordinates of the top-left corner of this element relative to
 * its parent.  Only for SVG elements and children (e.g. rect, g, path).
 * @param {!Element} element SVG element to find the coordinates of.
 * @return {!Object} Object with .x and .y properties.
 * @private
 */
Blockly.getRelativeXY_ = function(element) {
  var xy = {x: 0, y: 0};
  // First, check for x and y attributes.
  var x = element.getAttribute('x');
  if (x) {
    xy.x = parseInt(x, 10);
  }
  var y = element.getAttribute('y');
  if (y) {
    xy.y = parseInt(y, 10);
  }
  // Second, check for transform="translate(...)" attribute.
  var transform = element.getAttribute('transform');
  // Note that Firefox and IE (9,10) return 'translate(12)' instead of
  // 'translate(12, 0)'.
  // Note that IE (9,10) returns 'translate(16 8)' instead of
  // 'translate(16, 8)'.
  var r = transform &&
          transform.match(/translate\(\s*([-\d.]+)([ ,]\s*([-\d.]+)\s*\))?/);
  if (r) {
    xy.x += parseFloat(r[1]);
    if (r[3]) {
      xy.y += parseFloat(r[3]);
    }
  }
  return xy;
};

/**
 * Return the absolute coordinates of the top-left corner of this element.
 * The origin (0,0) is the top-left corner of the nearest SVG.
 * @param {!Element} element Element to find the coordinates of.
 * @return {!Object} Object with .x and .y properties.
 * @private
 */
Blockly.getSvgXY_ = function(element) {
  var x = 0;
  var y = 0;
  do {
    // Loop through this block and every parent.
    var xy = Blockly.getRelativeXY_(element);
    x += xy.x;
    y += xy.y;
    element = element.parentNode;
  } while (element && element.nodeName.toLowerCase() != 'svg');
  return {x: x, y: y};
};

/**
 * Helper method for creating SVG elements.
 * @param {string} name Element's tag name.
 * @param {!Object} attrs Dictionary of attribute names and values.
 * @param {Element=} opt_parent Optional parent on which to append the element.
 * @return {!SVGElement} Newly created SVG element.
 */
Blockly.createSvgElement = function(name, attrs, opt_parent) {
  var e = /** @type {!SVGElement} */ (
      document.createElementNS(Blockly.SVG_NS, name));
  for (var key in attrs) {
    e.setAttribute(key, attrs[key]);
  }
  // IE defines a unique attribute "runtimeStyle", it is NOT applied to
  // elements created with createElementNS. However, Closure checks for IE
  // and assumes the presence of the attribute and crashes.
  if (document.body.runtimeStyle) {  // Indicates presence of IE-only attr.
    e.runtimeStyle = e.currentStyle = e.style;
  }
  if (opt_parent) {
    opt_parent.appendChild(e);
  }
  return e;
};

/**
 * Is this event a right-click?
 * @param {!Event} e Mouse event.
 * @return {boolean} True if right-click.
 */
Blockly.isRightButton = function(e) {
  if (e.ctrlKey && goog.userAgent.MAC) {
    // Control-clicking on Mac OS X is treated as a right-click.
    // WebKit on Mac OS X fails to change button to 2 (but Gecko does).
    return true;
  }
  return e.button == 2;
};

/**
 * Return the converted coordinates of the given mouse event.
 * The origin (0,0) is the top-left corner of the Blockly svg.
 * @param {!Event} e Mouse event.
 * @param {!Element} svg SVG element.
 * @return {!Object} Object with .x and .y properties.
 */
Blockly.mouseToSvg = function(e, svg) {
  var svgPoint = svg.createSVGPoint();
  svgPoint.x = e.clientX;
  svgPoint.y = e.clientY;
  var matrix = svg.getScreenCTM();
  matrix = matrix.inverse();
  return svgPoint.matrixTransform(matrix);
};

/**
 * Given an array of strings, return the length of the shortest one.
 * @param {!Array.<string>} array Array of strings.
 * @return {number} Length of shortest string.
 */
Blockly.shortestStringLength = function(array) {
  if (!array.length) {
    return 0;
  }
  var len = array[0].length;
  for (var i = 1; i < array.length; i++) {
    len = Math.min(len, array[i].length);
  }
  return len;
};

/**
 * Given an array of strings, return the length of the common prefix.
 * Words may not be split.  Any space after a word is included in the length.
 * @param {!Array.<string>} array Array of strings.
 * @param {?number} opt_shortest Length of shortest string.
 * @return {number} Length of common prefix.
 */
Blockly.commonWordPrefix = function(array, opt_shortest) {
  if (!array.length) {
    return 0;
  } else if (array.length == 1) {
    return array[0].length;
  }
  var wordPrefix = 0;
  var max = opt_shortest || Blockly.shortestStringLength(array);
  for (var len = 0; len < max; len++) {
    var letter = array[0][len];
    for (var i = 1; i < array.length; i++) {
      if (letter != array[i][len]) {
        return wordPrefix;
      }
    }
    if (letter == ' ') {
      wordPrefix = len + 1;
    }
  }
  for (var i = 1; i < array.length; i++) {
    var letter = array[i][len];
    if (letter && letter != ' ') {
      return wordPrefix;
    }
  }
  return max;
};

/**
 * Given an array of strings, return the length of the common suffix.
 * Words may not be split.  Any space after a word is included in the length.
 * @param {!Array.<string>} array Array of strings.
 * @param {?number} opt_shortest Length of shortest string.
 * @return {number} Length of common suffix.
 */
Blockly.commonWordSuffix = function(array, opt_shortest) {
  if (!array.length) {
    return 0;
  } else if (array.length == 1) {
    return array[0].length;
  }
  var wordPrefix = 0;
  var max = opt_shortest || Blockly.shortestStringLength(array);
  for (var len = 0; len < max; len++) {
    var letter = array[0].substr(-len - 1, 1);
    for (var i = 1; i < array.length; i++) {
      if (letter != array[i].substr(-len - 1, 1)) {
        return wordPrefix;
      }
    }
    if (letter == ' ') {
      wordPrefix = len + 1;
    }
  }
  for (var i = 1; i < array.length; i++) {
    var letter = array[i].charAt(array[i].length - len - 1);
    if (letter && letter != ' ') {
      return wordPrefix;
    }
  }
  return max;
};

/**
 * Is the given string a number (includes negative and decimals).
 * @param {string} str Input string.
 * @return {boolean} True if number, false otherwise.
 */
Blockly.isNumber = function(str) {
  return !!str.match(/^\s*-?\d+(\.\d+)?\s*$/);
};
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ngList
 *
 * @description
 * Text input that converts between a delimited string and an array of strings. The default
 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
 *
 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
 *   list item is respected. This implies that the user of the directive is responsible for
 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
 *   tab or newline character.
 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
 *   when joining the list items back together) and whitespace around each list item is stripped
 *   before it is added to the model.
 *
 * ### Example with Validation
 *
 * <example name="ngList-directive" module="listExample">
 *   <file name="app.js">
 *      angular.module('listExample', [])
 *        .controller('ExampleController', ['$scope', function($scope) {
 *          $scope.names = ['morpheus', 'neo', 'trinity'];
 *        }]);
 *   </file>
 *   <file name="index.html">
 *    <form name="myForm" ng-controller="ExampleController">
 *      List: <input name="namesInput" ng-model="names" ng-list required>
 *
 *
 *        Required!
 *
 *

 *      <tt>names = {{names}}</tt>

 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt>

 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt>

 *      <tt>myForm.$valid = {{myForm.$valid}}</tt>

 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt>

 *     </form>
 *   </file>
 *   <file name="protractor.js" type="protractor">
 *     var listInput = element(by.model('names'));
 *     var names = element(by.exactBinding('names'));
 *     var valid = element(by.binding('myForm.namesInput.$valid'));
 *     var error = element(by.css('span.error'));
 *
 *     it('should initialize to model', function() {
 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
 *       expect(valid.getText()).toContain('true');
 *       expect(error.getCssValue('display')).toBe('none');
 *     });
 *
 *     it('should be invalid if empty', function() {
 *       listInput.clear();
 *       listInput.sendKeys('');
 *
 *       expect(names.getText()).toContain('');
 *       expect(valid.getText()).toContain('false');
 *       expect(error.getCssValue('display')).not.toBe('none');
 *     });
 *   </file>
 * </example>
 *
 * ### Example - splitting on whitespace
 * <example name="ngList-directive-newlines">
 *   <file name="index.html">
 *    <textarea ng-model="list" ng-list="
" ng-trim="false"></textarea>
 *
{{ list | json }}

 *   </file>
 *   <file name="protractor.js" type="protractor">
 *     it("should split the text by newlines", function() {
 *       var listInput = element(by.model('list'));
 *       var output = element(by.binding('list | json'));
 *       listInput.sendKeys('abc\ndef\nghi');
 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
 *     });
 *   </file>
 * </example>
 *
 * @element input
 * @param {string=} ngList optional delimiter that should be used to split the value.
 */
var ngListDirective = function() {
  return {
    restrict: 'A',
    priority: 100,
    require: 'ngModel',
    link: function(scope, element, attr, ctrl) {
      // We want to control whitespace trimming so we use this convoluted approach
      // to access the ngList attribute, which doesn't pre-trim the attribute
      var ngList = element.attr(attr.$attr.ngList) || ', ';
      var trimValues = attr.ngTrim !== 'false';
      var separator = trimValues ? trim(ngList) : ngList;

      var parse = function(viewValue) {
        // If the viewValue is invalid (say required but empty) it will be `undefined`
        if (isUndefined(viewValue)) return;

        var list = [];

        if (viewValue) {
          forEach(viewValue.split(separator), function(value) {
            if (value) list.push(trimValues ? trim(value) : value);
          });
        }

        return list;
      };

      ctrl.$parsers.push(parse);
      ctrl.$formatters.push(function(value) {
        if (isArray(value)) {
          return value.join(ngList);
        }

        return undefined;
      });

      // Override the standard $isEmpty because an empty array means the input is empty.
      ctrl.$isEmpty = function(value) {
        return !value || !value.length;
      };
    }
  };
};
<commit_msg>docs(ngList): whitespace -> newline<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ngList
 *
 * @description
 * Text input that converts between a delimited string and an array of strings. The default
 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
 *
 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
 *   list item is respected. This implies that the user of the directive is responsible for
 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
 *   tab or newline character.
 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
 *   when joining the list items back together) and whitespace around each list item is stripped
 *   before it is added to the model.
 *
 * ### Example with Validation
 *
 * <example name="ngList-directive" module="listExample">
 *   <file name="app.js">
 *      angular.module('listExample', [])
 *        .controller('ExampleController', ['$scope', function($scope) {
 *          $scope.names = ['morpheus', 'neo', 'trinity'];
 *        }]);
 *   </file>
 *   <file name="index.html">
 *    <form name="myForm" ng-controller="ExampleController">
 *      List: <input name="namesInput" ng-model="names" ng-list required>
 *
 *
 *        Required!
 *
 *

 *      <tt>names = {{names}}</tt>

 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt>

 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt>

 *      <tt>myForm.$valid = {{myForm.$valid}}</tt>

 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt>

 *     </form>
 *   </file>
 *   <file name="protractor.js" type="protractor">
 *     var listInput = element(by.model('names'));
 *     var names = element(by.exactBinding('names'));
 *     var valid = element(by.binding('myForm.namesInput.$valid'));
 *     var error = element(by.css('span.error'));
 *
 *     it('should initialize to model', function() {
 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
 *       expect(valid.getText()).toContain('true');
 *       expect(error.getCssValue('display')).toBe('none');
 *     });
 *
 *     it('should be invalid if empty', function() {
 *       listInput.clear();
 *       listInput.sendKeys('');
 *
 *       expect(names.getText()).toContain('');
 *       expect(valid.getText()).toContain('false');
 *       expect(error.getCssValue('display')).not.toBe('none');
 *     });
 *   </file>
 * </example>
 *
 * ### Example - splitting on newline
 * <example name="ngList-directive-newlines">
 *   <file name="index.html">
 *    <textarea ng-model="list" ng-list="
" ng-trim="false"></textarea>
 *
{{ list | json }}

 *   </file>
 *   <file name="protractor.js" type="protractor">
 *     it("should split the text by newlines", function() {
 *       var listInput = element(by.model('list'));
 *       var output = element(by.binding('list | json'));
 *       listInput.sendKeys('abc\ndef\nghi');
 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
 *     });
 *   </file>
 * </example>
 *
 * @element input
 * @param {string=} ngList optional delimiter that should be used to split the value.
 */
var ngListDirective = function() {
  return {
    restrict: 'A',
    priority: 100,
    require: 'ngModel',
    link: function(scope, element, attr, ctrl) {
      // We want to control whitespace trimming so we use this convoluted approach
      // to access the ngList attribute, which doesn't pre-trim the attribute
      var ngList = element.attr(attr.$attr.ngList) || ', ';
      var trimValues = attr.ngTrim !== 'false';
      var separator = trimValues ? trim(ngList) : ngList;

      var parse = function(viewValue) {
        // If the viewValue is invalid (say required but empty) it will be `undefined`
        if (isUndefined(viewValue)) return;

        var list = [];

        if (viewValue) {
          forEach(viewValue.split(separator), function(value) {
            if (value) list.push(trimValues ? trim(value) : value);
          });
        }

        return list;
      };

      ctrl.$parsers.push(parse);
      ctrl.$formatters.push(function(value) {
        if (isArray(value)) {
          return value.join(ngList);
        }

        return undefined;
      });

      // Override the standard $isEmpty because an empty array means the input is empty.
      ctrl.$isEmpty = function(value) {
        return !value || !value.length;
      };
    }
  };
};
<|endoftext|>


<commit_before>$(function() {

    var switchButton = $(".toggleSideBar");
    var sidePane = switchButton.closest("div.sidePane");
    var templateWrapper = $('#templateWrapper');
    var speed = 300;
    var animation = {};

    sidePane.data('isOpened', true);

    sidePane.css({
	'-webkit-transition': 'none !important',
	'-moz-transition': 'none !important',
	'-o-transition': 'none !important',
	'-ms-transition': 'none !important',
	'transition': 'none !important'
    });

    if(sidePane.data('closed')){
        sidePane.data('isOpened', false);
        sidePane.find('.sideContent').css('display','none');
        sidePane.css({
            'transition': 'none'
        });
        sidePane.css({right: (sidePane.outerWidth(true)*-1) + "px"});
    }

    sidePane.show(0);

    switchButton.on('click', function() {
        var isOpened = sidePane.data('isOpened');
        var align = sidePane.hasClass('right') ? 'right' : 'left';
        if(isOpened) {
            animation[align] =  "-" + sidePane.outerWidth(true) + "px"; //, "swing"];
            sidePane.animate(animation,speed, function() {
                templateWrapper.removeClass("sidePaneOpened");
                sidePane.data('isOpened', !isOpened);
		sidePane.find('.sideContent').css('display','none');
            });
        } else {
            templateWrapper.addClass("sidePaneOpened");
            animation[align] = "0px";
	    sidePane.find(".sideContent").css('display','block');
            sidePane.animate(animation, speed, function() {
                sidePane.data('isOpened', !isOpened);
            });
        }
    });
});
<commit_msg>fix closing sidepane.js<commit_after>$(function() {

    var switchButton = $(".toggleSideBar");
    var sidePane = switchButton.closest("div.sidePane");
    var templateWrapper = $('#templateWrapper');
    var speed = 300;
    var animation = {};

    sidePane.data('isOpened', true);

    sidePane.css({
        '-webkit-transition': 'none !important',
        '-moz-transition':    'none !important',
        '-o-transition':      'none !important',
        '-ms-transition':     'none !important',
        'transition':         'none !important'
    });

    // closing bugfix
    sidePane.width(sidePane.width());

    if(sidePane.data('closed')){
        sidePane.data('isOpened', false);
        sidePane.find('.sideContent').css('display','none');
        sidePane.css({
            'transition': 'none'
        });
        sidePane.css({right: (sidePane.outerWidth(true)*-1) + "px"});
    }

    sidePane.show(0);

    switchButton.on('click', function() {
        var isOpened = sidePane.data('isOpened');
        var align = sidePane.hasClass('right') ? 'right' : 'left';
        if(isOpened) {
            animation[align] =  "-" + sidePane.outerWidth(true) + "px"; //, "swing"];
            sidePane.animate(animation,speed, function() {
                templateWrapper.removeClass("sidePaneOpened");
                sidePane.data('isOpened', !isOpened);
                sidePane.find('.sideContent').css('display','none');
            });
        } else {
            templateWrapper.addClass("sidePaneOpened");
            animation[align] = "0px";
            sidePane.find(".sideContent").css('display','block');
            sidePane.animate(animation, speed, function() {
                sidePane.data('isOpened', !isOpened);
            });
        }
    });
});
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path');

// Read the contents of a dir. Adapted from https://gist.github.com/825583
exports.readDirSync = function(start) {
  try {
    // Use lstat to resolve symlink if we are passed a symlink
    var stat = fs.lstatSync(start);
    var found = {dirs: [], files: []}, total = 0, processed = 0;
    function isDir(abspath) {
      var stat = fs.statSync(abspath);
      if(stat.isDirectory()) {
        found.dirs.push(abspath);
        // If we found a directory, recurse!
        var data = exports.readDirSync(abspath);
        found.dirs = found.dirs.concat(data.dirs);
        found.files = found.files.concat(data.files);
        if(++processed == total) return found;
      } else {
        var file_name = abspath.split('/').last();
        if (!file_name.match(/(^_|^\.|~$)/)) found.files.push(abspath);
        if(++processed == total) return found;
      }
    }
    // Read through all the files in this directory
    if(stat.isDirectory()) {
      var files = fs.readdirSync(start).sort();
      total = files.length;
      for(var x=0, l=files.length; x<l; x++) {
        isDir(path.join(start, files[x]));
      }
    } else {
      throw (new Error("path: " + start + " is not a directory"));
    }
    return found;

  } catch(e) {
    if(e.code != 'ENOENT') throw(e); // Ignore if optional dirs are missing
    return false;
  };
};

<commit_msg>Fixes #51<commit_after>var fs = require('fs'),
    path = require('path');

// Read the contents of a dir. Adapted from https://gist.github.com/825583
exports.readDirSync = function(start) {
  try {
    // Use lstat to resolve symlink if we are passed a symlink
    var stat = fs.lstatSync(start);
    var found = {dirs: [], files: []}, total = 0, processed = 0;
    function isHidden(path){ return path.match(/(^_|^\.|~$)/); }
    function isDir(abspath) {
      var stat = fs.statSync(abspath);
      if(stat.isDirectory() && !isHidden(abspath.split('/').last())) {
        found.dirs.push(abspath);
        // If we found a directory, recurse!
        var data = exports.readDirSync(abspath);
        found.dirs = found.dirs.concat(data.dirs);
        found.files = found.files.concat(data.files);
        if(++processed == total) return found;
      } else {
        var file_name = abspath.split('/').last();
        if (!isHidden(file_name)) found.files.push(abspath);
        if(++processed == total) return found;
      }
    }
    // Read through all the files in this directory
    if(stat.isDirectory()) {
      var files = fs.readdirSync(start).sort();
      total = files.length;
      for(var x=0, l=files.length; x<l; x++) {
        isDir(path.join(start, files[x]));
      }
    } else {
      throw (new Error("path: " + start + " is not a directory"));
    }
    return found;

  } catch(e) {
    if(e.code != 'ENOENT') throw(e); // Ignore if optional dirs are missing
    return false;
  };
};

<|endoftext|>


<commit_before>// vim: ts=4:sw=4
/* global Ext */
/* global Curriki */
/* global _ */

Ext.ns('Curriki.module.nominate');
Curriki.module.nominate.init = function() {
	if (Ext.isEmpty(Curriki.module.nominate.initialized)) {
		// Local alias
		var Nominate = Curriki.module.nominate;

		Nominate.ie_size_shift = 10;

		Nominate.EnableNext = function() {
			Ext.getCmp('nextbutton').enable();
		}
		Nominate.DisableNext = function() {
			Ext.getCmp('nextbutton').disable();
		}

		Nominate.NominateDialogueId = 'nominate-resource-dialogue';
		Nominate.NominateResource = Ext.extend(Curriki.ui.dialog.Actions, {
			initComponent : function() {
				Ext.apply(this, {
					title : _('curriki.crs.nominate'),
					cls : 'Caption', // cls: the css class to use
					id : Nominate.NominateDialogueId,
					items : [{// Begin Panel
						// xtype is a symbolic name given to a class.
						// Ext.reg('panel', Ext.Panel); defined in
						// ext-all-debug.js
						xtype : 'panel'
						,
						items : [{
							xtype : 'box',
							cls : 'crs_nominate_title',
							autoEl : { // autoEl: for inserting html code
								tag : 'div',
								html : _('curriki.crs.nominate.nominatefollowingresourceforreview'),
								cls : 'crs_nominate_title'
							}
						}
						,{
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : Curriki.current.assetTitle,
								cls : 'crs_nominate_pagename'
							}
						}]
					},		// End Panel
					{		// Begin Form
						xtype : 'form',
						id : 'nominateResourcePanel',
						formId : 'nominateResourceForm',
						cls : 'form-container',
						labelWidth : 25,
						autoScroll : true,
						border : false,
						defaults : {
							labelSeparator : '',
							hideLabel : true,
							name : 'nominateResource'
						},
						bbar : ['->', {
							text : _('curriki.crs.nominate.cancel'),
							id : 'cancelbutton',
							cls : 'button button-cancel',
							listeners : {
								click : {
									fn : function() {
										this.close();
										window.location.href = Curriki.current.cameFrom;
									},
									scope : this
								}
							}
						}, {
							text : _('curriki.crs.nominate.submit'),
							id : 'submitbutton',
							cls : 'submitbutton button button-confirm',
							listeners : {
								click : {
									fn : function() {
										var form = this.findByType('form')[0]
												.getForm();
										if (form.isValid()) {
											var comments = (form
													.getValues(false))['nominate-comments'];
											Nominate.Nominate(comments);
										} else {
											alert("Invalid Form");
										}

									},
									scope : this
								}
							}
						}],
						monitorValid : true,
						listeners : {
							render : function(fPanel) {
								// TODO: Try to generalize this (for different #
								// of panels)
								fPanel.ownerCt.on('bodyresize', function(
										wPanel, width, height) {
									if (height === 'auto') {
										fPanel.setHeight('auto');
									} else {
										fPanel
												.setHeight(wPanel
														.getInnerHeight()
														- (wPanel
																.findByType('panel')[0]
																.getBox().height + (Ext.isIE
																? Nominate.ie_size_shift
																: 0)));
									}
								});
							}
						},
						items : [{
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : _('curriki.crs.nominate.comments'),
								cls : 'crs_nominate_title'
							}
						}, {
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : _('curriki.crs.nominate.commentstext'),
								cls : 'crs_nominate_commentstext'
							}
						}, {
							xtype : 'textarea',
							id : 'nominate-comments',
							name : 'nominate-comments' // ,allowBlank:false
							// ,preventMark:true
							// ,hideLabel:true
							,
							width : '80%'
						}, {
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : _('curriki.crs.nominate.commentsfootertext'),
								cls : 'crs_nominate_commentstext'
							}
						}]
					}		// End Form
					]
				});
				Nominate.NominateResource.superclass.initComponent.call(this);
			}
		});
		Ext.reg('nominateResourceDialog', Nominate.NominateResource);

		Nominate.Nominate = function(comments) {
			Curriki.assets.NominateAsset(Curriki.current.assetName, comments,
					function(response) {
						window.location.href = Curriki.current.cameFrom;
					});
		};

		Curriki.module.nominate.initialized = true;
	}
};

Curriki.module.nominate.nominateResource = function(options) {
	Curriki.module.nominate.initAndStart(function() {
		Curriki.ui.show('nominateResourceDialog');
	}, options);
}

Curriki.module.nominate.initAndStart = function(fcn, options) {
	var current = Curriki.current;
	if (!Ext.isEmpty(options)) {
		current.assetName = options.assetName || current.assetName;
		current.parentAsset = options.parentAsset || current.parentAsset;
		current.publishSpace = options.publishSpace || current.publishSpace;
		current.cameFrom = window.location.href;

		current.assetTitle = options.assetTitle || current.assetTitle;
		current.assetType = options.assetType || current.assetType;
		current.parentTitle = options.parentTitle || current.parentTitle;
	}

	Curriki.init(function() {
		if (Ext.isEmpty(Curriki.data.user.me)
				|| 'XWiki.XWikiGuest' === Curriki.data.user.me.username) {
			window.location.href = '/xwiki/bin/login/XWiki/XWikiLogin?xredirect='
					+ window.location.href;
			return;
		}

		Curriki.module.nominate.init();

		var startFn = function() {
			fcn();
		}

		var parentFn;
		if (!Ext.isEmpty(current.parentAsset)
				&& Ext.isEmpty(current.parentTitle)) {
			// Get parent asset info
			parentFn = function() {
				Curriki.assets.GetAssetInfo(current.parentAsset,
						function(info) {
							Curriki.current.parentTitle = info.title;
							startFn();
						});
			}
		} else {
			parentFn = function() {
				startFn();
			};
		}

		var currentFn;
		if (!Ext.isEmpty(current.assetName)
				&& (Ext.isEmpty(current.assetTitle) || Ext
						.isEmpty(current.assetType))) {
			// Get asset info
			currentFn = function() {
				Curriki.assets.GetAssetInfo(current.assetName, function(info) {
					Curriki.current.assetTitle = info.title;
					Curriki.current.assetType = info.assetType;
					parentFn();
				});
			}
		} else {
			currentFn = function() {
				parentFn();
			};
		}

		currentFn();
	});
}

Curriki.module.nominate.loaded = true;<commit_msg>SKE-348 Enabled html for translation keys<commit_after>// vim: ts=4:sw=4
/* global Ext */
/* global Curriki */
/* global _ */

Ext.ns('Curriki.module.nominate');
Curriki.module.nominate.init = function() {
	if (Ext.isEmpty(Curriki.module.nominate.initialized)) {
		// Local alias
		var Nominate = Curriki.module.nominate;

		Nominate.ie_size_shift = 10;

		Nominate.EnableNext = function() {
			Ext.getCmp('nextbutton').enable();
		}
		Nominate.DisableNext = function() {
			Ext.getCmp('nextbutton').disable();
		}

		Nominate.NominateDialogueId = 'nominate-resource-dialogue';
		Nominate.NominateResource = Ext.extend(Curriki.ui.dialog.Actions, {
			initComponent : function() {
				Ext.apply(this, {
					title : __('curriki.crs.nominate'),
					cls : 'Caption', // cls: the css class to use
					id : Nominate.NominateDialogueId,
					items : [{// Begin Panel
						// xtype is a symbolic name given to a class.
						// Ext.reg('panel', Ext.Panel); defined in
						// ext-all-debug.js
						xtype : 'panel'
						,
						items : [{
							xtype : 'box',
							cls : 'crs_nominate_title',
							autoEl : { // autoEl: for inserting html code
								tag : 'div',
								html : __('curriki.crs.nominate.nominatefollowingresourceforreview'),
								cls : 'crs_nominate_title'
							}
						}
						,{
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : Curriki.current.assetTitle,
								cls : 'crs_nominate_pagename'
							}
						}]
					},		// End Panel
					{		// Begin Form
						xtype : 'form',
						id : 'nominateResourcePanel',
						formId : 'nominateResourceForm',
						cls : 'form-container',
						labelWidth : 25,
						autoScroll : true,
						border : false,
						defaults : {
							labelSeparator : '',
							hideLabel : true,
							name : 'nominateResource'
						},
						bbar : ['->', {
							text : __('curriki.crs.nominate.cancel'),
							id : 'cancelbutton',
							cls : 'button button-cancel',
							listeners : {
								click : {
									fn : function() {
										this.close();
										window.location.href = Curriki.current.cameFrom;
									},
									scope : this
								}
							}
						}, {
							text : __('curriki.crs.nominate.submit'),
							id : 'submitbutton',
							cls : 'submitbutton button button-confirm',
							listeners : {
								click : {
									fn : function() {
										var form = this.findByType('form')[0]
												.getForm();
										if (form.isValid()) {
											var comments = (form
													.getValues(false))['nominate-comments'];
											Nominate.Nominate(comments);
										} else {
											alert("Invalid Form");
										}

									},
									scope : this
								}
							}
						}],
						monitorValid : true,
						listeners : {
							render : function(fPanel) {
								// TODO: Try to generalize this (for different #
								// of panels)
								fPanel.ownerCt.on('bodyresize', function(
										wPanel, width, height) {
									if (height === 'auto') {
										fPanel.setHeight('auto');
									} else {
										fPanel
												.setHeight(wPanel
														.getInnerHeight()
														- (wPanel
																.findByType('panel')[0]
																.getBox().height + (Ext.isIE
																? Nominate.ie_size_shift
																: 0)));
									}
								});
							}
						},
						items : [{
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : __('curriki.crs.nominate.comments'),
								cls : 'crs_nominate_title'
							}
						}, {
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : __('curriki.crs.nominate.commentstext'),
								cls : 'crs_nominate_commentstext'
							}
						}, {
							xtype : 'textarea',
							id : 'nominate-comments',
							name : 'nominate-comments' // ,allowBlank:false
							// ,preventMark:true
							// ,hideLabel:true
							,
							width : '80%'
						}, {
							xtype : 'box',
							autoEl : {
								tag : 'div',
								html : __('curriki.crs.nominate.commentsfootertext'),
								cls : 'crs_nominate_commentstext'
							}
						}]
					}		// End Form
					]
				});
				Nominate.NominateResource.superclass.initComponent.call(this);
			}
		});
		Ext.reg('nominateResourceDialog', Nominate.NominateResource);

		Nominate.Nominate = function(comments) {
			Curriki.assets.NominateAsset(Curriki.current.assetName, comments,
					function(response) {
						window.location.href = Curriki.current.cameFrom;
					});
		};

		Curriki.module.nominate.initialized = true;
	}
};

Curriki.module.nominate.nominateResource = function(options) {
	Curriki.module.nominate.initAndStart(function() {
		Curriki.ui.show('nominateResourceDialog');
	}, options);
}

Curriki.module.nominate.initAndStart = function(fcn, options) {
	var current = Curriki.current;
	if (!Ext.isEmpty(options)) {
		current.assetName = options.assetName || current.assetName;
		current.parentAsset = options.parentAsset || current.parentAsset;
		current.publishSpace = options.publishSpace || current.publishSpace;
		current.cameFrom = window.location.href;

		current.assetTitle = options.assetTitle || current.assetTitle;
		current.assetType = options.assetType || current.assetType;
		current.parentTitle = options.parentTitle || current.parentTitle;
	}

	Curriki.init(function() {
		if (Ext.isEmpty(Curriki.data.user.me)
				|| 'XWiki.XWikiGuest' === Curriki.data.user.me.username) {
			window.location.href = '/xwiki/bin/login/XWiki/XWikiLogin?xredirect='
					+ window.location.href;
			return;
		}

		Curriki.module.nominate.init();

		var startFn = function() {
			fcn();
		}

		var parentFn;
		if (!Ext.isEmpty(current.parentAsset)
				&& Ext.isEmpty(current.parentTitle)) {
			// Get parent asset info
			parentFn = function() {
				Curriki.assets.GetAssetInfo(current.parentAsset,
						function(info) {
							Curriki.current.parentTitle = info.title;
							startFn();
						});
			}
		} else {
			parentFn = function() {
				startFn();
			};
		}

		var currentFn;
		if (!Ext.isEmpty(current.assetName)
				&& (Ext.isEmpty(current.assetTitle) || Ext
						.isEmpty(current.assetType))) {
			// Get asset info
			currentFn = function() {
				Curriki.assets.GetAssetInfo(current.assetName, function(info) {
					Curriki.current.assetTitle = info.title;
					Curriki.current.assetType = info.assetType;
					parentFn();
				});
			}
		} else {
			currentFn = function() {
				parentFn();
			};
		}

		currentFn();
	});
}

Curriki.module.nominate.loaded = true;<|endoftext|>


<commit_before>'use strict'
/* eslint no-magic-numbers:0 */

const _ = require('lodash');
const cheerio = require('cheerio');
const request = require('request');

const News = require('../models/news');
const Allcaps = require('../models/allcaps');

module.exports.index = (req, res) => {
  res.send({this: 'is an API'});
};

module.exports.new = (req, res) => {
  const obj = _.mapValues(req.body, val => val.toUpperCase());
  const caps = new Allcaps(obj);
  caps.save((err, _caps) => {
    if (err) throw err;
    res.send(_caps);
  });
};

module.exports.news = (req, res) => {
  News.findOne({}).sort({_id: -1}).exec((err, doc) => {
    if (doc) {
      const FIFTEEN_MINS = 15 * 60 * 1000;
      const diff = new Date() - doc._id.getTimestamp() - FIFTEEN_MINS;
      const newsIsFresh = diff < 0;
      if (newsIsFresh) {
        res.send(doc)
        return;
      }
    }

    const url = 'http://cnn.com';

    request.get(url, (err, response, body) => {
      if (err) throw err;

      const $ = cheerio.load(body);
      const news = [];
      const $bannerText = $('.banner-text')
      news.push({
        title: $bannerText.text(),
        url: `http://cnn.com${$bannerText.closest('a').attr('href')}`
      });

      const $headline = $('.cd__headline');

      _.range(1, 12).forEach(i => {
        const $headlineEl = $headline.eq(i)
        let theUrl = $headlineEl.find('a').attr('href');
        if (theUrl) {
          if (theUrl.indexOf('http') !== 0) {
            theUrl = `http://cnn.com${theUrl}`;
          }
          news.push({
            title: $headlineEl.text(),
            url: theUrl
          });
        }
      });

      const obj = new News({ top: news });

      obj.save((err, _news) => {
        if (err) throw err;

        res.send(_news);
      });
    });
  });
};

module.exports.passthrough = (req, res) => {
  const url = 'https://randomapi.com/api/?key=2IS9-4BF5-3W5L-7Z67&id=irta6tm';
  request.get(url, (err, response, body) => {
    if (err) throw err;
    res.send(JSON.parse(body));
  });
};
<commit_msg>Switched external API call to new RandomAPI beta public url.<commit_after>'use strict'
/* eslint no-magic-numbers:0 */

const _ = require('lodash');
const cheerio = require('cheerio');
const request = require('request');

const News = require('../models/news');
const Allcaps = require('../models/allcaps');

module.exports.index = (req, res) => {
  res.send({this: 'is an API'});
};

module.exports.new = (req, res) => {
  const obj = _.mapValues(req.body, val => val.toUpperCase());
  const caps = new Allcaps(obj);
  caps.save((err, _caps) => {
    if (err) throw err;
    res.send(_caps);
  });
};

module.exports.news = (req, res) => {
  News.findOne({}).sort({_id: -1}).exec((err, doc) => {
    if (doc) {
      const FIFTEEN_MINS = 15 * 60 * 1000;
      const diff = new Date() - doc._id.getTimestamp() - FIFTEEN_MINS;
      const newsIsFresh = diff < 0;
      if (newsIsFresh) {
        res.send(doc)
        return;
      }
    }

    const url = 'http://cnn.com';

    request.get(url, (err, response, body) => {
      if (err) throw err;

      const $ = cheerio.load(body);
      const news = [];
      const $bannerText = $('.banner-text')
      news.push({
        title: $bannerText.text(),
        url: `http://cnn.com${$bannerText.closest('a').attr('href')}`
      });

      const $headline = $('.cd__headline');

      _.range(1, 12).forEach(i => {
        const $headlineEl = $headline.eq(i)
        let theUrl = $headlineEl.find('a').attr('href');
        if (theUrl) {
          if (theUrl.indexOf('http') !== 0) {
            theUrl = `http://cnn.com${theUrl}`;
          }
          news.push({
            title: $headlineEl.text(),
            url: theUrl
          });
        }
      });

      const obj = new News({ top: news });

      obj.save((err, _news) => {
        if (err) throw err;

        res.send(_news);
      });
    });
  });
};

module.exports.passthrough = (req, res) => {
  const url = 'http://beta.randomapi.com/api/98e4f746c52da19539e90cfce16e472a';
  request.get(url, (err, response, body) => {
    if (err) throw err;
    res.send(JSON.parse(body));
  });
};
<|endoftext|>


<commit_before>var PersonsController = function($rootScope, $scope, $state, $stateParams, ftrees, notification, $timeout, $uibModal, $window, $location) {

	var self = this,
		advanced_fields = ['birth_place', 'marriage_place', 'death_place', 'tree_number', 'birth_year', 'marriage_year', 'death_year'];
	this.individuals = [];
	this.search_params = {};
	this.query = '';

	this.search_modifiers = {
		first_name: 	'',
		last_name: 		'',
		birth_place: 	'',
		marriage_place: '',
		death_place: 	'',
		place:          ''
	};
	this.tree_number = parseInt($stateParams['tree_number']);

	this.$state = $state;
	this.$stateParams = $stateParams;
	this.$scope = $scope;
	this.$window = $window;
	this.$location = $location;
	this.ftrees = ftrees;
	this.notification = notification;
	this.persons_welcome_msg = ($window.sessionStorage.getItem('persons-welcome-msg') == 'dismissed') ||
		  					   ($window.localStorage.getItem('persons-welcome-msg') == 'dismissed');

	Object.defineProperty($scope, '$stateParams', {
		get: function() {
			return $stateParams;
		}
	});

   	$rootScope.$on('$stateChangeStart',
	    function(event, toState, toParams, fromState, fromParams) {
		  	var str = 'persons';
		  	var from_person_state = fromState.name.indexOf(str, fromState.name.length - str.length) !== -1;
		  	var to_person_state = toState.name.indexOf(str, toState.name.length - str.length) !== -1;

		  	if (to_person_state && from_person_state && fromParams.more != toParams.more) {
		  		event.preventDefault();
			  	self.$state.transitionTo(toState.name, toParams, {notify: false});
		  	}
	});

	if (!self.persons_welcome_msg) {

		var modalInstance = $uibModal.open({
		     	templateUrl: 'templates/main/ftrees/persons-welcome-message.html',
		     	controller: 'PersonsWelcomeCtrl',
		     	animation: true,
		     	size: 'ftree'
	    });

		modalInstance.result.then(function () {
		    $window.localStorage.setItem('persons-welcome-msg', 'dismissed');

		}, function () {
		    $window.sessionStorage.setItem('persons-welcome-msg', 'dismissed');
		});
	};

	//search
	var query = {},
	    parameters = [],
	    advanced = $stateParams['more']=='1';

	for (var key in $stateParams) {
		if ($stateParams[key]) {
			// read state params & update bound objects to update view accordingly
			var val = $stateParams[key];
			if (key !== 'more') {
				if (advanced || (advanced_fields.indexOf(key) == -1)) {
					parameters.push(val);
					query[key] = val;
				}

				// handle search modifiers & fudge factors in query string
				if ( val.indexOf(';') !== -1 ) {
					var parts = val.split(';');
					self.search_params[key] = parts[0];
					self.search_modifiers[key] = parts[1];
				}
				else {
					if(key !== 'tree_number') {
						self.search_params[key] = $stateParams[key];
					}
					else
						self.search_params[key] = parseInt($stateParams[key]);
				}
			}
		}
	};
	if (parameters.length > 0) {
		this.query = parameters.join(' + ');
		self.search(query);
	};
};

PersonsController.prototype = {
	search: function(search_params) {
		var self = this;

		this.notification.loading(true);

		this.ftrees.search(search_params).
			then(function(individuals) {
				self.individuals = individuals;

				self.notification.loading(false);
				if (individuals.total == 0)
					self.notification.put(3);

			}, function() {
				self.notification.put(500);
			});
	},

	fetch_more: function() {
		var self = this;
		this.notification.loading(true);
		var search_params = angular.copy(this.search_params);
		search_params.start = self.individuals.items.length;
		this.ftrees.search(search_params).
			then(function (r){
				self.individuals.items = self.individuals.items.concat(r.items);
				self.notification.loading(false);
			});
    },

    toggle_more: function() {
    	var param = this.$stateParams;
    	if (param.more == '0' || param.more == undefined) {
    		param.more = '1';
    		if(this.search_params.place) {
    			this.search_params.birth_place = this.search_params.place;
    			this.search_params.place = '';
    		}
    	}
    	else {
     		param.more = '0';
     		if(this.search_params.birth_place) {
     			this.search_params.place = this.search_params.birth_place;
     			this.search_params.birth_place = '';
     		}
    	}
    	this.$location.search(param);
    },

	update: function() {
		var search_params = angular.copy(this.search_params);

		for (var param in search_params) {
			if (search_params[param] === '') {
				delete search_params[param];
			}
		}

		// insert search modifiers & fudge_factors into query string
		for (var modifier in this.search_modifiers) {
			var modifier_string = this.search_modifiers[modifier];
			if (search_params[modifier] !== undefined && modifier_string !== '') {
				search_params[modifier] += ';' + modifier_string;
			}
		}
		search_params.more = this.$stateParams.more;

		this.$state.go('persons', search_params, {inherit: false});
		this.$window.sessionStorage.setItem('ftrees_search_params', JSON.stringify(search_params));
	},

 	clear_filters: function() {
 		for (var parameter in this.search_params) {
 			this.search_params[parameter] = '';
 			this.$location.search(parameter, null);
 		}
 	},

	get_href: function (individual) {
		// TODO: support languages
		return this.$state.href('person-view', {tree_number: individual.GTN,
							   			   node_id: individual.II});
	},

	read_about_center: function (collection_name) {
        this.$state.go('about_center', {collection: collection_name});
    },
};

angular.module('main').controller('PersonsController', ['$rootScope', '$scope', '$state', '$stateParams', 'ftrees', 'notification', '$timeout', '$uibModal', '$window', '$location', PersonsController]);
<commit_msg>fix More button in Person search (with prefix)<commit_after>var PersonsController = function($rootScope, $scope, $state, $stateParams, ftrees, notification, $timeout, $uibModal, $window, $location) {

	var self = this,
		advanced_fields = ['birth_place', 'marriage_place', 'death_place', 'tree_number', 'birth_year', 'marriage_year', 'death_year'];
	this.individuals = [];
	this.search_params = {};
	this.query = '';

	this.search_modifiers = {
		first_name: 	'',
		last_name: 		'',
		birth_place: 	'',
		marriage_place: '',
		death_place: 	'',
		place:          ''
	};
	this.tree_number = parseInt($stateParams['tree_number']);

	this.$state = $state;
	this.$stateParams = $stateParams;
	this.$scope = $scope;
	this.$window = $window;
	this.$location = $location;
	this.ftrees = ftrees;
	this.notification = notification;
	this.persons_welcome_msg = ($window.sessionStorage.getItem('persons-welcome-msg') == 'dismissed') ||
		  					   ($window.localStorage.getItem('persons-welcome-msg') == 'dismissed');

	Object.defineProperty($scope, '$stateParams', {
		get: function() {
			return $stateParams;
		}
	});

   	$rootScope.$on('$stateChangeStart',
	    function(event, toState, toParams, fromState, fromParams) {
		  	var str = 'persons';
		  	var from_person_state = fromState.name.indexOf(str, fromState.name.length - str.length) !== -1;
		  	var to_person_state = toState.name.indexOf(str, toState.name.length - str.length) !== -1;

		  	if (to_person_state && from_person_state && fromParams.more != toParams.more) {
		  		event.preventDefault();
			  	self.$state.transitionTo(toState.name, toParams, {notify: false});
		  	}
	});

	if (!self.persons_welcome_msg) {

		var modalInstance = $uibModal.open({
		     	templateUrl: 'templates/main/ftrees/persons-welcome-message.html',
		     	controller: 'PersonsWelcomeCtrl',
		     	animation: true,
		     	size: 'ftree'
	    });

		modalInstance.result.then(function () {
		    $window.localStorage.setItem('persons-welcome-msg', 'dismissed');

		}, function () {
		    $window.sessionStorage.setItem('persons-welcome-msg', 'dismissed');
		});
	};

	//search
	var query = {},
	    parameters = [],
	    advanced = $stateParams['more']=='1';

	for (var key in $stateParams) {
		if ($stateParams[key]) {
			// read state params & update bound objects to update view accordingly
			var val = $stateParams[key];
			if (key !== 'more') {
				if (advanced || (advanced_fields.indexOf(key) == -1)) {
					parameters.push(val);
					query[key] = val;
				}

				// handle search modifiers & fudge factors in query string
				if ( val.indexOf(';') !== -1 ) {
					var parts = val.split(';');
					self.search_params[key] = parts[0];
					self.search_modifiers[key] = parts[1];
				}
				else {
					if(key !== 'tree_number') {
						self.search_params[key] = $stateParams[key];
					}
					else
						self.search_params[key] = parseInt($stateParams[key]);
				}
			}
		}
	};
	if (parameters.length > 0) {
		this.query = parameters.join(' + ');
		self.search(query);
	};
};

PersonsController.prototype = {
	search: function(search_params) {
		var self = this;
		this.notification.loading(true);
		this.ftrees.search(search_params).
			then(function(individuals) {
				self.individuals = individuals;

				self.notification.loading(false);
				if (individuals.total == 0)
					self.notification.put(3);

			}, function() {
				self.notification.put(500);
			});
	},

	fetch_more: function() {
		var self = this;
		this.notification.loading(true);
		var search_params = angular.copy(self.search_params);
		search_params.start = self.individuals.items.length;
		this.handle_search_modifiers(search_params);
		this.ftrees.search(search_params).
			then(function (r){
				self.individuals.items = self.individuals.items.concat(r.items);
				self.notification.loading(false);
			});
    },

    toggle_more: function() {
    	var param = this.$stateParams;
    	if (param.more == '0' || param.more == undefined) {
    		param.more = '1';
    		if(this.search_params.place) {
    			this.search_params.birth_place = this.search_params.place;
    			this.search_params.place = '';
    		}
    	}
    	else {
     		param.more = '0';
     		if(this.search_params.birth_place) {
     			this.search_params.place = this.search_params.birth_place;
     			this.search_params.birth_place = '';
     		}
    	}
    	this.$location.search(param);
    },

    handle_search_modifiers: function(search_params) {
		for (var modifier in this.search_modifiers) {
			var modifier_string = this.search_modifiers[modifier];
			if (this.search_params[modifier] !== undefined && modifier_string !== '') {
				search_params[modifier] += ';' + modifier_string;
			}
		}
    },

	update: function() {
		var search_params = angular.copy(this.search_params);

		for (var param in search_params) {
			if (search_params[param] === '') {
				delete search_params[param];
			}
		}
		this.handle_search_modifiers(search_params);
		search_params.more = this.$stateParams.more;
		this.$state.go('persons', search_params, {inherit: false});
		this.$window.sessionStorage.setItem('ftrees_search_params', JSON.stringify(search_params));
	},

 	clear_filters: function() {
 		for (var parameter in this.search_params) {
 			this.search_params[parameter] = '';
 			this.$location.search(parameter, null);
 		}
 	},

	get_href: function (individual) {
		// TODO: support languages
		return this.$state.href('person-view', {tree_number: individual.GTN,
							   			   node_id: individual.II});
	},

	read_about_center: function (collection_name) {
        this.$state.go('about_center', {collection: collection_name});
    },
};

angular.module('main').controller('PersonsController', ['$rootScope', '$scope', '$state', '$stateParams', 'ftrees', 'notification', '$timeout', '$uibModal', '$window', '$location', PersonsController]);
<|endoftext|>


<commit_before>import { json as d3_json } from 'd3-request';

import { utilQsString } from '../util';
import { currentLocale } from '../util/locale';

var endpoint = 'https://www.wikidata.org/w/api.php?';
var _wikidataCache = {};

export default {

    init: function() {},

    reset: function() {
        _wikidataCache = {};
    },

    // Given a Wikipedia language and article title, return an array of
    // corresponding Wikidata entities.
    itemsByTitle: function(lang, title, callback) {
        if (!title) {
            callback('', {});
            return;
        }

        lang = lang || 'en';
        d3_json(endpoint + utilQsString({
            action: 'wbgetentities',
            format: 'json',
            sites: lang.replace(/-/g, '_') + 'wiki',
            titles: title,
            languages: 'en', // shrink response by filtering to one language
            origin: '*'
        }), function(err, data) {
            if (err || !data || data.error) {
                callback('', {});
            } else {
                callback(title, data.entities || {});
            }
        });
    },

    entityByQID: function(qid, callback) {
        if (!qid) {
            callback('', {});
            return;
        }
        if (_wikidataCache[qid]) {
            callback('', _wikidataCache[qid]);
            return;
        }

        var lang = currentLocale.replace(/-/g, '_');

        d3_json(endpoint + utilQsString({
            action: 'wbgetentities',
            format: 'json',
            ids: qid,
            props: /*sitelinks|*/'labels|descriptions',
            //sitefilter: lang + 'wiki',
            languages: lang,
            languagefallback: 1,
            origin: '*'
        }), function(err, data) {
            if (err || !data || data.error) {
                callback('', {});
            } else {
                _wikidataCache[qid] = data.entities[qid];
                callback(qid, data.entities[qid] || {});
            }
        });
    }

};
<commit_msg>Use user's locale for fetching wikidata label/description (closes #5563)<commit_after>import _uniq from 'lodash-es/uniq';

import { json as d3_json } from 'd3-request';

import { utilQsString } from '../util';
import { currentLocale } from '../util/locale';

var endpoint = 'https://www.wikidata.org/w/api.php?';
var _wikidataCache = {};

export default {

    init: function() {},

    reset: function() {
        _wikidataCache = {};
    },

    // Given a Wikipedia language and article title, return an array of
    // corresponding Wikidata entities.
    itemsByTitle: function(lang, title, callback) {
        if (!title) {
            callback('', {});
            return;
        }

        lang = lang || 'en';

        d3_json(endpoint + utilQsString({
            action: 'wbgetentities',
            format: 'json',
            formatversion: 2,
            sites: lang.replace(/-/g, '_') + 'wiki',
            titles: title,
            languages: 'en', // shrink response by filtering to one language
            origin: '*'
        }), function(err, data) {
            if (err || !data || data.error) {
                callback('', {});
            } else {
                callback(title, data.entities || {});
            }
        });
    },

    entityByQID: function(qid, callback) {
        if (!qid) {
            callback('', {});
            return;
        }
        if (_wikidataCache[qid]) {
            callback('', _wikidataCache[qid]);
            return;
        }

        var langs = _uniq([
            currentLocale.toLowerCase(),
            currentLocale.split('-', 2)[0].toLowerCase(),
            'en'
        ]);

        d3_json(endpoint + utilQsString({
            action: 'wbgetentities',
            format: 'json',
            formatversion: 2,
            ids: qid,
            props: /*sitelinks|*/'labels|descriptions',
            //sitefilter: lang + 'wiki',
            languages: langs.join('|'),
            languagefallback: 1,
            origin: '*'
        }), function(err, data) {
            if (err || !data || data.error) {
                callback('', {});
            } else {
                _wikidataCache[qid] = data.entities[qid];
                callback(qid, data.entities[qid] || {});
            }
        });
    }

};
<|endoftext|>


<commit_before>var quicktest = function(svg, data, Plottable) {

  console.log(data);

  var xScale = new Plottable.Scale.Linear();
  var xAxis = new Plottable.Axis.XAxis(xScale, "bottom");

  var yScale = new Plottable.Scale.Linear();
  var yAxis = new Plottable.Axis.YAxis(yScale, "left").tickLabelPosition("bottom");
  yAxis.tickFormat(d3.format("g"));

  var renderAreaD1 = new Plottable.Plot.Scatter(data[0].slice(0, 21), xScale, yScale);
  var gridlines = new Plottable.Component.Gridlines(xScale, yScale);
  var renderGroup = renderAreaD1.merge(gridlines);
  var chart = new Plottable.Component.Table([[yAxis, renderGroup],
                                         [null,  xAxis]]);
  var pzi = new Plottable.Interaction.PanZoom(renderGroup, xScale, yScale);
  pzi.registerWithComponent();
  chart.renderTo(svg);

}

quicktest.quicktestName = "gridlines";
quicktests.push(quicktest);

<commit_msg>Update gridlines.js<commit_after>var quicktest = function(svg, data, Plottable) {

  console.log(data);

  var xScale = new Plottable.Scale.Linear();
  var xAxis = new Plottable.Axis.XAxis(xScale, "bottom");

  var yScale = new Plottable.Scale.Linear();
  var yAxis = new Plottable.Axis.YAxis(yScale, "left").tickLabelPosition("bottom");
  yAxis.tickFormat(d3.format("g"));

  var renderAreaD1 = new Plottable.Plot.Scatter(data[0].slice(0, 21), xScale, yScale);
  var gridlines = new Plottable.Component.Gridlines(xScale, yScale);
  var renderGroup = renderAreaD1.merge(gridlines);
  var chart = new Plottable.Component.Table([[yAxis, renderGroup],
                                         [null,  xAxis]]);
  //var pzi = new Plottable.Interaction.PanZoom(renderGroup, xScale, yScale);
  //pzi.registerWithComponent();
  chart.renderTo(svg);

}

quicktest.quicktestName = "gridlines";
quicktests.push(quicktest);

<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular.module('scaleApp').factory('Workspace', function (scaleConfig) {
        var Workspace = function (id, name, title, description, base_url, is_active, used_size, total_size, created, archived, last_modified, json_config) {
            this.id = id;
            this.name = name;
            this.title = title;
            this.description = description;
            this.base_url = base_url;
            this.is_active = is_active;
            this.used_size = used_size;
            this.total_size = total_size;
            this.created = created;
            this.archived = archived;
            this.last_modified = last_modified;
            this.json_config = json_config || scaleConfig.workspaceTypes[0];
        };

        Workspace.prototype = {
            clean: function () {
                return {
                    name: this.name,
                    title: this.title,
                    description: this.description,
                    base_url: this.base_url,
                    is_active: this.is_active,
                    json_config: this.json_config
                };
            }
        };

        // static methods, assigned to class
        Workspace.build = function (data) {
            if (data) {
                return new Workspace(
                    data.id,
                    data.name,
                    data.title,
                    data.description,
                    data.base_url,
                    data.is_active,
                    data.used_size,
                    data.total_size,
                    data.created,
                    data.archived,
                    data.last_modified,
                    data.json_config
                );
            }
            return new Workspace();
        };

        Workspace.transformer = function (data) {
            if (angular.isArray(data)) {
                return data.map(Workspace.build);
            }
            return Workspace.build(data);
        };

        return Workspace;
    });
})();
<commit_msg>bugfix for broker type select input<commit_after>(function () {
    'use strict';

    angular.module('scaleApp').factory('Workspace', function (scaleConfig) {
        var Workspace = function (id, name, title, description, base_url, is_active, used_size, total_size, created, archived, last_modified, json_config) {
            this.id = id;
            this.name = name;
            this.title = title;
            this.description = description;
            this.base_url = base_url;
            this.is_active = is_active;
            this.used_size = used_size;
            this.total_size = total_size;
            this.created = created;
            this.archived = archived;
            this.last_modified = last_modified;
            this.json_config = json_config || _.cloneDeep(scaleConfig.workspaceTypes[0]);
        };

        Workspace.prototype = {
            clean: function () {
                return {
                    name: this.name,
                    title: this.title,
                    description: this.description,
                    base_url: this.base_url,
                    is_active: this.is_active,
                    json_config: this.json_config
                };
            }
        };

        // static methods, assigned to class
        Workspace.build = function (data) {
            if (data) {
                return new Workspace(
                    data.id,
                    data.name,
                    data.title,
                    data.description,
                    data.base_url,
                    data.is_active,
                    data.used_size,
                    data.total_size,
                    data.created,
                    data.archived,
                    data.last_modified,
                    data.json_config
                );
            }
            return new Workspace();
        };

        Workspace.transformer = function (data) {
            if (angular.isArray(data)) {
                return data.map(Workspace.build);
            }
            return Workspace.build(data);
        };

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


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:ngView
 * @restrict ECA
 *
 * @description
 * # Обзор
 * Директива `ngView` дополняет сервис {@link ng.$route $route}, включая отображение шаблона по текущему адресу
 * в основном файле (`index.html`). Каждый раз, когда меняется адрес, меняется и представление в соответствии
 * с настройкой сервиса `$route`
 *
 * Дополнительно можно задать анимациючерез атрибут ngAnimate для анимации эффектов **enter**
 * и **leave**.
 *
 * @animations
 * enter - происходит только после того как изменилось содержимое ngView (когда DOM-элемент с новым видом вставляется в DOM)
 * leave - происходит только после того как изменилось содержимое ngView и до того как прежнее содержимое удалится из DOM
 *
 * @scope
 * @example
    <example module="ngView" animations="true">
      <file name="index.html">

          Choose:
          Moby |
          Moby: Ch1 |
          Gatsby |
          Gatsby: Ch4 |
          Scarlet Letter

$location.path() = {{$location.path()}}

$route.current.templateUrl = {{$route.current.templateUrl}}

$route.current.params = {{$route.current.params}}

$route.current.scope.name = {{$route.current.scope.name}}

$routeParams = {{$routeParams}}

      </file>

      <file name="book.html">

          controller: {{name}}

          Book Id: {{params.bookId}}

      </file>

      <file name="chapter.html">

          controller: {{name}}

          Book Id: {{params.bookId}}

          Chapter Id: {{params.chapterId}}

      </file>

      <file name="animations.css">
        .example-leave-setup, .example-enter-setup {
          -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
        }

        .example-animate-container {
          position:relative;
          height:100px;
        }

        .example-animate-container > * {
          display:block;
          width:100%;
          border-left:1px solid black;

          position:absolute;
          top:0;
          left:0;
          right:0;
          bottom:0;
          padding:10px;
        }

        .example-enter-setup {
          left:100%;
        }
        .example-enter-setup.example-enter-start {
          left:0;
        }

        .example-leave-setup { }
        .example-leave-setup.example-leave-start {
          left:-100%;
        }
      </file>

      <file name="script.js">
        angular.module('ngView', [], function($routeProvider, $locationProvider) {
          $routeProvider.when('/Book/:bookId', {
            templateUrl: 'book.html',
            controller: BookCntl
          });
          $routeProvider.when('/Book/:bookId/ch/:chapterId', {
            templateUrl: 'chapter.html',
            controller: ChapterCntl
          });

          // configure html5 to get links working on jsfiddle
          $locationProvider.html5Mode(true);
        });

        function MainCntl($scope, $route, $routeParams, $location) {
          $scope.$route = $route;
          $scope.$location = $location;
          $scope.$routeParams = $routeParams;
        }

        function BookCntl($scope, $routeParams) {
          $scope.name = "BookCntl";
          $scope.params = $routeParams;
        }

        function ChapterCntl($scope, $routeParams) {
          $scope.name = "ChapterCntl";
          $scope.params = $routeParams;
        }
      </file>

      <file name="scenario.js">
        it('should load and compile correct template', function() {
          element('a:contains("Moby: Ch1")').click();
          var content = element('.doc-example-live [ng-view]').text();
          expect(content).toMatch(/controller\: ChapterCntl/);
          expect(content).toMatch(/Book Id\: Moby/);
          expect(content).toMatch(/Chapter Id\: 1/);

          element('a:contains("Scarlet")').click();
          content = element('.doc-example-live [ng-view]').text();
          expect(content).toMatch(/controller\: BookCntl/);
          expect(content).toMatch(/Book Id\: Scarlet/);
        });
      </file>
    </example>
 */

/**
 * @ngdoc event
 * @name ng.directive:ngView#$viewContentLoaded
 * @eventOf ng.directive:ngView
 * @eventType задействует текущую область видимости ngView
 * @description
 * Задействуется каждый раз когда содержимое ngView перезагружается.
 */
var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
                       '$controller', '$animator',
               function($http,   $templateCache,   $route,   $anchorScroll,   $compile,
                        $controller,  $animator) {
  return {
    restrict: 'ECA',
    terminal: true,
    link: function(scope, element, attr) {
      var lastScope,
          onloadExp = attr.onload || '',
          animate = $animator(scope, attr);

      scope.$on('$routeChangeSuccess', update);
      update();

      function destroyLastScope() {
        if (lastScope) {
          lastScope.$destroy();
          lastScope = null;
        }
      }

      function clearContent() {
        animate.leave(element.contents(), element);
        destroyLastScope();
      }

      function update() {
        var locals = $route.current && $route.current.locals,
            template = locals && locals.$template;

        if (template) {
          clearContent();
          animate.enter(jqLite('

').html(template).contents(), element);

          var link = $compile(element.contents()),
              current = $route.current,
              controller;

          lastScope = current.scope = scope.$new();
          if (current.controller) {
            locals.$scope = lastScope;
            controller = $controller(current.controller, locals);
            element.children().data('$ngControllerController', controller);
          }

          link(lastScope);
          lastScope.$emit('$viewContentLoaded');
          lastScope.$eval(onloadExp);

          // $anchorScroll might listen on event...
          $anchorScroll();
        } else {
          clearContent();
        }
      }
    }
  };
}];
<commit_msg>fix(api:ngView): Поправил сборку<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ng.directive:ngView
 * @restrict ECA
 *
 * @description
 * # Обзор
 * Директива `ngView` дополняет сервис {@link ng.$route $route}, включая отображение шаблона по текущему адресу
 * в основном файле (`index.html`). Каждый раз, когда меняется адрес, меняется и представление в соответствии
 * с настройкой сервиса `$route`
 *
 * Дополнительно можно задать анимациючерез атрибут ngAnimate для анимации эффектов **enter**
 * и **leave**.
 *
 * @animations
 * enter - происходит только после того как изменилось содержимое ngView (когда DOM-элемент с новым видом вставляется в DOM)
 * leave - происходит только после того как изменилось содержимое ngView и до того как прежнее содержимое удалится из DOM
 *
 * @scope
 * @example
    <example module="ngView" animations="true">
      <file name="index.html">

          Choose:
          Moby |
          Moby: Ch1 |
          Gatsby |
          Gatsby: Ch4 |
          Scarlet Letter

$location.path() = {{$location.path()}}

$route.current.templateUrl = {{$route.current.templateUrl}}

$route.current.params = {{$route.current.params}}

$route.current.scope.name = {{$route.current.scope.name}}

$routeParams = {{$routeParams}}

      </file>

      <file name="book.html">

          controller: {{name}}

          Book Id: {{params.bookId}}

      </file>

      <file name="chapter.html">

          controller: {{name}}

          Book Id: {{params.bookId}}

          Chapter Id: {{params.chapterId}}

      </file>

      <file name="animations.css">
        .example-leave-setup, .example-enter-setup {
          -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
          transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
        }

        .example-animate-container {
          position:relative;
          height:100px;
        }

        .example-animate-container > * {
          display:block;
          width:100%;
          border-left:1px solid black;

          position:absolute;
          top:0;
          left:0;
          right:0;
          bottom:0;
          padding:10px;
        }

        .example-enter-setup {
          left:100%;
        }
        .example-enter-setup.example-enter-start {
          left:0;
        }

        .example-leave-setup { }
        .example-leave-setup.example-leave-start {
          left:-100%;
        }
      </file>

      <file name="script.js">
        angular.module('ngView', [], function($routeProvider, $locationProvider) {
          $routeProvider.when('/Book/:bookId', {
            templateUrl: 'book.html',
            controller: BookCntl
          });
          $routeProvider.when('/Book/:bookId/ch/:chapterId', {
            templateUrl: 'chapter.html',
            controller: ChapterCntl
          });

          // configure html5 to get links working on jsfiddle
          $locationProvider.html5Mode(true);
        });

        function MainCntl($scope, $route, $routeParams, $location) {
          $scope.$route = $route;
          $scope.$location = $location;
          $scope.$routeParams = $routeParams;
        }

        function BookCntl($scope, $routeParams) {
          $scope.name = "BookCntl";
          $scope.params = $routeParams;
        }

        function ChapterCntl($scope, $routeParams) {
          $scope.name = "ChapterCntl";
          $scope.params = $routeParams;
        }
      </file>

      <file name="scenario.js">
        it('should load and compile correct template', function() {
          element('a:contains("Moby: Ch1")').click();
          var content = element('.doc-example-live [ng-view]').text();
          expect(content).toMatch(/controller\: ChapterCntl/);
          expect(content).toMatch(/Book Id\: Moby/);
          expect(content).toMatch(/Chapter Id\: 1/);

          element('a:contains("Scarlet")').click();
          content = element('.doc-example-live [ng-view]').text();
          expect(content).toMatch(/controller\: BookCntl/);
          expect(content).toMatch(/Book Id\: Scarlet/);
        });
      </file>
    </example>
 */

/**
 * @ngdoc event
 * @name ng.directive:ngView#$viewContentLoaded
 * @eventOf ng.directive:ngView
 * @eventType emit on the current ngView scope(задействует текущую область видимости ngView)
 * @description
 * Задействуется каждый раз когда содержимое ngView перезагружается.
 */
var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile',
                       '$controller', '$animator',
               function($http,   $templateCache,   $route,   $anchorScroll,   $compile,
                        $controller,  $animator) {
  return {
    restrict: 'ECA',
    terminal: true,
    link: function(scope, element, attr) {
      var lastScope,
          onloadExp = attr.onload || '',
          animate = $animator(scope, attr);

      scope.$on('$routeChangeSuccess', update);
      update();

      function destroyLastScope() {
        if (lastScope) {
          lastScope.$destroy();
          lastScope = null;
        }
      }

      function clearContent() {
        animate.leave(element.contents(), element);
        destroyLastScope();
      }

      function update() {
        var locals = $route.current && $route.current.locals,
            template = locals && locals.$template;

        if (template) {
          clearContent();
          animate.enter(jqLite('

').html(template).contents(), element);

          var link = $compile(element.contents()),
              current = $route.current,
              controller;

          lastScope = current.scope = scope.$new();
          if (current.controller) {
            locals.$scope = lastScope;
            controller = $controller(current.controller, locals);
            element.children().data('$ngControllerController', controller);
          }

          link(lastScope);
          lastScope.$emit('$viewContentLoaded');
          lastScope.$eval(onloadExp);

          // $anchorScroll might listen on event...
          $anchorScroll();
        } else {
          clearContent();
        }
      }
    }
  };
}];
<|endoftext|>