<commit_before>/*globals */
'use strict';

var EventEmitter = require('eventemitter3')
  , collection = require('./collection')
  , AsyncAsset = require('async-asset')
  , Fortress = require('fortress')
  , async = require('./async')
  , val = require('parsifal')
  , sandbox
  , undef;

//
// Async Asset loader.
//
var assets = new AsyncAsset();

/**
 * Representation of a single pagelet.
 *
 * @constructor
 * @param {BigPipe} bigpipe The BigPipe instance that was created.
 * @api public
 */
function Pagelet(bigpipe) {
  if (!(this instanceof Pagelet)) return new Pagelet(bigpipe);

  //
  // Create one single Fortress instance that orchestrates all iframe based client
  // code. This sandbox variable should never be exposed to the outside world in
  // order to prevent leaking.
  //
  this.sandbox = sandbox = sandbox || new Fortress();
  this.bigpipe = bigpipe;
}

//
// Inherit from EventEmitter.
//
Pagelet.prototype = new EventEmitter();
Pagelet.prototype.constructor = Pagelet;

/**
 * Configure the Pagelet.
 *
 * @param {String} name The given name of the pagelet.
 * @param {Object} data The data of the pagelet.
 * @param {Array} roots HTML root elements search for targets.
 * @api private
 */
Pagelet.prototype.configure = function configure(name, data, roots) {
  var bigpipe = this.bigpipe
    , pagelet = this;

  //
  // Pagelet identification.
  //
  pagelet.id = data.id;                          // ID of the pagelet.
  pagelet.name = name;                           // Name of the pagelet.
  pagelet.css = collection.array(data.css);      // CSS for the Page.
  pagelet.js = collection.array(data.js);        // Dependencies for the page.
  pagelet.run = data.run;                        // Pagelet client code.
  pagelet.data = data.data;                      // All the template data.
  pagelet.mode = data.mode;                      // Fragment rendering mode.
  pagelet.container = pagelet.sandbox.create();  // Create an application sandbox.
  pagelet.timeout = data.timeout || 25 * 1000;   // Resource loading timeout.
  pagelet.error = data.error;                    // Error template.
  pagelet.client = data.client;                  // Client template.
  pagelet.loader = data.loader || '';            // Loading placeholder.
  pagelet.append = data.append || false;         // Append content to the container.

  //
  // This pagelet was actually part of a parent pagelet, so set a reference to
  // the parent pagelet that was loaded.
  //
  var parent = pagelet.parent = data.parent ? bigpipe.get(data.parent) : void 0;

  //
  // Locate all the placeholders for this given pagelet.
  //
  pagelet.placeholders = pagelet.$('data-pagelet', name, roots);

  //
  // Destroy the pagelet as we've been given the remove flag.
  // However do not destroy assets as unauthorized pagelets won't register
  // assets in the first place and they might be used by other pagelets.
  //
  if (data.remove) return pagelet.destroy({
    assets: false,
    remove: true
  });

  //
  // If we don't have any loading placeholders we want to scan the current
  // placeholders for content and assume that this content should be used when
  // the pagelet is loading or re-loading content. This also needs to be done
  // BEFORE we render the template from the server or we will capture the wrong
  // piece of HTML.
  //
  if (!pagelet.loader) collection.each(pagelet.placeholders, function each(node) {
    if (pagelet.loader) return false;

    var html = (node.innerHTML || '').replace(/^\s+|\s+$/g, '');
    if (html.length) pagelet.loader = html;

    return !pagelet.loader;
  });

  async.each(this.css.concat(this.js), function download(url, next) {
    assets.add(url, next);
  }, function done(err) {
    if (err) return pagelet.broadcast('error', err);

    pagelet.broadcast('loaded');
    pagelet.render(pagelet.parse());

    //
    // All resources are loaded, but we have a parent element. When the parent
    // element renders it will most likely also nuke our placeholder references
    // preventing us from rendering updates again.
    //
    if (parent) parent.on('render', function render() {
      pagelet.placeholders = pagelet.$('data-pagelet', pagelet.name, parent.placeholders);
      pagelet.render(pagelet.parse() || pagelet.data);
    });

    pagelet.initialize();
  }, { context: bigpipe, timeout: this.timeout });

  pagelet.broadcast('configured', data);
};

/**
 * Get the template for a given type. We currently only support `client` and
 * `error` as types.
 *
 * @param {String} type Template type
 * @returns {Function}
 * @api private
 */
Pagelet.prototype.template = function template(type) {
  type = type || 'client';

  return this.bigpipe.templates[type +'@'+ this.id];
};

/**
 * Load the required template by type. Either client or error.
 *
 * @param {String} type Client or Error
 * @param {Function} next Completion callback.
 * @api private
 */
Pagelet.prototype.load = function load(type, next) {
  type = ~['client', 'error'].indexOf(type) ? this[type] : this.client;
  if (!type) return next(new Error('Missing client-side template'));

  assets.add(type, next);
};

/**
 * Get a pagelet loaded on the page.
 *
 * @param {String} name Name of the pagelet we need.
 * @returns {Pagelet|Undefined}
 */
Pagelet.prototype.pagelet = function pagelet(name) {
  return this.bigpipe.get(name, this.name);
};

/**
 * The Pagelet's resource has all been loaded.
 *
 * @api private
 */
Pagelet.prototype.initialize = function initialise() {
  this.broadcast('initialize');

  //
  // Only load the client code in a sandbox when it exists. There no point in
  // spinning up a sandbox if it does nothing
  //
  if (!this.code) return;
  this.sandbox(this.prepare(this.code));
};

/**
 * Broadcast an event that will be emitted on the pagelet and the page.
 *
 * @param {String} event The name of the event we should emit
 * @returns {Pagelet}
 * @api public
 */
Pagelet.prototype.broadcast = function broadcast(event) {
  var pagelet = this;

  /**
   * Broadcast the event with namespaced name.
   *
   * @param {String} name Event name.
   * @returns {Pagelet}
   * @api private
   */
  function shout(name) {
    pagelet.bigpipe.emit.apply(pagelet.bigpipe, [
      name.join(':'),
      pagelet
    ].concat(Array.prototype.slice.call(arguments, 1)));

    return pagelet;
  }

  EventEmitter.prototype.emit.apply(this, arguments);

  if (this.parent) shout([this.parent.name, this.name, event]);
  return shout([this.name, event]);
};

/**
 * Check if the event we're about to emit is a reserved event and should be
 * blocked.
 *
 * @param {String} event Name of the event we want to emit
 * @returns {Boolean}
 * @api public
 */
Pagelet.prototype.reserved = function reserved(event) {
  return event in this.reserved.events;
};

/**
 * The events that are used internally.
 *
 * @type {Object}
 * @api private
 */
Pagelet.prototype.reserved.events = {
  configured: 1,    // Pagelet has been configured.
  error: 1,         // Something when wrong in the Pagelet.
  loaded: 1,        // All assets has been loaded.
  submit: 1,        // We've submitted a form.
  initialize: 1,    // Pagelet has been fully initialized, ready to go.
  render: 1,        // Pagelet has rendered new HTML.
  destroy: 1        // Pagelet has been destroyed.
};

/**
 * Find the element based on the attribute and value.
 *
 * @param {String} attribute The name of the attribute we're searching.
 * @param {String} value The value that the attribute should equal to.
 * @param {Array} root Optional array of root elements.
 * @returns {Array} A list of HTML elements that match.
 * @api public
 */
Pagelet.prototype.$ = function $(attribute, value, roots) {
  var elements = [];

  collection.each(roots || [document], function each(root) {
    if ('querySelectorAll' in root) return Array.prototype.push.apply(
      elements,
      root.querySelectorAll('['+ attribute +'="'+ value +'"]')
    );

    //
    // No querySelectorAll support, so we're going to do a full DOM scan in
    // order to search for attributes.
    //
    for (var all = root.getElementsByTagName('*'), i = 0, l = all.length; i < l; i++) {
      if (value === all[i].getAttribute(attribute)) {
        elements.push(all[i]);
      }
    }
  });

  return elements;
};

/**
 * Invoke the correct render method for the pagelet.
 *
 * @param {String|Object} html The HTML or data that needs to be rendered.
 * @returns {Boolean} Successfully rendered a pagelet.
 * @api public
 */
Pagelet.prototype.render = function render(html) {
  if (!this.placeholders.length) return false;

  var mode = this.mode in this ? this[this.mode] : this.html
    , template = this.template('client');

  //
  // We have been given an object instead of pure HTML so we are going to make
  // the assumption that this is data for the client side template and render
  // that our selfs. If no HTML is supplied we're going to use the data that has
  // been send to the client
  //
  if (
       'function' === collection.type(template)
    && (
      'object' === collection.type(html)
      || undef === html && 'object' === collection.type(this.data)
      || html instanceof Error
    )) {
    try {
      if (html instanceof Error) throw html; // So it's captured an processed as error
      html = template(collection.copy(html || {}, this.data || {}));
    }
    catch (e) {
      html = this.template('error')(collection.copy(html || {}, this.data || {}, {
        reason: 'Failed to render: '+ this.name,
        message: e.message,
        stack: e.stack,
        error: e
      }));
    }
  }

  collection.each(this.placeholders, function each(root) {
    mode.call(this, root, html);
  }, this);

  //
  // Register the name of the rendered pagelet as child pagelets
  // might be waiting for it. The length of the collection
  // is also used to keep track of the number of rendered pagelets.
  //
  this.bigpipe.rendered.push(this.name);
  this.broadcast('render', html);

  return true;
};

/**
 * Render the fragment as HTML (default).
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api public
 */
Pagelet.prototype.html = function html(root, content) {
  this.createElements(root, content);
};

/**
 * Render the fragment as SVG.
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api public
 */
Pagelet.prototype.svg = function svg(root, content) {
  this.createElements(root, content);
};

/**
 * Get the element namespaceURI description based on mode.
 *
 * @param {String} mode Mode the pagelet will be rendered in.
 * @return {String} Element namespace.
 * @api private
 */
Pagelet.prototype.getElementNS = function getElementNS(mode) {
  mode = mode.toLowerCase();

  switch(mode) {
    case 'svg': return 'http://www.w3.org/2000/svg';
    default: return 'http://www.w3.org/1999/xhtml';
  }
};

/**
 * Create elements by namespace and via a document fragment.
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api private
 */
Pagelet.prototype.createElements = function createElements(root, content) {
  var fragment = document.createDocumentFragment()
    , div = document.createElementNS(this.getElementNS(this.mode), 'div')
    , borked = this.bigpipe.IEV < 7;

  //
  // Clean out old HTML before we append our new HTML or we will get duplicate
  // DOM. Or there might have been a loading placeholder in place that needs
  // to be removed. If elements need to be appended only move the elements from
  // the root to the new fragment.
  //
  while (root.firstChild) {
    if (this.append) {
      fragment.appendChild(root.firstChild);
      continue;
    }

    root.removeChild(root.firstChild);
  }

  if (borked) root.appendChild(div);

  div.innerHTML = content;

  while (div.lastChild) {
    fragment.insertBefore(div.lastChild, fragment.firstChild);
  }

  root.appendChild(fragment);
  if (borked) root.removeChild(div);
};

/**
 * Parse the included template from the comment node so it can be injected in to
 * the page as initial rendered view.
 *
 * @returns {String} View.
 * @api private
 */
Pagelet.prototype.parse = function parse() {
  var node = this.$('data-pagelet-fragment', this.id)[0]
    , comment;

  //
  // The firstChild of the fragment should have been a HTML comment, this is to
  // prevent the browser from rendering and parsing the template.
  //
  if (!node.firstChild || node.firstChild.nodeType !== 8) return;

  comment = node.firstChild.nodeValue;

  return comment
    .substring(1, comment.length -1)
    .replace(/\\([\s\S]|$)/g, '$1');
};

/**
 * Set the pagelet in a loading state.
 *
 * @param {Boolean} unloading We're not loading, but unloading.
 * @returns {Pagelet}
 * @api public
 */
Pagelet.prototype.loading = function loading(unloading) {
  if (!unloading) this.render(
    'function' !== typeof this.loader
      ? this.loader || ''
      : this.loader()
  );

  collection.each(this.placeholders, !unloading ? function add(node) {
    var className = (node.className || '').split(' ');

    if (!~collection.index(className, 'loading')) {
      className.push('loading');
      node.className = className.join(' ');
    }

    node.style.cursor = 'wait';
  } : function remove(node) {
    var className = (node.className || '').split(' ')
      , index = collection.index(className, 'loading');

    if (~index) {
      className.splice(index, 1);
      node.className = className.join(' ');
    }

    node.style.cursor = '';
  });

  return this;
};

/**
 * Destroy the pagelet and clean up all references so it can be re-used again in
 * the future.
 *
 * Options:
 *
 * - assets: Also remove assets, true by default can be set to false to keep.
 * - remove: Remove the DOM node after deletion.
 *
 * @param {Object} options Destruction information.
 * @api public
 */
Pagelet.prototype.destroy = function destroy(options) {
  var pagelet = this;

  options = options || {};

  //
  // Execute any extra destroy hooks. This needs to be done before we remove any
  // elements or destroy anything as there might people subscribed to these
  // events.
  //
  this.broadcast('destroy', options);

  //
  // Remove all the HTML from the placeholders.
  //
  if (this.placeholders) collection.each(this.placeholders, function remove(root) {
    if (options.remove && root.parentNode) root.parentNode.removeChild(root);
    else while (root.firstChild) root.removeChild(root.firstChild);
  });

  //
  // Remove the sandboxing and prevent element leaking by deferencing them.
  //
  if (this.container) sandbox.kill(this.container.id);
  this.placeholders = this.container = null;

  //
  // Remove the CSS and JS assets.
  //
  if (options.assets !== false) {
    collection.each(this.css.concat(this.js), function remove(url) {
      assets.remove(url);
    });
  }

  //
  // Everything has been cleaned up, release it to our free list Pagelet pool.
  //
  this.bigpipe.free(this);

  return this;
};

//
// Expose the module.
//
module.exports = Pagelet;
<commit_msg>[minor] Remove pointless empty comment<commit_after>'use strict';

var EventEmitter = require('eventemitter3')
  , collection = require('./collection')
  , AsyncAsset = require('async-asset')
  , Fortress = require('fortress')
  , async = require('./async')
  , val = require('parsifal')
  , sandbox
  , undef;

//
// Async Asset loader.
//
var assets = new AsyncAsset();

/**
 * Representation of a single pagelet.
 *
 * @constructor
 * @param {BigPipe} bigpipe The BigPipe instance that was created.
 * @api public
 */
function Pagelet(bigpipe) {
  if (!(this instanceof Pagelet)) return new Pagelet(bigpipe);

  //
  // Create one single Fortress instance that orchestrates all iframe based client
  // code. This sandbox variable should never be exposed to the outside world in
  // order to prevent leaking.
  //
  this.sandbox = sandbox = sandbox || new Fortress();
  this.bigpipe = bigpipe;
}

//
// Inherit from EventEmitter.
//
Pagelet.prototype = new EventEmitter();
Pagelet.prototype.constructor = Pagelet;

/**
 * Configure the Pagelet.
 *
 * @param {String} name The given name of the pagelet.
 * @param {Object} data The data of the pagelet.
 * @param {Array} roots HTML root elements search for targets.
 * @api private
 */
Pagelet.prototype.configure = function configure(name, data, roots) {
  var bigpipe = this.bigpipe
    , pagelet = this;

  //
  // Pagelet identification.
  //
  pagelet.id = data.id;                          // ID of the pagelet.
  pagelet.name = name;                           // Name of the pagelet.
  pagelet.css = collection.array(data.css);      // CSS for the Page.
  pagelet.js = collection.array(data.js);        // Dependencies for the page.
  pagelet.run = data.run;                        // Pagelet client code.
  pagelet.data = data.data;                      // All the template data.
  pagelet.mode = data.mode;                      // Fragment rendering mode.
  pagelet.container = pagelet.sandbox.create();  // Create an application sandbox.
  pagelet.timeout = data.timeout || 25 * 1000;   // Resource loading timeout.
  pagelet.error = data.error;                    // Error template.
  pagelet.client = data.client;                  // Client template.
  pagelet.loader = data.loader || '';            // Loading placeholder.
  pagelet.append = data.append || false;         // Append content to the container.

  //
  // This pagelet was actually part of a parent pagelet, so set a reference to
  // the parent pagelet that was loaded.
  //
  var parent = pagelet.parent = data.parent ? bigpipe.get(data.parent) : void 0;

  //
  // Locate all the placeholders for this given pagelet.
  //
  pagelet.placeholders = pagelet.$('data-pagelet', name, roots);

  //
  // Destroy the pagelet as we've been given the remove flag.
  // However do not destroy assets as unauthorized pagelets won't register
  // assets in the first place and they might be used by other pagelets.
  //
  if (data.remove) return pagelet.destroy({
    assets: false,
    remove: true
  });

  //
  // If we don't have any loading placeholders we want to scan the current
  // placeholders for content and assume that this content should be used when
  // the pagelet is loading or re-loading content. This also needs to be done
  // BEFORE we render the template from the server or we will capture the wrong
  // piece of HTML.
  //
  if (!pagelet.loader) collection.each(pagelet.placeholders, function each(node) {
    if (pagelet.loader) return false;

    var html = (node.innerHTML || '').replace(/^\s+|\s+$/g, '');
    if (html.length) pagelet.loader = html;

    return !pagelet.loader;
  });

  async.each(this.css.concat(this.js), function download(url, next) {
    assets.add(url, next);
  }, function done(err) {
    if (err) return pagelet.broadcast('error', err);

    pagelet.broadcast('loaded');
    pagelet.render(pagelet.parse());

    //
    // All resources are loaded, but we have a parent element. When the parent
    // element renders it will most likely also nuke our placeholder references
    // preventing us from rendering updates again.
    //
    if (parent) parent.on('render', function render() {
      pagelet.placeholders = pagelet.$('data-pagelet', pagelet.name, parent.placeholders);
      pagelet.render(pagelet.parse() || pagelet.data);
    });

    pagelet.initialize();
  }, { context: bigpipe, timeout: this.timeout });

  pagelet.broadcast('configured', data);
};

/**
 * Get the template for a given type. We currently only support `client` and
 * `error` as types.
 *
 * @param {String} type Template type
 * @returns {Function}
 * @api private
 */
Pagelet.prototype.template = function template(type) {
  type = type || 'client';

  return this.bigpipe.templates[type +'@'+ this.id];
};

/**
 * Load the required template by type. Either client or error.
 *
 * @param {String} type Client or Error
 * @param {Function} next Completion callback.
 * @api private
 */
Pagelet.prototype.load = function load(type, next) {
  type = ~['client', 'error'].indexOf(type) ? this[type] : this.client;
  if (!type) return next(new Error('Missing client-side template'));

  assets.add(type, next);
};

/**
 * Get a pagelet loaded on the page.
 *
 * @param {String} name Name of the pagelet we need.
 * @returns {Pagelet|Undefined}
 */
Pagelet.prototype.pagelet = function pagelet(name) {
  return this.bigpipe.get(name, this.name);
};

/**
 * The Pagelet's resource has all been loaded.
 *
 * @api private
 */
Pagelet.prototype.initialize = function initialise() {
  this.broadcast('initialize');

  //
  // Only load the client code in a sandbox when it exists. There no point in
  // spinning up a sandbox if it does nothing
  //
  if (!this.code) return;
  this.sandbox(this.prepare(this.code));
};

/**
 * Broadcast an event that will be emitted on the pagelet and the page.
 *
 * @param {String} event The name of the event we should emit
 * @returns {Pagelet}
 * @api public
 */
Pagelet.prototype.broadcast = function broadcast(event) {
  var pagelet = this;

  /**
   * Broadcast the event with namespaced name.
   *
   * @param {String} name Event name.
   * @returns {Pagelet}
   * @api private
   */
  function shout(name) {
    pagelet.bigpipe.emit.apply(pagelet.bigpipe, [
      name.join(':'),
      pagelet
    ].concat(Array.prototype.slice.call(arguments, 1)));

    return pagelet;
  }

  EventEmitter.prototype.emit.apply(this, arguments);

  if (this.parent) shout([this.parent.name, this.name, event]);
  return shout([this.name, event]);
};

/**
 * Check if the event we're about to emit is a reserved event and should be
 * blocked.
 *
 * @param {String} event Name of the event we want to emit
 * @returns {Boolean}
 * @api public
 */
Pagelet.prototype.reserved = function reserved(event) {
  return event in this.reserved.events;
};

/**
 * The events that are used internally.
 *
 * @type {Object}
 * @api private
 */
Pagelet.prototype.reserved.events = {
  configured: 1,    // Pagelet has been configured.
  error: 1,         // Something when wrong in the Pagelet.
  loaded: 1,        // All assets has been loaded.
  submit: 1,        // We've submitted a form.
  initialize: 1,    // Pagelet has been fully initialized, ready to go.
  render: 1,        // Pagelet has rendered new HTML.
  destroy: 1        // Pagelet has been destroyed.
};

/**
 * Find the element based on the attribute and value.
 *
 * @param {String} attribute The name of the attribute we're searching.
 * @param {String} value The value that the attribute should equal to.
 * @param {Array} root Optional array of root elements.
 * @returns {Array} A list of HTML elements that match.
 * @api public
 */
Pagelet.prototype.$ = function $(attribute, value, roots) {
  var elements = [];

  collection.each(roots || [document], function each(root) {
    if ('querySelectorAll' in root) return Array.prototype.push.apply(
      elements,
      root.querySelectorAll('['+ attribute +'="'+ value +'"]')
    );

    //
    // No querySelectorAll support, so we're going to do a full DOM scan in
    // order to search for attributes.
    //
    for (var all = root.getElementsByTagName('*'), i = 0, l = all.length; i < l; i++) {
      if (value === all[i].getAttribute(attribute)) {
        elements.push(all[i]);
      }
    }
  });

  return elements;
};

/**
 * Invoke the correct render method for the pagelet.
 *
 * @param {String|Object} html The HTML or data that needs to be rendered.
 * @returns {Boolean} Successfully rendered a pagelet.
 * @api public
 */
Pagelet.prototype.render = function render(html) {
  if (!this.placeholders.length) return false;

  var mode = this.mode in this ? this[this.mode] : this.html
    , template = this.template('client');

  //
  // We have been given an object instead of pure HTML so we are going to make
  // the assumption that this is data for the client side template and render
  // that our selfs. If no HTML is supplied we're going to use the data that has
  // been send to the client
  //
  if (
       'function' === collection.type(template)
    && (
      'object' === collection.type(html)
      || undef === html && 'object' === collection.type(this.data)
      || html instanceof Error
    )) {
    try {
      if (html instanceof Error) throw html; // So it's captured an processed as error
      html = template(collection.copy(html || {}, this.data || {}));
    }
    catch (e) {
      html = this.template('error')(collection.copy(html || {}, this.data || {}, {
        reason: 'Failed to render: '+ this.name,
        message: e.message,
        stack: e.stack,
        error: e
      }));
    }
  }

  collection.each(this.placeholders, function each(root) {
    mode.call(this, root, html);
  }, this);

  //
  // Register the name of the rendered pagelet as child pagelets
  // might be waiting for it. The length of the collection
  // is also used to keep track of the number of rendered pagelets.
  //
  this.bigpipe.rendered.push(this.name);
  this.broadcast('render', html);

  return true;
};

/**
 * Render the fragment as HTML (default).
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api public
 */
Pagelet.prototype.html = function html(root, content) {
  this.createElements(root, content);
};

/**
 * Render the fragment as SVG.
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api public
 */
Pagelet.prototype.svg = function svg(root, content) {
  this.createElements(root, content);
};

/**
 * Get the element namespaceURI description based on mode.
 *
 * @param {String} mode Mode the pagelet will be rendered in.
 * @return {String} Element namespace.
 * @api private
 */
Pagelet.prototype.getElementNS = function getElementNS(mode) {
  mode = mode.toLowerCase();

  switch(mode) {
    case 'svg': return 'http://www.w3.org/2000/svg';
    default: return 'http://www.w3.org/1999/xhtml';
  }
};

/**
 * Create elements by namespace and via a document fragment.
 *
 * @param {Element} root Container.
 * @param {String} content Fragment content.
 * @api private
 */
Pagelet.prototype.createElements = function createElements(root, content) {
  var fragment = document.createDocumentFragment()
    , div = document.createElementNS(this.getElementNS(this.mode), 'div')
    , borked = this.bigpipe.IEV < 7;

  //
  // Clean out old HTML before we append our new HTML or we will get duplicate
  // DOM. Or there might have been a loading placeholder in place that needs
  // to be removed. If elements need to be appended only move the elements from
  // the root to the new fragment.
  //
  while (root.firstChild) {
    if (this.append) {
      fragment.appendChild(root.firstChild);
      continue;
    }

    root.removeChild(root.firstChild);
  }

  if (borked) root.appendChild(div);

  div.innerHTML = content;

  while (div.lastChild) {
    fragment.insertBefore(div.lastChild, fragment.firstChild);
  }

  root.appendChild(fragment);
  if (borked) root.removeChild(div);
};

/**
 * Parse the included template from the comment node so it can be injected in to
 * the page as initial rendered view.
 *
 * @returns {String} View.
 * @api private
 */
Pagelet.prototype.parse = function parse() {
  var node = this.$('data-pagelet-fragment', this.id)[0]
    , comment;

  //
  // The firstChild of the fragment should have been a HTML comment, this is to
  // prevent the browser from rendering and parsing the template.
  //
  if (!node.firstChild || node.firstChild.nodeType !== 8) return;

  comment = node.firstChild.nodeValue;

  return comment
    .substring(1, comment.length -1)
    .replace(/\\([\s\S]|$)/g, '$1');
};

/**
 * Set the pagelet in a loading state.
 *
 * @param {Boolean} unloading We're not loading, but unloading.
 * @returns {Pagelet}
 * @api public
 */
Pagelet.prototype.loading = function loading(unloading) {
  if (!unloading) this.render(
    'function' !== typeof this.loader
      ? this.loader || ''
      : this.loader()
  );

  collection.each(this.placeholders, !unloading ? function add(node) {
    var className = (node.className || '').split(' ');

    if (!~collection.index(className, 'loading')) {
      className.push('loading');
      node.className = className.join(' ');
    }

    node.style.cursor = 'wait';
  } : function remove(node) {
    var className = (node.className || '').split(' ')
      , index = collection.index(className, 'loading');

    if (~index) {
      className.splice(index, 1);
      node.className = className.join(' ');
    }

    node.style.cursor = '';
  });

  return this;
};

/**
 * Destroy the pagelet and clean up all references so it can be re-used again in
 * the future.
 *
 * Options:
 *
 * - assets: Also remove assets, true by default can be set to false to keep.
 * - remove: Remove the DOM node after deletion.
 *
 * @param {Object} options Destruction information.
 * @api public
 */
Pagelet.prototype.destroy = function destroy(options) {
  var pagelet = this;

  options = options || {};

  //
  // Execute any extra destroy hooks. This needs to be done before we remove any
  // elements or destroy anything as there might people subscribed to these
  // events.
  //
  this.broadcast('destroy', options);

  //
  // Remove all the HTML from the placeholders.
  //
  if (this.placeholders) collection.each(this.placeholders, function remove(root) {
    if (options.remove && root.parentNode) root.parentNode.removeChild(root);
    else while (root.firstChild) root.removeChild(root.firstChild);
  });

  //
  // Remove the sandboxing and prevent element leaking by deferencing them.
  //
  if (this.container) sandbox.kill(this.container.id);
  this.placeholders = this.container = null;

  //
  // Remove the CSS and JS assets.
  //
  if (options.assets !== false) {
    collection.each(this.css.concat(this.js), function remove(url) {
      assets.remove(url);
    });
  }

  //
  // Everything has been cleaned up, release it to our free list Pagelet pool.
  //
  this.bigpipe.free(this);

  return this;
};

//
// Expose the module.
//
module.exports = Pagelet;
<|endoftext|>


<commit_before>import passport from 'passport';
import passportLocal from 'passport-local';
import { OAuth2Strategy } from 'passport-google-oauth';
import { User } from '../data/models';
import config from '../config';
import {
  ACTIVE,
  PENDING,
} from '../constants/userStatus';

/* eslint-disable */
const LocalStrategy = passportLocal.Strategy;
passport.use(
  'google',
  new OAuth2Strategy(
    {
      clientID: config.google.clientID,
      clientSecret: config.google.clientSecret,
      callbackURL: config.google.callbackURL,
      passReqToCallback: true,
    },
    (req, accessToken, refreshToken, profile, done) => {
      process.nextTick(async () => {
        if (!req.user) {
          const email = (profile.emails[0].value || '').toLowerCase();
          try {
            const userWithEmail = await User.findOne({ email });

            if (userWithEmail && userWithEmail.status === PENDING) {
              User.findOne({ email }, (err, user) => {
                if (err) {
                  return done(err);
                }
                if (user) {
                  const u = user;
                  u.google.id = profile.id;
                  u.name = profile.displayName;
                  u.google.token = accessToken;
                  u.google.refreshToken = refreshToken;
                  u.status = ACTIVE;
                  u.save((error) => {
                    if (error) {
                      return done(null, false, {
                        success: false,
                        message: error,
                      });
                    }
                    return done(null, {
                      success: true,
                      message: 'Successfully Logged In',
                      user,
                    });
                  });
                }
              });
            } else {
              User.findOne(
                {
                  'google.id': profile.id,
                },
                (err, user) => {
                  if (err) {
                    return done(err);
                  }
                  if (user) {
                    const u = user;
                    u.google.id = profile.id;
                    u.google.token = accessToken;
                    u.google.refreshToken = refreshToken;
                    u.name = profile.displayName;
                    u.status = ACTIVE;
                    u.save((error) => {
                      if (error) {
                        return done(null, false, {
                          success: false,
                          message: error,
                        });
                      }
                      return done(null, {
                        success: true,
                        message: 'Successfully Logged In',
                        user,
                      });
                    });
                  }

                  const newUser = new User();
                  newUser.status = ACTIVE;
                  newUser.google.id = profile.id;
                  newUser.google.token = accessToken;
                  newUser.google.refreshToken = refreshToken;
                  newUser.name = profile.displayName;
                  newUser.email = (profile.emails[0].value || '').toLowerCase();
                  newUser.save((error) => {
                    if (error) {
                      return done(error);
                    }
                    return done(null, {
                      success: true,
                      message: 'Successfully Logged In',
                      user: newUser,
                    });
                  });
                },
              );
            }
          } catch (error) {
            return done(error);
          }
        } else {
          const user = req.user;
          user.status = ACTIVE;
          user.google.token = profile.id;
          user.google.token = accessToken;
          user.google.refreshToken = refreshToken;
          user.name = profile.displayName;
          user.email = (profile.emails[0].value || '').toLowerCase();
          user.save((err) => {
            if (err) {
              return done(err);
            }

            return done(null, {
              success: true,
              message: 'Successfully Logged In',
              user,
            });
          });
        }
      });
    },
  ),
);

passport.serializeUser((req, done) => {
  done(null, req.user._id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, (err, req) => {
    done(err, req);
  });
});
<commit_msg>refactor auth<commit_after>import passport from 'passport';
import passportLocal from 'passport-local';
import { OAuth2Strategy } from 'passport-google-oauth';
import { User } from '../data/models';
import config from '../config';
import {
  ACTIVE,
  PENDING,
} from '../constants/userStatus';

/* eslint-disable */
const LocalStrategy = passportLocal.Strategy;
passport.use(
  'google',
  new OAuth2Strategy(
    {
      clientID: config.google.clientID,
      clientSecret: config.google.clientSecret,
      callbackURL: config.google.callbackURL,
      passReqToCallback: true,
    },
    (req, accessToken, refreshToken, profile, done) => {
      const { displayName, id, emails } = profile;
      const email = (emails[0].value || '').toLowerCase();
      process.nextTick(async () => {
        if (!req.user) {
          try {
            const userWithEmail = await User.findOne({ email });
            if (userWithEmail && userWithEmail.status === PENDING) {
              const u = userWithEmail;
              u.google.id = id;
              u.name = displayName;
              u.google.token = accessToken;
              u.google.refreshToken = refreshToken;
              u.email = email;
              u.status = ACTIVE;
              await u.save();
              return done(null, {
                success: true,
                message: 'Successfully Logged In',
                user: u,
              });

            } else {

              const user = await User.findOne({ 'google.id': profile.id });
              let returnUser = user;
              if (user) {
                if (!user.google.token) {
                  const u = user;
                  u.google.id = profile.id;
                  u.google.token = accessToken;
                  u.google.refreshToken = refreshToken;
                  u.name = profile.displayName;
                  u.status = ACTIVE;
                  u.email = email;
                  returnUser = await u.save();
                }
              } else {
                  const newUser = new User();
                  newUser.status = ACTIVE;
                  newUser.google.id = profile.id;
                  newUser.google.token = accessToken;
                  newUser.google.refreshToken = refreshToken;
                  newUser.name = profile.displayName;
                  newUser.email = email;
                  returnUser = await newUser.save();

              }
              return done(null, {
                success: true,
                message: 'Successfully Logged In',
                user: returnUser,
              });

            }
          } catch (error) {
              return done(null, false, {
                success: false,
                message: error,
              });
          }
        } else {
          try {
            const user = req.user;
            user.status = ACTIVE;
            user.google.token = profile.id;
            user.google.token = accessToken;
            user.google.refreshToken = refreshToken;
            user.name = profile.displayName;
            user.email = email;
            await user.save();
            return done(null, {
              success: true,
              message: 'Successfully Logged In',
              user,
            });

          } catch (err) {
              return done(null, false, {
                success: false,
                message: error,
              });
          }

        }
      });
    },
  ),
);

passport.serializeUser((req, done) => {
  done(null, req.user._id);
});

passport.deserializeUser((id, done) => {
  User.findById(id, (err, req) => {
    done(err, req);
  });
});
<|endoftext|>


<commit_before>var config = require('./config')
var mongoOp = require('./model/mongo')
var stringProcessor = require('./stringprocessor')
var dates = require('./dates')

// Example of promisifying
// function doSomething(input) {
//   console.log('hello' + input)
//   return Q.resolve();
// }

// const addReminder = (sendTo, familyId) => {
//
//   return Q.ninvoke(mongoOp.FamilyMembers, 'findOne', { 'name': sendTo, 'familyId': familyId }) //wraps n a promise
//   .catch(() => Q.reject('@' + sendTo + ' unkown sorry! 😕'))
//   //.then(doSomething) // automatically sends the result (the second parameter) to the function doSomething
//   .then(familyMember => {
//     return Q.ninvoke(mongoOp.Lists, 'findOne', {'listKey': config.remindersListKey, 'familyId': familyId})
//         .catch(() => Q.reject('second error')) // 'Second error' will be the actual error
//       })
// }

function addReminder (inputText, familyId, timeZone, callback) {
  var sendTo = stringProcessor.getFirstWord(inputText)
  var dateText = stringProcessor.removeFirstWord(inputText)
  dateText = dateText.replace('weekend', 'saturday')
  var generalError = 'Error creating reminder list 😦'

  // Q.ninvoke(mongoOp.FamilyMembers, 'findOne', { 'name': sendTo, 'familyId': familyId }) //wraps n a promise
  // .catch(() => Q.reject('@' + sendTo + ' unkown sorry! 😕'))
  // .then(doSomething) // automatically sends the result (the second parameter) to the function doSomething
  // .then(familyMember => {
  //
  //   return Q.ninvoke(mongoOp.Lists, 'findOne', {'listKey': config.remindersListKey, 'familyId': familyId})
  //       .catch(() => Q.reject('second error')) // 'Second error' will be the actual error
  //     })
  // .catch(err => {
  //   callback(err)
  // })

  mongoOp.FamilyMembers.findOne({ 'name': sendTo, 'familyId': familyId }, function (err, familyMember) {
    if ((familyMember == null || err) && (sendTo !== 'all')) {
      callback('@' + sendTo + ' unkown in this family sorry! 😕')
    } else {
      var sendToId = familyMember == null ? config.allFamilyMembersID : familyMember.userId

      // Check lists and create if necessary
      mongoOp.Lists.findOne({'listKey': config.remindersListKey, 'familyId': familyId}, function (err, list) {
        if (err) callback(generalError)
        if (list == null) {
          var newList = new mongoOp.Lists({
            'listKey': config.remindersListKey,
            'listDescription': config.remindersListKey,
            'familyId': familyId
          })
          newList.save(function (err, data) {
            if (err) callback(generalError)
          })
        }
        dates.processDateAndTitleFromText(dateText, timeZone, function (err, date, localDateText, title) {
          if (err) {
            callback(err)
            return
          }

          if (title == null || title === '') {
            callback('Sorry need a reminder desciption 😦\nJust add one to the end.')
            return
          }

          var createListItem = function (listItem) {
            listItem.save(function (err, data) {
              if (err) callback('Error adding reminder 😦.')
              else {
                console.log('----reminder saved: ' + inputText + ' Sherlocked:' + date + ' < ' + title)
                callback(null, 'Set for ' + localDateText + '.')
              }
            })
          }

          if (title.charAt(0) === '#') {
            var listName = stringProcessor.getFirstWord(title).substr(1)
            var updatedTitle = stringProcessor.removeFirstWord(title)

            mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, function (err, list) {
              if (err || list == null) {
                callback('Could not find #' + listName + ' 😦.')
              } else {
                var newListItem = new mongoOp.ListItems({
                  'listKey': config.remindersListKey,
                  'listItemName': '@' + sendTo + ' ' + title + ' ' + localDateText,
                  'familyId': familyId,
                  'reminderWhen': date,
                  'reminderUserId': sendToId,
                  'reminderTitle': updatedTitle,
                  'reminderListKey': list.listKey
                })
                createListItem(newListItem)
              }
            })
          } else {
            var newListItem = new mongoOp.ListItems({
              'listKey': config.remindersListKey,
              'listItemName': '@' + sendTo + ' ' + title + ' ' + localDateText,
              'familyId': familyId,
              'reminderWhen': date,
              'reminderUserId': sendToId,
              'reminderTitle': title
            })
            createListItem(newListItem)
          }
        })
      })
    }
  })
}

module.exports = {
  addReminder
}
<commit_msg>scheduler<commit_after>var config = require('./config')
var mongoOp = require('./model/mongo')
var stringProcessor = require('./stringprocessor')
var dates = require('./dates')

// Example of promisifying
// function doSomething(input) {
//   console.log('hello' + input)
//   return Q.resolve();
// }

// const addReminder = (sendTo, familyId) => {
//
//   return Q.ninvoke(mongoOp.FamilyMembers, 'findOne', { 'name': sendTo, 'familyId': familyId }) //wraps n a promise
//   .catch(() => Q.reject('@' + sendTo + ' unkown sorry! 😕'))
//   //.then(doSomething) // automatically sends the result (the second parameter) to the function doSomething
//   .then(familyMember => {
//     return Q.ninvoke(mongoOp.Lists, 'findOne', {'listKey': config.remindersListKey, 'familyId': familyId})
//         .catch(() => Q.reject('second error')) // 'Second error' will be the actual error
//       })
// }

function addReminder (inputText, familyId, timeZone, callback) {
  var sendTo = stringProcessor.getFirstWord(inputText)
  var dateText = stringProcessor.removeFirstWord(inputText)
  dateText = dateText.replace('weekend', 'saturday')
  var generalError = 'Error creating reminder list 😦'

  // Q.ninvoke(mongoOp.FamilyMembers, 'findOne', { 'name': sendTo, 'familyId': familyId }) //wraps n a promise
  // .catch(() => Q.reject('@' + sendTo + ' unkown sorry! 😕'))
  // .then(doSomething) // automatically sends the result (the second parameter) to the function doSomething
  // .then(familyMember => {
  //
  //   return Q.ninvoke(mongoOp.Lists, 'findOne', {'listKey': config.remindersListKey, 'familyId': familyId})
  //       .catch(() => Q.reject('second error')) // 'Second error' will be the actual error
  //     })
  // .catch(err => {
  //   callback(err)
  // })

  mongoOp.FamilyMembers.findOne({ 'name': sendTo, 'familyId': familyId }, function (err, familyMember) {
    if ((familyMember == null || err) && (sendTo !== 'all')) {
      callback('@' + sendTo + ' unkown in this family sorry! 😕')
    } else {
      var sendToId = familyMember == null ? config.allFamilyMembersID : familyMember.userId

      // Check lists and create if necessary
      mongoOp.Lists.findOne({'listKey': config.remindersListKey, 'familyId': familyId}, function (err, list) {
        if (err) callback(generalError)
        if (list == null) {
          var newList = new mongoOp.Lists({
            'listKey': config.remindersListKey,
            'listDescription': config.remindersListKey,
            'familyId': familyId
          })
          newList.save(function (err, data) {
            if (err) callback(generalError)
          })
        }
        dates.processDateAndTitleFromText(dateText, timeZone, function (err, date, localDateText, title) {
          if (err) {
            callback(err)
            return
          }

          if (title == null || title === '') {
            callback('Sorry need a reminder desciption 😦\nJust add one to the end.')
            return
          }

          var createListItem = function (listItem) {
            listItem.save(function (err, data) {
              if (err) callback('Error adding reminder 😦.')
              else {
                console.log('----reminder saved: ' + inputText + ' Sherlocked:' + date + ' < ' + title)
                callback(null, 'Set for ' + localDateText + '.')
              }
            })
          }
          console.log('TEST10:' + title.charAt(0) + '--:' + title)
          if (title.charAt(0) === '#') {
            var listName = stringProcessor.getFirstWord(title).substr(1)
            var updatedTitle = stringProcessor.removeFirstWord(title)
            console.log('TEST11:' + listName + '--:' + updatedTitle)

            mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, function (err, list) {
              if (err || list == null) {
                callback('Could not find #' + listName + ' 😦.')
              } else {
                var newListItem = new mongoOp.ListItems({
                  'listKey': config.remindersListKey,
                  'listItemName': '@' + sendTo + ' ' + title + ' ' + localDateText,
                  'familyId': familyId,
                  'reminderWhen': date,
                  'reminderUserId': sendToId,
                  'reminderTitle': updatedTitle,
                  'reminderListKey': list.listKey
                })
                createListItem(newListItem)
              }
            })
          } else {
            var newListItem = new mongoOp.ListItems({
              'listKey': config.remindersListKey,
              'listItemName': '@' + sendTo + ' ' + title + ' ' + localDateText,
              'familyId': familyId,
              'reminderWhen': date,
              'reminderUserId': sendToId,
              'reminderTitle': title
            })
            createListItem(newListItem)
          }
        })
      })
    }
  })
}

module.exports = {
  addReminder
}
<|endoftext|>


<commit_before>var View = require("ampersand-view");
var State = require("ampersand-state");
var Collection = require("ampersand-collection");

var template = ["
",
	"<input type=\"file\" style=\"visibility:hidden;width:0;height:0;\"/>",
	"",
	"

",
"
"].join("");

var fileTemplate = ["
",
	"",
	"",
	"",
	"

",
	"<button data-hook=\"remove\">Remove</button>",
"
"].join("");

var FileState = State.extend({
	initialize: function(file){
		this.set("file", file);
	},
	props: {
		size: "number",
		name: "text",
		type: "text",
		file: "any",
		preview: "string"
	}
});

var FilesCollection = Collection.extend({
	model: FileState,
	toFiles: function() {
		return this.map(function(fileModel) {
			return fileModel.file;
		});
	}
});

var FileView = View.extend({
	template: fileTemplate,
	initialize: function(opts) {
		var self = this;
		self.displayPreview = opts.displayPreview;
		self.fileSizeUnit = opts.fileSizeUnit;
		var file = self.model.file;

		if (this.displayPreview && /image/.test(file.type)) {
			var reader = new FileReader();
			reader.onloadend = function() {
				self.model.preview = reader.result;
			};
			reader.readAsDataURL(file);
		}
	},
	events: {
		"click [data-hook=remove]": "removeFile",
	},
	bindings: {
		"fileSizeUnit": {
			type: "text",
			hook: "size-unit"
		},
		"fileSize": {
			type: "text",
			hook: "size"
		},
		"model.name": {
			type: "text",
			hook: "name"
		},
		"model.type": {
			type: "text",
			hook: "type"
		},
		"model.preview": [
			{
				type: "toggle",
				selector: "img"
			},
			{
				type: "attribute",
				selector: "img",
				name: "src"
			}
		]
	},
	props: {
		displayPreview: {
			type: "boolean"
		},
		fileSizeUnit: {
			type: "string"
		}
	},
	derived: {
		fileSize: {
			deps: ["model.size", "fileSizeUnit"],
			fn: function() {
				var number = this.model.size;
				var exp = 0;

				switch(this.fileSizeUnit.toUpperCase()) {
					case "KILOBYTE":
					case "KB":
						exp = 10;
						break;
					case "MEGABYTE":
					case "MB":
						exp = 20;
						break;
					case "GIGABYTE":
					case "GB":
						exp = 30;
						break;
					default:
						exp = 0;
				}

				return (number / Math.pow(2,exp)).toFixed(2);
			}
		}
	},
	removeFile: function(event) {
		event.preventDefault();
		event.stopImmediatePropagation();
		this.collection.remove(this.model);
	}
});

function eventNoOp(event) {
	event.stopPropagation();
	event.preventDefault();
}

module.exports = View.extend({
	template: template,
	initialize: function() {
		var self = this;

		this.files.on("add remove", function() {
			self.getValue();
			//self.valid;
			if (self.parent) {
				self.parent.update(self);
			}
		});
	},
	events: {
		"click [data-hook=drop-zone]": "simulateInputClick",
		"change input[type=file]": "handleFileInput",
		"dragenter [data-hook=drop-zone]": "dragEnter",
		"dragover [data-hook=drop-zone]": "dragOver",
		"drop [data-hook=drop-zone]": "drop"
	},
	bindings: {
		name: {
			type: "attribute",
			selector: "input[type=file]",
			name: "name"
		},
		label: [
			{
				type: "toggle",
				hook: "label"
			},
			{
				type: "text",
				hook: "label"
			}
		],
		holderClass: {
			type: "class",
			hook: "drop-zone"
		},
		multiple: {
			type: "booleanAttribute",
			selector: "input[type=file]",
			name: "multiple"
		},
		accept: {
			type: "attribute",
			selector: "input[type=file]",
			name: "accept"
		}
	},
	props: {
		holderClass: {
			type: "string"
		},
		label: {
			type: "string",
			default: "Drag and drop a file"
		},
		value: {
			type: "any"
		},
		required: {
			type: "boolean",
			default: true
		},
		multiple: {
			type: "boolean",
			default: false
		},
		name: {
			type: "string",
			required: true
		},
		accept: {
			type: "string",
			default: "*/*"
		},
		tests: {
			type: "array",
			default: function() {
				return [];
			}
		},
		fileSizeUnit: {
			type: "string",
			default: "KB"
		},
		displayPreviews: {
			type: "boolean",
			default: true
		}
	},
	derived: {
		acceptArray: {
			deps: ["accept"],
			fn: function() {
				var accept;
				if (typeof this.accept === "string") {
					accept = this.accept;
				} else if (this.accept === true) {
					accept = "*/*";
				} else {
					accept = "";
				}
				return accept.split(",");
			}
		},
		valid: {
			deps: ["value", "required"],
			fn: function() {

				var filesLength = this.files.length;

				if (this.required && !filesLength) {
					return false;
				}

				if (!this.multiple && filesLength > 1) {
					return false;
				}

				if (!this.tests.every(function(test) {
					return test(this.value);
				})) {
					return false;
				}

				return true;
			}
		}
	},
	collections: {
		files: FilesCollection
	},
	getValue: function() {
		var value;

		value = this.files.toFiles();

		this.value = value;
		return this.value;
	},
	render: function() {
		var self = this;

		this.renderWithTemplate(this);
		this.input = this.query("input[type=file]");
		this.renderCollection(self.files, FileView, self.queryByHook("files"), {
			viewOptions: {
				fileSizeUnit: self.fileSizeUnit,
				displayPreview: self.displayPreviews
			}
		});
	},
	reset: function() {
		var self = this;
		setTimeout(function() {
			self.files.reset();
		}, 0);
	},
	clear: function() {
		this.reset();
	},
	simulateInputClick: function() {
		this.input.click();
	},
	handleFiles: function(files) {

		if (this.acceptArray.length) {
			var MIMEtypes = this.acceptArray.map(function(accept) {
				return new RegExp(accept.replace("*", "[^\\/,]+"));
			});

			files = files.filter(function(file) {
				return MIMEtypes.some(function(mime) {
					return mime.test(file.type || "application/octet-stream");
				});
			});
		}

		if (!this.multiple) {

			if (this.files.length && files.length) {
				throw new Error("Multiple files are not allowed");
			}

			files = files.splice(0, 1);
		}

		this.files.add(files);
	},
	handleFileInput: function() {
		this.handleFiles(Array.prototype.slice.apply(this.input.files));
	},
	dragEnter: eventNoOp,
	dragOver: eventNoOp,
	drop: function(event) {
		event.stopPropagation();
		event.preventDefault();

		this.handleFiles(Array.prototype.slice.apply(event.dataTransfer.files));
	}
});<commit_msg>Fix resetting of field (field.value persisted after reset)<commit_after>var View = require("ampersand-view");
var State = require("ampersand-state");
var Collection = require("ampersand-collection");

var template = ["
",
	"<input type=\"file\" style=\"visibility:hidden;width:0;height:0;\"/>",
	"",
	"

",
"
"].join("");

var fileTemplate = ["
",
	"",
	"",
	"",
	"

",
	"<button data-hook=\"remove\">Remove</button>",
"
"].join("");

var FileState = State.extend({
	initialize: function(file){
		this.set("file", file);
	},
	props: {
		size: "number",
		name: "text",
		type: "text",
		file: "any",
		preview: "string"
	}
});

var FilesCollection = Collection.extend({
	model: FileState,
	toFiles: function() {
		return this.map(function(fileModel) {
			return fileModel.file;
		});
	}
});

var FileView = View.extend({
	template: fileTemplate,
	initialize: function(opts) {
		var self = this;
		self.displayPreview = opts.displayPreview;
		self.fileSizeUnit = opts.fileSizeUnit;
		var file = self.model.file;

		if (this.displayPreview && /image/.test(file.type)) {
			var reader = new FileReader();
			reader.onloadend = function() {
				self.model.preview = reader.result;
			};
			reader.readAsDataURL(file);
		}
	},
	events: {
		"click [data-hook=remove]": "removeFile",
	},
	bindings: {
		"fileSizeUnit": {
			type: "text",
			hook: "size-unit"
		},
		"fileSize": {
			type: "text",
			hook: "size"
		},
		"model.name": {
			type: "text",
			hook: "name"
		},
		"model.type": {
			type: "text",
			hook: "type"
		},
		"model.preview": [
			{
				type: "toggle",
				selector: "img"
			},
			{
				type: "attribute",
				selector: "img",
				name: "src"
			}
		]
	},
	props: {
		displayPreview: {
			type: "boolean"
		},
		fileSizeUnit: {
			type: "string"
		}
	},
	derived: {
		fileSize: {
			deps: ["model.size", "fileSizeUnit"],
			fn: function() {
				var number = this.model.size;
				var exp = 0;

				switch(this.fileSizeUnit.toUpperCase()) {
					case "KILOBYTE":
					case "KB":
						exp = 10;
						break;
					case "MEGABYTE":
					case "MB":
						exp = 20;
						break;
					case "GIGABYTE":
					case "GB":
						exp = 30;
						break;
					default:
						exp = 0;
				}

				return (number / Math.pow(2,exp)).toFixed(2);
			}
		}
	},
	removeFile: function(event) {
		event.preventDefault();
		event.stopImmediatePropagation();
		this.collection.remove(this.model);
	}
});

function eventNoOp(event) {
	event.stopPropagation();
	event.preventDefault();
}

module.exports = View.extend({
	template: template,
	initialize: function() {
		var self = this;

		this.files.on("add remove", function() {
			self.getValue();
			//self.valid;
			if (self.parent) {
				self.parent.update(self);
			}
		});
	},
	events: {
		"click [data-hook=drop-zone]": "simulateInputClick",
		"change input[type=file]": "handleFileInput",
		"dragenter [data-hook=drop-zone]": "dragEnter",
		"dragover [data-hook=drop-zone]": "dragOver",
		"drop [data-hook=drop-zone]": "drop"
	},
	bindings: {
		name: {
			type: "attribute",
			selector: "input[type=file]",
			name: "name"
		},
		label: [
			{
				type: "toggle",
				hook: "label"
			},
			{
				type: "text",
				hook: "label"
			}
		],
		holderClass: {
			type: "class",
			hook: "drop-zone"
		},
		multiple: {
			type: "booleanAttribute",
			selector: "input[type=file]",
			name: "multiple"
		},
		accept: {
			type: "attribute",
			selector: "input[type=file]",
			name: "accept"
		}
	},
	props: {
		holderClass: {
			type: "string"
		},
		label: {
			type: "string",
			default: "Drag and drop a file"
		},
		value: {
			type: "array",
			default: function() {
				return [];
			}
		},
		required: {
			type: "boolean",
			default: true
		},
		multiple: {
			type: "boolean",
			default: false
		},
		name: {
			type: "string",
			required: true
		},
		accept: {
			type: "string",
			default: "*/*"
		},
		tests: {
			type: "array",
			default: function() {
				return [];
			}
		},
		fileSizeUnit: {
			type: "string",
			default: "KB"
		},
		displayPreviews: {
			type: "boolean",
			default: true
		}
	},
	derived: {
		acceptArray: {
			deps: ["accept"],
			fn: function() {
				var accept;
				if (typeof this.accept === "string") {
					accept = this.accept;
				} else if (this.accept === true) {
					accept = "*/*";
				} else {
					accept = "";
				}
				return accept.split(",");
			}
		},
		valid: {
			deps: ["value", "required"],
			fn: function() {

				var filesLength = this.files.length;

				if (this.required && !filesLength) {
					return false;
				}

				if (!this.multiple && filesLength > 1) {
					return false;
				}

				if (!this.tests.every(function(test) {
					return test(this.value);
				})) {
					return false;
				}

				return true;
			}
		}
	},
	collections: {
		files: FilesCollection
	},
	getValue: function() {
		this.value = this.files.toFiles();
		return this.value;
	},
	render: function() {
		var self = this;

		this.renderWithTemplate(this);
		this.input = this.query("input[type=file]");
		this.renderCollection(self.files, FileView, self.queryByHook("files"), {
			viewOptions: {
				fileSizeUnit: self.fileSizeUnit,
				displayPreview: self.displayPreviews
			}
		});
	},
	reset: function() {
		var self = this;
		setTimeout(function() {
			self.files.reset();
			self.getValue();
		}, 0);
	},
	clear: function() {
		this.reset();
	},
	simulateInputClick: function() {
		this.input.click();
	},
	handleFiles: function(files) {

		if (this.acceptArray.length) {
			var MIMEtypes = this.acceptArray.map(function(accept) {
				return new RegExp(accept.replace("*", "[^\\/,]+"));
			});

			files = files.filter(function(file) {
				return MIMEtypes.some(function(mime) {
					return mime.test(file.type || "application/octet-stream");
				});
			});
		}

		if (!this.multiple) {

			if (this.files.length && files.length) {
				throw new Error("Multiple files are not allowed");
			}

			files = files.splice(0, 1);
		}

		this.files.add(files);
	},
	handleFileInput: function() {
		this.handleFiles(Array.prototype.slice.apply(this.input.files));
	},
	dragEnter: eventNoOp,
	dragOver: eventNoOp,
	drop: function(event) {
		event.stopPropagation();
		event.preventDefault();

		this.handleFiles(Array.prototype.slice.apply(event.dataTransfer.files));
	}
});<|endoftext|>


<commit_before>var Hoist = (function () {
	var configs = {},
		user,
		toString = Object.prototype.toString,
		u;

	function extend(into, from) {
		for (var x in from) into[x] = from[x];
	}

	function classOf(data) {
		return toString.call(data).slice(8, -1);
	}

	// ajax helper

	function request(opts, success, error, context) {
		var method,
			contentType;

		if ("data" in opts) {
			var type = classOf(opts.data);

			if (type === "String") {
				contentType = "application/json";
			} else if (type === "FormData") {
				method = "POST";
			} else {
				method = "POST";
				contentType = "application/json";
				opts.data = JSON.stringify(opts.data);
			}
		} else {
			method = "GET";
		}

		if (typeof error !== "function") {
			context = error;
			error = null;
		}

		if (!configs.apikey) {
			error && setTimeout(function () {
				error.call(context, "API key not set", null);
			}, 0);
		}

		var xhr = new XMLHttpRequest();

		xhr.open(method, opts.url);

		xhr.responseType = opts.responseType || "json";
		contentType && xhr.setRequestHeader("Content-Type", contentType);

		xhr.setRequestHeader("Authorization", "Hoist " + configs.apikey);

		xhr.withCredentials = true;

		xhr.onreadystatechange = function () {
			if (xhr.readyState === 4) {
				if (xhr.status >= 200 && xhr.status < 300) {
					if (typeof xhr.response === "string" && xhr.responseType === "json") {
						success && success.call(context, JSON.parse(xhr.response), xhr);
					} else {
						success && success.call(context, xhr.response, xhr);
					}
				} else {
					error && error.call(context, xhr.statusText, xhr);
				}
			}
		};

		xhr.send(opts.data);
	}

	var managers = {};

	function DataManager(type) {
		this.type = type;
		this.url = "https://data.hoi.io/" + type;
	}

	extend(DataManager.prototype, {
		get: function (id, success, error, context) {
			if (typeof id === "function" || id === undefined) {
				context = error;
				error = success;
				success = id;

				request({ url: this.url }, success, error, context);
			} else {
				request({ url: this.url + "/" + id }, success, error, context);
			}
		},

		post: function (id, data, success, error, context) {
			if (typeof id === "object") {
				context = error;
				error = success;
				success = data;
				data = id;

				if (data._id) {
					id = data._id;
				} else {
					request({ url: this.url, data: data }, success, error, context);
					return;
				}
			}

			request({ url: this.url + "/" + id, data: data }, success, error, context);
		}
	});

	Hoist = function (type) {
		return managers[type] || (managers[type] = new DataManager(type));
	};

	extend(Hoist, {
		apiKey: function (v) {
			return this.config("apikey", v);
		},

		get: function (type, id, success, error, context) {
			Hoist(type).get(id, success, error, context);
		},

		post: function (type, id, data, success, error, context) {
			Hoist(type).post(id, data, success, error, context);
		},

		config: function (a, b) {
			if (b === u) {
				var type = typeof a;

				if (type === "string") {
					return configs[a];
				} else if (type === "object") {
					for (var x in a) {
						configs[x.toLowerCase()] = a[x];
					}
				}
			} else {
				configs[a.toLowerCase()] = b;
			}
		},

		status: function (success, error, context) {
			request({ url: "https://auth.hoi.io/status" }, function (resp) {
				user = resp;
				success && success.apply(this, arguments);
			}, error, context);
		},

		signup: function (member, success, error, context) {
			if (typeof member === "object") {
				request({ url: "https://auth.hoi.io/user", data: member }, function (resp) {
					user = resp;
					success && success.apply(this, arguments);
				}, error, context);
			}
		},

		login: function (member, success, error, context) {
			if (typeof member === "object") {
				request({ url: "https://auth.hoi.io/login", data: member }, function (resp) {
					user = resp;
					success && success.apply(this, arguments);
				}, error, context);
			}
		},

		user: function () {
			return user;
		},

		notify: function (id, data, success, error, context) {
			if (typeof id === "object") {
				context = error;
				error = success;
				success = data;
				data = id.data;
				id = id.id;
			}

			if (typeof data === "object") {
				request({ url: "https://notify.hoi.io/notification/" + id, data: data }, success, error, context);
			}
		},

		file: function (key, file, success, error, context) {
			if (file.jQuery) file = file[0];

			var type = classOf(file),
				data;

			if (type === "File") {
				data = new FormData();
				data.append("file", file);
			} else if (type === "FormData") {
				data = file;
			} else if (type === "HTMLInputElement") {
				file = file.files && file.files[0];

				if (!file) return false;

				data = new FormData();
				data.append("file", file);
			} else if (type === "Function") {
				context = error;
				error = success;
				success = file;

				request({ url: "https://file.hoi.io/" + key, responseType: "blob" }, success, error, context);
				return;
			} else if (type === "Undefined") {
				request({ url: "https://file.hoi.io/" + key, responseType: "blob" }, success, error, context);
				return;
			} else {
				return;
			}

			request({ url: "https://file.hoi.io/" + key, data: data }, success, error, context);
		}
	});

	Hoist.clone = arguments.callee;

	return Hoist;
})();

if (typeof define === "function" && define.amd) {
	define("Hoist", [], function() {
		return Hoist;
	});
}
else
{
	//put hoist on the global namespace
	window.Hoist = Hoist;
}<commit_msg>Fix XHR responseType bug in Safari<commit_after>var Hoist = (function () {
	var configs = {},
		user,
		toString = Object.prototype.toString,
		u;

	function extend(into, from) {
		for (var x in from) into[x] = from[x];
	}

	function classOf(data) {
		return toString.call(data).slice(8, -1);
	}

	// ajax helper

	function request(opts, success, error, context) {
		var method, contentType, responseType;

		if ("data" in opts) {
			var type = classOf(opts.data);

			if (type === "String") {
				contentType = "application/json";
			} else if (type === "FormData") {
				method = "POST";
			} else {
				method = "POST";
				contentType = "application/json";
				opts.data = JSON.stringify(opts.data);
			}
		} else {
			method = "GET";
		}

		if (typeof error !== "function") {
			context = error;
			error = null;
		}

		if (!configs.apikey) {
			error && setTimeout(function () {
				error.call(context, "API key not set", null);
			}, 0);
		}

		var xhr = new XMLHttpRequest();

		xhr.open(method, opts.url);

		xhr.responseType = responseType = opts.responseType || "json";

		contentType && xhr.setRequestHeader("Content-Type", contentType);

		xhr.setRequestHeader("Authorization", "Hoist " + configs.apikey);

		xhr.withCredentials = true;

		xhr.onreadystatechange = function () {
			if (xhr.readyState === 4) {
				if (xhr.status >= 200 && xhr.status < 300) {
					if (typeof xhr.response === "string" && responseType === "json") {
						success && success.call(context, JSON.parse(xhr.response), xhr);
					} else {
						success && success.call(context, xhr.response, xhr);
					}
				} else {
					error && error.call(context, xhr.statusText, xhr);
				}
			}
		};

		xhr.send(opts.data);
	}

	var managers = {};

	function DataManager(type) {
		this.type = type;
		this.url = "https://data.hoi.io/" + type;
	}

	extend(DataManager.prototype, {
		get: function (id, success, error, context) {
			if (typeof id === "function" || id === undefined) {
				context = error;
				error = success;
				success = id;

				request({ url: this.url }, success, error, context);
			} else {
				request({ url: this.url + "/" + id }, success, error, context);
			}
		},

		post: function (id, data, success, error, context) {
			if (typeof id === "object") {
				context = error;
				error = success;
				success = data;
				data = id;

				if (data._id) {
					id = data._id;
				} else {
					request({ url: this.url, data: data }, success, error, context);
					return;
				}
			}

			request({ url: this.url + "/" + id, data: data }, success, error, context);
		}
	});

	Hoist = function (type) {
		return managers[type] || (managers[type] = new DataManager(type));
	};

	extend(Hoist, {
		apiKey: function (v) {
			return this.config("apikey", v);
		},

		get: function (type, id, success, error, context) {
			Hoist(type).get(id, success, error, context);
		},

		post: function (type, id, data, success, error, context) {
			Hoist(type).post(id, data, success, error, context);
		},

		config: function (a, b) {
			if (b === u) {
				var type = typeof a;

				if (type === "string") {
					return configs[a];
				} else if (type === "object") {
					for (var x in a) {
						configs[x.toLowerCase()] = a[x];
					}
				}
			} else {
				configs[a.toLowerCase()] = b;
			}
		},

		status: function (success, error, context) {
			request({ url: "https://auth.hoi.io/status" }, function (resp) {
				user = resp;
				success && success.apply(this, arguments);
			}, error, context);
		},

		signup: function (member, success, error, context) {
			if (typeof member === "object") {
				request({ url: "https://auth.hoi.io/user", data: member }, function (resp) {
					user = resp;
					success && success.apply(this, arguments);
				}, error, context);
			}
		},

		login: function (member, success, error, context) {
			if (typeof member === "object") {
				request({ url: "https://auth.hoi.io/login", data: member }, function (resp) {
					user = resp;
					success && success.apply(this, arguments);
				}, error, context);
			}
		},

		user: function () {
			return user;
		},

		notify: function (id, data, success, error, context) {
			if (typeof id === "object") {
				context = error;
				error = success;
				success = data;
				data = id.data;
				id = id.id;
			}

			if (typeof data === "object") {
				request({ url: "https://notify.hoi.io/notification/" + id, data: data }, success, error, context);
			}
		},

		file: function (key, file, success, error, context) {
			if (file.jQuery) file = file[0];

			var type = classOf(file),
				data;

			if (type === "File") {
				data = new FormData();
				data.append("file", file);
			} else if (type === "FormData") {
				data = file;
			} else if (type === "HTMLInputElement") {
				file = file.files && file.files[0];

				if (!file) return false;

				data = new FormData();
				data.append("file", file);
			} else if (type === "Function") {
				context = error;
				error = success;
				success = file;

				request({ url: "https://file.hoi.io/" + key, responseType: "blob" }, success, error, context);
				return;
			} else if (type === "Undefined") {
				request({ url: "https://file.hoi.io/" + key, responseType: "blob" }, success, error, context);
				return;
			} else {
				return;
			}

			request({ url: "https://file.hoi.io/" + key, data: data }, success, error, context);
		}
	});

	Hoist.clone = arguments.callee;

	return Hoist;
})();

if (typeof define === "function" && define.amd) {
	define("Hoist", [], function() {
		return Hoist;
	});
}
else
{
	//put hoist on the global namespace
	window.Hoist = Hoist;
}<|endoftext|>


<commit_before>export function hideNode ( node ) {
	node.__ramjetOriginalTransition__ = node.style.webkitTransition || node.style.transition;
	node.__ramjetOriginalOpacity__ = node.style.opacity;

	node.style.webkitTransition = node.style.transition = '';

	node.style.opacity = 0;
}

export function showNode ( node ) {
	if ( '__ramjetOriginalOpacity__' in node ) {
		node.style.transition = '';
		node.style.opacity = node.__ramjetOriginalOpacity__;

		if ( node.__ramjetOriginalTransition__ ) {
			setTimeout( () => {
				node.style.transition = node.__ramjetOriginalTransition__;
			});
		}
	}
}

export function cloneNode ( node ) {
	const clone = node.cloneNode();

	if ( node.nodeType === 1 ) {
		clone.setAttribute( 'style', window.getComputedStyle( node ).cssText );

		const len = node.childNodes.length;
		let i;

		for ( i = 0; i < len; i += 1 ) {
			clone.appendChild( cloneNode( node.childNodes[i] ) );
		}
	}

	return clone;
}
<commit_msg>fix html-to-svg transforms<commit_after>import { svgns } from './svg.js';

export function hideNode ( node ) {
	node.__ramjetOriginalTransition__ = node.style.webkitTransition || node.style.transition;
	node.__ramjetOriginalOpacity__ = node.style.opacity;

	node.style.webkitTransition = node.style.transition = '';

	node.style.opacity = 0;
}

export function showNode ( node ) {
	if ( '__ramjetOriginalOpacity__' in node ) {
		node.style.transition = '';
		node.style.opacity = node.__ramjetOriginalOpacity__;

		if ( node.__ramjetOriginalTransition__ ) {
			setTimeout( () => {
				node.style.transition = node.__ramjetOriginalTransition__;
			});
		}
	}
}

export function cloneNode ( node ) {
	const clone = node.cloneNode();

	const isSvg = node.parentNode && node.parentNode.namespaceURI === svgns;

	if ( node.nodeType === 1 ) {
		const width = node.style.width;
		const height = node.style.height;

		clone.setAttribute( 'style', window.getComputedStyle( node ).cssText );

		if ( isSvg ) {
			clone.style.width = width;
			clone.style.height = height;
		}

		const len = node.childNodes.length;
		let i;

		for ( i = 0; i < len; i += 1 ) {
			clone.appendChild( cloneNode( node.childNodes[i] ) );
		}
	}

	return clone;
}
<|endoftext|>


<commit_before>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _db = (function () {

//  P U B L I C   I N T E R F A C E
    var that = {};

    that.save_permalink = function ( groups ) {
        // inform user it can take a while!!
        _utils.create_preloader( 'Zapisuję arkusze w bazie danych.
To może chwilę potrwać...' );

        $.ajax({
            url: 'store_state/',
            type: 'GET',
            data: {
                state: JSON.stringify( groups )
            },
            dataType: 'json',
            success: function ( received_data ) {
              $('#app-sh-permalink')
                .slideDown( 100 )
                .find('input')
                .val( 'http://otwartedane.pl/'+received_data['id'] )
                .focus()

                _utils.clear_preloader();
            }
        });
    };

    that.search = function ( query, scope, strict ) {
        var data = {
            query: query,
            scope: scope.toString(),
            strict: strict.toString()
        };

        $.ajax({
            url: 'search/',
            data: data,
            dataType: 'json',
            success: function ( received_data ) {
                var html = [];
                var thead = $('#pl-sr-results').find('thead');
                var tbody = $('#pl-sr-results').find('tbody');
                tbody.empty();

                if( received_data['records_found_total'] === 0 ) {
                    html.push( '
' );
                    html.push( 'Niestety wyszukiwane hasło nie znajduje się wśród zebranych tutaj danych	' );
                    html.push( '
' );

                    tbody.append( $( html.join( '' ) ) );
                    thead.hide();
                }
                else {
                    received_data['strict']['result'].forEach( function ( collection ) {
                        var html = [];
                        var single_row;
                        var idefs = [];
                        var results_length = collection['data'].length;
                        var results_limit = 250;

                        html.push( '
' );
                        html.push( '', collection['data'].length, '	' );
                        html.push( '' );
                        html.push( '
', collection['perspective'], '
' );
                        if( results_length > results_limit ) {
                            html.push( '
>
' );
                            html.push( '
' );
                            html.push( 'Zbyt wiele wyników - nie sposób ich wyświetlić
' );
                        }
                        html.push( '	' );
                        html.push( '
' );

                        single_row = $( html.join('') );
                        collection['data'].forEach( function ( result ) {
                            idefs.push( result['idef'] );
                        });

                        if( results_length < results_limit ) {
                            single_row
                                .click( function () {
                                    that.add_search_data({
                                        dataset: collection['dataset'].toString(),
                                        view: collection['view'].toString(),
                                        issue: collection['issue'].toString(),
                                        idef: idefs.toString(),
                                        query: query
                                    });
                                })
                                .find( '.pl-sr-results-name' )
                                    .hover(
                                        function () {
                                            $(this)
                                                .css( 'cursor', 'pointer' )
                                                .find( '.pl-sr-results-name-text' )
                                                .css( 'color', '#1ea3e8' )
                                                .end()
                                                .find( '.pl-sr-results-button' )
                                                .css( 'background-color', '#1ea3e8' );
                                        },
                                        function () {
                                            $(this)
                                                .find( '.pl-sr-results-name-text' )
                                                .css( 'color', '#000' )
                                                .end()
                                                .find( '.pl-sr-results-button' )
                                                .css( 'background-color', '#c1c1c1' );
                                        }
                                    );
                        }

                        tbody.append( single_row );
                    });
                    thead.show();
                }
                _utils.clear_preloader();

                $('#pl-sr-full')
                    .slideUp( 200 );

                $('#pl-sr-more')
                    .html('Pokaż zaawansowane');

                $('#pl-sr-results')
                    .slideDown( 200 )
                    .find('tr')
                    .each( function () {
                        var h = $(this).find('.pl-sr-results-name').height();

                        $(this)
                            .find('.pl-sr-results-number')
                            .height( h );
                    });

                $('#pl-sr-show').show();
            }
        });
    };

    that.add_search_data = function ( search_list ) {
        var col_id = {
            dataset: search_list['dataset'],
            view: search_list['view'],
            perspective: search_list['view'],
            issue: search_list['issue'],
        };
        _utils.create_preloader( "Wczytuję dane z bazy danych" );
        $.ajax({
            url: 'get_searched/',
            data: search_list,
            dataType: 'json',
            success: function ( received_data ) {
                if ( _store.group_exists( col_id ) ) {
                    _sheet.create_searched_sheet( col_id, received_data );
                }
                else {
                    _sheet.add_searched_group( col_id, received_data );
                }
                _utils.clear_preloader();
                _gui.show_table_tab();

                $('#app-tb-datatable > tbody td').each( function () {
                    if( $(this).html().toLowerCase().indexOf( search_list['query'].toLowerCase() ) !== -1 ) {
                        $(this).parent().css( 'background-color', '#F3E58A' );
                    }
                });
            }
        }); // $.ajax
    };

    // gets the top-level from db
    that.get_init_data = function (col_id) {
        // ajax call data object
        var init_data_info = {
            "action": "get_init_data",
            "dataset": col_id.dataset,
            "perspective": col_id.view,
            "issue": col_id.issue
        };

        _utils.create_preloader( "Wczytuję dane z bazy danych" );
        $.ajax({
            data: init_data_info,
            dataType: "json",
            success: function( received_data ) {
                var data = {
			// TODO get rid of colkumns here
			// TODO move it to create sheet function
//                    columns: received_data.perspective.columns,
                    rows: received_data.rows,
                    name: received_data.perspective.perspective
                };

                // create group
                _store.create_group({
                   "dataset": col_id.dataset,
                   "view": col_id.view,
                   "issue": col_id.issue,
                   "columns": received_data.perspective.columns,
                });
                _store.init_basic_sheet( data );
                // create a table
                _table.init_table();
                _utils.clear_preloader();
                // initialize an application
                _gui.init_app( data['name'] );
            }
        });
    };

    // downloads requested nodes, appends them to object with other nodes
    // and adds html code for new nodes
    that.download_node = function ( parent_id ) {
        // ajax call data object
        var download_data = {
            action: 'get_node',
            dataset: _store.dataset(),
            perspective: _store.view(),
            issue: _store.issue(),
            parent: parent_id,
        };

        $.ajax({
            data: download_data,
            dataType: 'json',
            success: function( received_data ) {

                // store new data in model
                _store.add_data( received_data );
                // render new data in table
                // TODO is parent_id really necessary?!
                _table.add_node( parent_id );

                // if it was a basic sheet sign it changed
                if ( _store.active_sheet_index() === 0 ) {
                    _store.active_basic_changed( true );
                }
            }
        });
    };

    return that;

})();
<commit_msg>Trying to fix url bug<commit_after>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _db = (function () {

//  P U B L I C   I N T E R F A C E
    var that = {};

    that.save_permalink = function ( groups ) {
        // inform user it can take a while!!
        _utils.create_preloader( 'Zapisuję arkusze w bazie danych.
To może chwilę potrwać...' );

        $.ajax({
            url: 'store_state/',
            type: 'GET',
            data: {
                state: JSON.stringify( groups )
            },
            dataType: 'json',
            success: function ( received_data ) {
              $('#app-sh-permalink')
                .slideDown( 100 )
                .find('input')
                .val( 'http://otwartedane.pl/'+received_data['id'] )
                .focus()

                _utils.clear_preloader();
            }
        });
    };

    that.search = function ( query, scope, strict ) {
        var data = {
            query: query,
            scope: scope.toString(),
            strict: strict.toString()
        };

        $.ajax({
            url: 'search/',
            data: data,
            dataType: 'json',
            success: function ( received_data ) {
                var html = [];
                var thead = $('#pl-sr-results').find('thead');
                var tbody = $('#pl-sr-results').find('tbody');
                tbody.empty();

                if( received_data['records_found_total'] === 0 ) {
                    html.push( '
' );
                    html.push( 'Niestety wyszukiwane hasło nie znajduje się wśród zebranych tutaj danych	' );
                    html.push( '
' );

                    tbody.append( $( html.join( '' ) ) );
                    thead.hide();
                }
                else {
                    received_data['strict']['result'].forEach( function ( collection ) {
                        var html = [];
                        var single_row;
                        var idefs = [];
                        var results_length = collection['data'].length;
                        var results_limit = 250;

                        html.push( '
' );
                        html.push( '', collection['data'].length, '	' );
                        html.push( '' );
                        html.push( '
', collection['perspective'], '
' );
                        if( results_length > results_limit ) {
                            html.push( '
>
' );
                            html.push( '
' );
                            html.push( 'Zbyt wiele wyników - nie sposób ich wyświetlić
' );
                        }
                        html.push( '	' );
                        html.push( '
' );

                        single_row = $( html.join('') );
                        collection['data'].forEach( function ( result ) {
                            idefs.push( result['idef'] );
                        });

                        if( results_length < results_limit ) {
                            single_row
                                .click( function () {
                                    that.add_search_data({
                                        dataset: collection['dataset'].toString(),
                                        view: collection['view'].toString(),
                                        issue: collection['issue'].toString(),
                                        idef: idefs.toString(),
                                        query: query
                                    });
                                })
                                .find( '.pl-sr-results-name' )
                                    .hover(
                                        function () {
                                            $(this)
                                                .css( 'cursor', 'pointer' )
                                                .find( '.pl-sr-results-name-text' )
                                                .css( 'color', '#1ea3e8' )
                                                .end()
                                                .find( '.pl-sr-results-button' )
                                                .css( 'background-color', '#1ea3e8' );
                                        },
                                        function () {
                                            $(this)
                                                .find( '.pl-sr-results-name-text' )
                                                .css( 'color', '#000' )
                                                .end()
                                                .find( '.pl-sr-results-button' )
                                                .css( 'background-color', '#c1c1c1' );
                                        }
                                    );
                        }

                        tbody.append( single_row );
                    });
                    thead.show();
                }
                _utils.clear_preloader();

                $('#pl-sr-full')
                    .slideUp( 200 );

                $('#pl-sr-more')
                    .html('Pokaż zaawansowane');

                $('#pl-sr-results')
                    .slideDown( 200 )
                    .find('tr')
                    .each( function () {
                        var h = $(this).find('.pl-sr-results-name').height();

                        $(this)
                            .find('.pl-sr-results-number')
                            .height( h );
                    });

                $('#pl-sr-show').show();
            }
        });
    };

    that.add_search_data = function ( search_list ) {
        var col_id = {
            dataset: search_list['dataset'],
            view: search_list['view'],
            perspective: search_list['view'],
            issue: search_list['issue'],
        };
        _utils.create_preloader( "Wczytuję dane z bazy danych" );
        $.ajax({
            url: 'get_searched/',
            data: search_list,
            dataType: 'json',
            success: function ( received_data ) {
                if ( _store.group_exists( col_id ) ) {
                    _sheet.create_searched_sheet( col_id, received_data );
                }
                else {
                    _sheet.add_searched_group( col_id, received_data );
                }
                _utils.clear_preloader();
                _gui.show_table_tab();

                $('#app-tb-datatable > tbody td').each( function () {
                    if( $(this).html().toLowerCase().indexOf( search_list['query'].toLowerCase() ) !== -1 ) {
                        $(this).parent().css( 'background-color', '#F3E58A' );
                    }
                });
            }
        }); // $.ajax
    };

    // gets the top-level from db
    that.get_init_data = function (col_id) {
        // ajax call data object
        var init_data_info = {
            "action": "get_init_data",
            "dataset": col_id.dataset,
            "perspective": col_id.view,
            "issue": col_id.issue
        };

        _utils.create_preloader( "Wczytuję dane z bazy danych" );
        $.ajax({
            data: init_data_info,
            dataType: "json",
            success: function( received_data ) {
                var data = {
			// TODO get rid of colkumns here
			// TODO move it to create sheet function
//                    columns: received_data.perspective.columns,
                    rows: received_data.rows,
                    name: received_data.perspective.perspective
                };

                // create group
                _store.create_group({
                   "dataset": col_id.dataset,
                   "view": col_id.view,
                   "issue": col_id.issue,
                   "columns": received_data.perspective.columns,
                });
                _store.init_basic_sheet( data );
                // create a table
                _table.init_table();
                _utils.clear_preloader();
                // initialize an application
                _gui.init_app( data['name'] );
            }
        });
    };

    // downloads requested nodes, appends them to object with other nodes
    // and adds html code for new nodes
    that.download_node = function ( parent_id ) {
        // ajax call data object
        var download_data = {
            action: 'get_node',
            dataset: _store.dataset(),
            perspective: _store.view(),
            issue: _store.issue(),
            parent: parent_id,
        };

        $.ajax({
            url: '/',
            data: download_data,
            dataType: 'json',
            success: function( received_data ) {

                // store new data in model
                _store.add_data( received_data );
                // render new data in table
                // TODO is parent_id really necessary?!
                _table.add_node( parent_id );

                // if it was a basic sheet sign it changed
                if ( _store.active_sheet_index() === 0 ) {
                    _store.active_basic_changed( true );
                }
            }
        });
    };

    return that;

})();
<|endoftext|>