<commit_before>/**
 * @module Input
 * @submodule Files
 * @for p5
 * @requires core
 * @requires reqwest
 */
define(function (require) {

  'use strict';

  var p5 = require('core');
  var reqwest = require('reqwest');

  //BufferedReader
  p5.prototype.createInput = function() {
    // TODO
    throw 'not yet implemented';
  };

  p5.prototype.createReader = function() {
    // TODO
    throw 'not yet implemented';
  };

  p5.prototype.loadBytes = function() {
    // TODO
    throw 'not yet implemented';
  };

  /**
   * Loads a JSON file from a file or a URL, and returns an Object or Array.
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadStrings() completes.
   *
   * @method loadJSON
   * @param  {String}        path       name of the file or url to load
   * @param  {Function}      [callback] function to be executed after
   *                                    loadJSON()
   *                                    completes, Array is passed in as first
   *                                    argument
   * @return {Object|Array}             JSON data
   */
  p5.prototype.httpGet = function(url, data, callback) {
    reqwest({url: path, data:data crossOrigin: true})
      .then(function(resp) {
        if (typeof callback !== 'undefined') {
          callback(resp);
        }
      });
    return this;
  };

  /**
   * Loads a JSON file from a file or a URL, and returns an Object or Array.
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadStrings() completes.
   *
   * @method loadJSON
   * @param  {String}        path       name of the file or url to load
   * @param  {Function}      [callback] function to be executed after
   *                                    loadJSON()
   *                                    completes, Array is passed in as first
   *                                    argument
   * @return {Object|Array}             JSON data
   */
  p5.prototype.loadJSON = function(path, callback) {
    var ret = [];
    var t = path.indexOf('http') === -1 ? 'json' : 'jsonp';
    reqwest({url: path, type: t, crossOrigin: true})
      .then(function(resp) {
        for (var k in resp) {
          ret[k] = resp[k];
        }
        if (typeof callback !== 'undefined') {
          callback(ret);
        }
      });
    return ret;
  };

  /**
   * Reads the contents of a file and creates a String array of its individual
   * lines. If the name of the file is used as the parameter, as in the above
   * example, the file must be located in the sketch directory/folder.
   *
   * Alternatively, the file maybe be loaded from anywhere on the local
   * computer using an absolute path (something that starts with / on Unix and
   * Linux, or a drive letter on Windows), or the filename parameter can be a
   * URL for a file found on a network.
   *
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadStrings() completes.
   *
   * @method loadStrings
   * @param  {String}   filename   name of the file or url to load
   * @param  {Function} [callback] function to be executed after loadStrings()
   *                               completes, Array is passed in as first
   *                               argument
   * @return {Array}               Array of Strings
   * @example
   *

   * var result;
   * function preload() {
   *   result = loadStrings('assets/test.txt');
   * }

   * function setup() {
   *   background(200);
   *   var ind = floor(random(result.length));
   *   text(result[ind], 10, 10, 80, 80);
   * }
   *

   *
   *

   * function setup() {
   *   loadStrings('assets/test.txt', pickString);
   * }
   *
   * function pickString(result) {
   *   background(200);
   *   var ind = floor(random(result.length));
   *   text(result[ind], 10, 10, 80, 80);
   * }
   *

   */
  p5.prototype.loadStrings = function (path, callback) {
    var ret = [];
    var req = new XMLHttpRequest();
    req.open('GET', path, true);
    req.onreadystatechange = function () {
      if (req.readyState === 4 && (req.status === 200 || req.status === 0)) {
        var arr = req.responseText.match(/[^\r\n]+/g);
        for (var k in arr) {
          ret[k] = arr[k];
        }
        if (typeof callback !== 'undefined') {
          callback(ret);
        }
      }
    };
    req.send(null);
    return ret;
  };

  /**
   *
Reads the contents of a file or URL and creates a p5.Table
   *  object with its values. If a file is specified, it must be
   *  located in the sketch's "data" folder. The filename parameter
   *  can also be a URL to a file found online. By default, the file
   *  is assumed to be comma-separated (in CSV format). Table only
   *  looks for a header row if the 'header' option is included.

   *
   *
Possible options include:
   *

   *
csv - parse the table as comma-separated values
   *
tsv - parse the table as tab-separated values
   *
newlines - this CSV file contains newlines inside individual cells
   *
header - this table has a header (title) row
   *

   *

   *
   *
 All files loaded and saved use UTF-8 encoding.

   *
   *
This method is asynchronous, meaning it may not finish before the next
   *  line in your sketch is executed. Either use preload() to guarantee the
   *  file loads before setup() and draw() are called, or supply a callback
   *  function that is executed when loadTable() completes.

   *
   *  @method  loadTable
   *  @param  {String}   filename   name of the file or URL to load
   *  @param  {String|Strings}   [options]  "header" "csv" "tsv"
   *  @param  {Function} [callback] function to be executed after loadTable()
   *                               completes, Table object is passed in as
   *                               first argument
   *  @return {Object}              Table object containing data
   */
  p5.prototype.loadTable = function (path) {
    var callback = null;
    var options = [];
    var header = false;
    var sep = ',';
    for (var i = 1; i < arguments.length; i++) {
      if (typeof(arguments[i]) === 'function' ){
        callback = arguments[i];
      }
      else if (typeof(arguments[i]) === 'string') {
        options.push(arguments[i]);
        if (arguments[i] === 'header') {
          header = true;
        }
        if (arguments[i] === 'csv') {
          sep = ',';
        }
        else if (arguments[i] === 'tsv') {
          sep = '\t';
        }
      }
    }
    var ret = [];
    var t = new p5.Table();
    var req = new XMLHttpRequest();
    req.open('GET', path, true);
    req.onreadystatechange = function () {
      if (req.readyState === 4 && (req.status === 200 || req.status === 0)) {
        var arr = req.responseText.match(/[^\r\n]+/g);
        for (var k in arr) {
          ret[k] = arr[k];
        }
        if (typeof callback !== 'undefined') {
          var i, row;
          if (header) {
            t.columns = new p5.TableRow(ret[0]).arr;
            for (i = 1; i<ret.length; i++) {
              row = new p5.TableRow(ret[i], sep);
              row.obj = makeObject(row.arr, t.columns);
              t.addRow(row);
            }
          } else {
            // no header: column titles will be numbers
            for (i = 0; i < ret[0].split(sep).length; i++){
              t.columns[i] = i.toString();
            }
            for (i = 0; i<ret.length; i++) {
              row = new p5.TableRow(ret[i], sep);
              t.addRow(row);
            }
          }

          callback(t);
        }
      }
    };
    req.send(null);
    return t;
  };

  // helper function to turn a row into a JSON object
  function makeObject(row, headers) {
    var ret = {};
    headers = headers || [];
    if (typeof(headers) === 'undefined'){
      for (var j = 0; j < row.length; j++ ){
        headers[j.toString()] = j;
      }
    }
    for (var i = 0; i < headers.length; i++){
      var key = headers[i];
      var val = row[i];
      ret[key] = val;
    }
    return ret;
  }

  /**
   * Reads the contents of a file and creates an XML object with its values.
   * If the name of the file is used as the parameter, as in the above example,
   * the file must be located in the sketch directory/folder.
   *
   * Alternatively, the file maybe be loaded from anywhere on the local
   * computer using an absolute path (something that starts with / on Unix and
   * Linux, or a drive letter on Windows), or the filename parameter can be a
   * URL for a file found on a network.
   *
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadXML() completes.
   *
   * @method loadXML
   * @param  {String}   filename   name of the file or URL to load
   * @param  {Function} [callback] function to be executed after loadXML()
   *                               completes, XML object is passed in as
   *                               first argument
   * @return {Object}              XML object containing data
   */
  p5.prototype.loadXML = function(path, callback) {
    var ret = [];
    reqwest({
      url: path,
      type: 'xml',
      crossOrigin: true,
    })
      .then(function(resp){
        callback(resp);
      });
    return ret;
  };

  // name clash with window.open
  // p5.prototype.open = function() {
  //   // TODO

  // };

  p5.prototype.parseXML = function() {
    // TODO
    throw 'not yet implemented';

  };

  p5.prototype.selectFolder = function() {
    // TODO
    throw 'not yet implemented';

  };

  p5.prototype.selectInput = function() {
    // TODO
    throw 'not yet implemented';

  };

  return p5;
});
<commit_msg>removing premature httpget test<commit_after>/**
 * @module Input
 * @submodule Files
 * @for p5
 * @requires core
 * @requires reqwest
 */
define(function (require) {

  'use strict';

  var p5 = require('core');
  var reqwest = require('reqwest');

  //BufferedReader
  p5.prototype.createInput = function() {
    // TODO
    throw 'not yet implemented';
  };

  p5.prototype.createReader = function() {
    // TODO
    throw 'not yet implemented';
  };

  p5.prototype.loadBytes = function() {
    // TODO
    throw 'not yet implemented';
  };

  /**
   * Loads a JSON file from a file or a URL, and returns an Object or Array.
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadStrings() completes.
   *
   * @method loadJSON
   * @param  {String}        path       name of the file or url to load
   * @param  {Function}      [callback] function to be executed after
   *                                    loadJSON()
   *                                    completes, Array is passed in as first
   *                                    argument
   * @return {Object|Array}             JSON data
   */
  p5.prototype.loadJSON = function(path, callback) {
    var ret = [];
    var t = path.indexOf('http') === -1 ? 'json' : 'jsonp';
    reqwest({url: path, type: t, crossOrigin: true})
      .then(function(resp) {
        for (var k in resp) {
          ret[k] = resp[k];
        }
        if (typeof callback !== 'undefined') {
          callback(ret);
        }
      });
    return ret;
  };

  /**
   * Reads the contents of a file and creates a String array of its individual
   * lines. If the name of the file is used as the parameter, as in the above
   * example, the file must be located in the sketch directory/folder.
   *
   * Alternatively, the file maybe be loaded from anywhere on the local
   * computer using an absolute path (something that starts with / on Unix and
   * Linux, or a drive letter on Windows), or the filename parameter can be a
   * URL for a file found on a network.
   *
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadStrings() completes.
   *
   * @method loadStrings
   * @param  {String}   filename   name of the file or url to load
   * @param  {Function} [callback] function to be executed after loadStrings()
   *                               completes, Array is passed in as first
   *                               argument
   * @return {Array}               Array of Strings
   * @example
   *

   * var result;
   * function preload() {
   *   result = loadStrings('assets/test.txt');
   * }

   * function setup() {
   *   background(200);
   *   var ind = floor(random(result.length));
   *   text(result[ind], 10, 10, 80, 80);
   * }
   *

   *
   *

   * function setup() {
   *   loadStrings('assets/test.txt', pickString);
   * }
   *
   * function pickString(result) {
   *   background(200);
   *   var ind = floor(random(result.length));
   *   text(result[ind], 10, 10, 80, 80);
   * }
   *

   */
  p5.prototype.loadStrings = function (path, callback) {
    var ret = [];
    var req = new XMLHttpRequest();
    req.open('GET', path, true);
    req.onreadystatechange = function () {
      if (req.readyState === 4 && (req.status === 200 || req.status === 0)) {
        var arr = req.responseText.match(/[^\r\n]+/g);
        for (var k in arr) {
          ret[k] = arr[k];
        }
        if (typeof callback !== 'undefined') {
          callback(ret);
        }
      }
    };
    req.send(null);
    return ret;
  };

  /**
   *
Reads the contents of a file or URL and creates a p5.Table
   *  object with its values. If a file is specified, it must be
   *  located in the sketch's "data" folder. The filename parameter
   *  can also be a URL to a file found online. By default, the file
   *  is assumed to be comma-separated (in CSV format). Table only
   *  looks for a header row if the 'header' option is included.

   *
   *
Possible options include:
   *

   *
csv - parse the table as comma-separated values
   *
tsv - parse the table as tab-separated values
   *
newlines - this CSV file contains newlines inside individual cells
   *
header - this table has a header (title) row
   *

   *

   *
   *
 All files loaded and saved use UTF-8 encoding.

   *
   *
This method is asynchronous, meaning it may not finish before the next
   *  line in your sketch is executed. Either use preload() to guarantee the
   *  file loads before setup() and draw() are called, or supply a callback
   *  function that is executed when loadTable() completes.

   *
   *  @method  loadTable
   *  @param  {String}   filename   name of the file or URL to load
   *  @param  {String|Strings}   [options]  "header" "csv" "tsv"
   *  @param  {Function} [callback] function to be executed after loadTable()
   *                               completes, Table object is passed in as
   *                               first argument
   *  @return {Object}              Table object containing data
   */
  p5.prototype.loadTable = function (path) {
    var callback = null;
    var options = [];
    var header = false;
    var sep = ',';
    for (var i = 1; i < arguments.length; i++) {
      if (typeof(arguments[i]) === 'function' ){
        callback = arguments[i];
      }
      else if (typeof(arguments[i]) === 'string') {
        options.push(arguments[i]);
        if (arguments[i] === 'header') {
          header = true;
        }
        if (arguments[i] === 'csv') {
          sep = ',';
        }
        else if (arguments[i] === 'tsv') {
          sep = '\t';
        }
      }
    }
    var ret = [];
    var t = new p5.Table();
    var req = new XMLHttpRequest();
    req.open('GET', path, true);
    req.onreadystatechange = function () {
      if (req.readyState === 4 && (req.status === 200 || req.status === 0)) {
        var arr = req.responseText.match(/[^\r\n]+/g);
        for (var k in arr) {
          ret[k] = arr[k];
        }
        if (typeof callback !== 'undefined') {
          var i, row;
          if (header) {
            t.columns = new p5.TableRow(ret[0]).arr;
            for (i = 1; i<ret.length; i++) {
              row = new p5.TableRow(ret[i], sep);
              row.obj = makeObject(row.arr, t.columns);
              t.addRow(row);
            }
          } else {
            // no header: column titles will be numbers
            for (i = 0; i < ret[0].split(sep).length; i++){
              t.columns[i] = i.toString();
            }
            for (i = 0; i<ret.length; i++) {
              row = new p5.TableRow(ret[i], sep);
              t.addRow(row);
            }
          }

          callback(t);
        }
      }
    };
    req.send(null);
    return t;
  };

  // helper function to turn a row into a JSON object
  function makeObject(row, headers) {
    var ret = {};
    headers = headers || [];
    if (typeof(headers) === 'undefined'){
      for (var j = 0; j < row.length; j++ ){
        headers[j.toString()] = j;
      }
    }
    for (var i = 0; i < headers.length; i++){
      var key = headers[i];
      var val = row[i];
      ret[key] = val;
    }
    return ret;
  }

  /**
   * Reads the contents of a file and creates an XML object with its values.
   * If the name of the file is used as the parameter, as in the above example,
   * the file must be located in the sketch directory/folder.
   *
   * Alternatively, the file maybe be loaded from anywhere on the local
   * computer using an absolute path (something that starts with / on Unix and
   * Linux, or a drive letter on Windows), or the filename parameter can be a
   * URL for a file found on a network.
   *
   * This method is asynchronous, meaning it may not finish before the next
   * line in your sketch is executed. Either use preload() to guarantee the
   * file loads before setup() and draw() are called, or supply a callback
   * function that is executed when loadXML() completes.
   *
   * @method loadXML
   * @param  {String}   filename   name of the file or URL to load
   * @param  {Function} [callback] function to be executed after loadXML()
   *                               completes, XML object is passed in as
   *                               first argument
   * @return {Object}              XML object containing data
   */
  p5.prototype.loadXML = function(path, callback) {
    var ret = [];
    reqwest({
      url: path,
      type: 'xml',
      crossOrigin: true,
    })
      .then(function(resp){
        callback(resp);
      });
    return ret;
  };

  // name clash with window.open
  // p5.prototype.open = function() {
  //   // TODO

  // };

  p5.prototype.parseXML = function() {
    // TODO
    throw 'not yet implemented';

  };

  p5.prototype.selectFolder = function() {
    // TODO
    throw 'not yet implemented';

  };

  p5.prototype.selectInput = function() {
    // TODO
    throw 'not yet implemented';

  };

  return p5;
});
<|endoftext|>


<commit_before>toString = Object.prototype.toString;

// thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
isArray = function ( obj ) {
	return toString.call( obj ) === '[object Array]';
};

isEqual = function ( a, b ) {
	if ( a === null && b === null ) {
		return true;
	}

	if ( typeof a === 'object' || typeof b === 'object' ) {
		return false;
	}

	return a === b;
};

// http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric
isNumeric = function ( n ) {
	return !isNaN( parseFloat( n ) ) && isFinite( n );
};

isObject = function ( obj ) {
	return ( toString.call( obj ) === '[object Object]' ) && ( typeof obj !== 'function' );
};<commit_msg>bug fix for the leper known as IE8<commit_after>toString = Object.prototype.toString;

// thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
isArray = function ( obj ) {
	return toString.call( obj ) === '[object Array]';
};

isEqual = function ( a, b ) {
	if ( a === null && b === null ) {
		return true;
	}

	if ( typeof a === 'object' || typeof b === 'object' ) {
		return false;
	}

	return a === b;
};

// http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric
isNumeric = function ( n ) {
	return !isNaN( parseFloat( n ) ) && isFinite( n );
};

isObject = function ( obj ) {
	return ( typeof obj === 'object' && toString.call( obj ) === '[object Object]' );
};<|endoftext|>


<commit_before>import { keys, bindAll, each, isUndefined, debounce } from 'underscore';
import Dragger from 'utils/Dragger';

const evName = 'dmode';

export default {
  run(editor, sender, opts = {}) {
    bindAll(
      this,
      'setPosition',
      'onStart',
      'onDrag',
      'onEnd',
      'getPosition',
      'getGuidesStatic',
      'renderGuide',
      'getGuidesTarget'
    );
    const { target, event, mode, dragger = {} } = opts;
    const el = target.getEl();
    const config = {
      doc: el.ownerDocument,
      onStart: this.onStart,
      onEnd: this.onEnd,
      onDrag: this.onDrag,
      getPosition: this.getPosition,
      setPosition: this.setPosition,
      guidesStatic: () => this.guidesStatic,
      guidesTarget: () => this.guidesTarget,
      ...dragger
    };
    this.setupGuides();
    this.opts = opts;
    this.editor = editor;
    this.em = editor.getModel();
    this.target = target;
    this.isTran = mode == 'translate';
    this.guidesContainer = this.getGuidesContainer();
    this.guidesTarget = this.getGuidesTarget();
    this.guidesStatic = this.getGuidesStatic();
    let drg = this.dragger;

    if (!drg) {
      drg = new Dragger(config);
      this.dragger = drg;
    } else {
      drg.setOptions(config);
    }

    event && drg.start(event);
    this.toggleDrag(1);
    this.em.trigger(`${evName}:start`, this.getEventOpts());

    return drg;
  },

  getEventOpts() {
    return {
      mode: this.opts.mode,
      target: this.target,
      guidesTarget: this.guidesTarget,
      guidesStatic: this.guidesStatic
    };
  },

  stop() {
    this.toggleDrag();
  },

  setupGuides() {
    (this.guides || []).forEach(item => {
      const { guide } = item;
      guide && guide.parentNode.removeChild(guide);
    });
    this.guides = [];
  },

  getGuidesContainer() {
    let { guidesEl } = this;

    if (!guidesEl) {
      const { editor, em, opts } = this;
      const pfx = editor.getConfig('stylePrefix');
      const elInfoX = document.createElement('div');
      const elInfoY = document.createElement('div');
      const guideContent = `

`;
      guidesEl = document.createElement('div');
      guidesEl.className = `${pfx}guides`;
      elInfoX.className = `${pfx}guide-info ${pfx}guide-info__x`;
      elInfoY.className = `${pfx}guide-info ${pfx}guide-info__y`;
      elInfoX.innerHTML = guideContent;
      elInfoY.innerHTML = guideContent;
      guidesEl.appendChild(elInfoX);
      guidesEl.appendChild(elInfoY);
      editor.Canvas.getGlobalToolsEl().appendChild(guidesEl);
      this.guidesEl = guidesEl;
      this.elGuideInfoX = elInfoX;
      this.elGuideInfoY = elInfoY;
      this.elGuideInfoContentX = elInfoX.querySelector(
        `.${pfx}guide-info__content`
      );
      this.elGuideInfoContentY = elInfoY.querySelector(
        `.${pfx}guide-info__content`
      );

      em.on(
        'canvas:update frame:scroll',
        debounce(() => {
          this.updateGuides();
          opts.debug && this.guides.forEach(item => this.renderGuide(item));
        }, 200)
      );
    }

    return guidesEl;
  },

  getGuidesStatic() {
    let result = [];
    const el = this.target.getEl();
    const { parentNode = {} } = el;
    each(
      parentNode.children,
      item =>
        (result = result.concat(el !== item ? this.getElementGuides(item) : []))
    );

    return result.concat(this.getElementGuides(parentNode));
  },

  getGuidesTarget() {
    return this.getElementGuides(this.target.getEl());
  },

  updateGuides(guides) {
    let lastEl, lastPos;
    (guides || this.guides).forEach(item => {
      const { origin } = item;
      const pos = lastEl === origin ? lastPos : this.getElementPos(origin);
      lastEl = origin;
      lastPos = pos;
      each(this.getGuidePosUpdate(item, pos), (val, key) => (item[key] = val));
      item.originRect = pos;
    });
  },

  getGuidePosUpdate(item, rect) {
    const result = {};
    const { top, height, left, width } = rect;

    switch (item.type) {
      case 't':
        result.y = top;
        break;
      case 'b':
        result.y = top + height;
        break;
      case 'l':
        result.x = left;
        break;
      case 'r':
        result.x = left + width;
        break;
      case 'x':
        result.x = left + width / 2;
        break;
      case 'y':
        result.y = top + height / 2;
        break;
    }

    return result;
  },

  renderGuide(item = {}) {
    const el = item.guide || document.createElement('div');
    const un = 'px';
    const guideSize = item.active ? 2 : 1;
    let numEl = el.children[0];
    el.style = `position: absolute; background-color: ${
      item.active ? 'green' : 'red'
    };`;

    if (!el.children.length) {
      numEl = document.createElement('div');
      numEl.style =
        'position: absolute; color: red; padding: 5px; top: 0; left: 0;';
      el.appendChild(numEl);
    }

    if (item.y) {
      el.style.width = '100%';
      el.style.height = `${guideSize}${un}`;
      el.style.top = `${item.y}${un}`;
      el.style.left = 0;
    } else {
      el.style.width = `${guideSize}${un}`;
      el.style.height = '100%';
      el.style.left = `${item.x}${un}`;
      el.style.top = `0${un}`;
    }

    !item.guide && this.guidesContainer.appendChild(el);
    return el;
  },

  getElementPos(el) {
    return this.editor.Canvas.getElementPos(el, { noScroll: 1 });
  },

  getElementGuides(el) {
    const { opts } = this;
    const originRect = this.getElementPos(el);
    const { top, height, left, width } = originRect;
    const guides = [
      { type: 't', y: top }, // Top
      { type: 'b', y: top + height }, // Bottom
      { type: 'l', x: left }, // Left
      { type: 'r', x: left + width }, // Right
      { type: 'x', x: left + width / 2 }, // Mid x
      { type: 'y', y: top + height / 2 } // Mid y
    ].map(item => ({
      ...item,
      origin: el,
      originRect,
      guide: opts.debug && this.renderGuide(item)
    }));
    guides.forEach(item => this.guides.push(item));

    return guides;
  },

  getTranslate(transform, axis = 'x') {
    let result = 0;
    (transform || '').split(' ').forEach(item => {
      const itemStr = item.trim();
      const fn = `translate${axis.toUpperCase()}(`;
      if (itemStr.indexOf(fn) === 0)
        result = parseFloat(itemStr.replace(fn, ''));
    });
    return result;
  },

  setTranslate(transform, axis, value) {
    const fn = `translate${axis.toUpperCase()}(`;
    const val = `${fn}${value})`;
    let result = (transform || '')
      .split(' ')
      .map(item => {
        const itemStr = item.trim();
        if (itemStr.indexOf(fn) === 0) item = val;
        return item;
      })
      .join(' ');
    if (result.indexOf(fn) < 0) result += ` ${val}`;

    return result;
  },

  getPosition() {
    const { target, isTran } = this;
    const { left, top, transform } = target.getStyle();
    let x = 0;
    let y = 0;

    if (isTran) {
      x = this.getTranslate(transform);
      y = this.getTranslate(transform, 'y');
    } else {
      x = parseFloat(left);
      y = parseFloat(top);
    }

    return { x, y };
  },

  setPosition({ x, y, end, position, width, height }) {
    const { target, isTran } = this;
    const unit = 'px';
    const en = !end ? 1 : ''; // this will trigger the final change
    const left = `${x}${unit}`;
    const top = `${y}${unit}`;

    if (isTran) {
      let transform = target.getStyle()['transform'] || '';
      transform = this.setTranslate(transform, 'x', left);
      transform = this.setTranslate(transform, 'y', top);
      return target.addStyle({ transform, en }, { avoidStore: !end });
    }

    const adds = { position, width, height };
    const style = { left, top, en };
    keys(adds).forEach(add => {
      const prop = adds[add];
      if (prop) style[add] = prop;
    });
    target.addStyle(style, { avoidStore: !end });
  },

  _getDragData() {
    const { target } = this;
    return {
      target,
      parent: target.parent(),
      index: target.index()
    };
  },

  onStart(event) {
    const { target, editor, isTran, opts } = this;
    const { center, onStart } = opts;
    const { Canvas } = editor;
    const style = target.getStyle();
    const position = 'absolute';
    onStart && onStart(this._getDragData());
    if (isTran) return;

    if (style.position !== position) {
      let { left, top, width, height } = Canvas.offset(target.getEl());

      // Check if to center the target to the pointer position
      if (center) {
        const { x, y } = Canvas.getMouseRelativeCanvas(event);
        left = x;
        top = y;
      }

      this.setPosition({
        x: left,
        y: top,
        width: `${width}px`,
        height: `${height}px`,
        position
      });
    }
  },

  onDrag(...args) {
    const { guidesTarget, opts } = this;
    const { onDrag } = opts;
    this.updateGuides(guidesTarget);
    opts.debug && guidesTarget.forEach(item => this.renderGuide(item));
    opts.guidesInfo &&
      this.renderGuideInfo(guidesTarget.filter(item => item.active));
    onDrag && onDrag(this._getDragData());
  },

  onEnd(ev, dragger, opt = {}) {
    const { editor, opts, id } = this;
    const { onEnd } = opts;
    onEnd && onEnd(ev, opt, { event: ev, ...opt, ...this._getDragData() });
    editor.stopCommand(id);
    this.hideGuidesInfo();
    this.em.trigger(`${evName}:end`, this.getEventOpts());
  },

  hideGuidesInfo() {
    ['X', 'Y'].forEach(item => {
      const guide = this[`elGuideInfo${item}`];
      if (guide) guide.style.display = 'none';
    });
  },

  /**
   * Render guides with spacing information
   */
  renderGuideInfo(guides = []) {
    const { guidesStatic } = this;
    this.hideGuidesInfo();
    guides.forEach(item => {
      const { origin, x } = item;
      const rectOrigin = this.getElementPos(origin);
      const axis = isUndefined(x) ? 'y' : 'x';
      const isY = axis === 'y';
      const origEdge1 = rectOrigin[isY ? 'left' : 'top'];
      const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top'];
      const origEdge2 = isY
        ? origEdge1 + rectOrigin.width
        : origEdge1 + rectOrigin.height;
      const origEdge2Raw = isY
        ? origEdge1Raw + rectOrigin.rect.width
        : origEdge1Raw + rectOrigin.rect.height;
      const elGuideInfo = this[`elGuideInfo${axis.toUpperCase()}`];
      const elGuideInfoCnt = this[`elGuideInfoContent${axis.toUpperCase()}`];
      const guideInfoStyle = elGuideInfo.style;

      // Find the nearest element
      const res = guidesStatic
        .filter(stat => stat.type === item.type)
        .map(stat => {
          const { left, width, top, height } = stat.originRect;
          const statEdge1 = isY ? left : top;
          const statEdge2 = isY ? left + width : top + height;
          return {
            gap:
              statEdge2 < origEdge1
                ? origEdge1 - statEdge2
                : statEdge1 - origEdge2,
            guide: stat
          };
        })
        .filter(item => item.gap > 0)
        .sort((a, b) => a.gap - b.gap)
        .map(item => item.guide)[0];

      if (res) {
        const { left, width, top, height, rect } = res.originRect;
        const isEdge1 = isY ? left < rectOrigin.left : top < rectOrigin.top;
        const statEdge1 = isY ? left : top;
        const statEdge1Raw = isY ? rect.left : rect.top;
        const statEdge2 = isY ? left + width : top + height;
        const statEdge2Raw = isY
          ? rect.left + rect.width
          : rect.top + rect.height;
        const posFirst = isY ? item.y : item.x;
        const posSecond = isEdge1 ? statEdge2 : origEdge2;
        const pos2 = `${posFirst}px`;
        const size = isEdge1 ? origEdge1 - statEdge2 : statEdge1 - origEdge2;
        const sizeRaw = isEdge1
          ? origEdge1Raw - statEdge2Raw
          : statEdge1Raw - origEdge2Raw;
        guideInfoStyle.display = '';
        guideInfoStyle[isY ? 'top' : 'left'] = pos2;
        guideInfoStyle[isY ? 'left' : 'top'] = `${posSecond}px`;
        guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`;
        elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`;
        this.em.trigger(`${evName}:active`, {
          ...this.getEventOpts(),
          guide: item,
          guidesStatic,
          matched: res,
          posFirst,
          posSecond,
          size,
          sizeRaw,
          elGuideInfo,
          elGuideInfoCnt
        });
      }
    });
  },

  toggleDrag(enable) {
    const { ppfx, editor } = this;
    const methodCls = enable ? 'add' : 'remove';
    const classes = [`${ppfx}is__grabbing`];
    const { Canvas } = editor;
    const body = Canvas.getBody();
    classes.forEach(cls => body.classList[methodCls](cls));
    Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll']();
  }
};
<commit_msg>Update style manager properties on dragMode change. Fixes #2374<commit_after>import { keys, bindAll, each, isUndefined, debounce } from 'underscore';
import Dragger from 'utils/Dragger';

const evName = 'dmode';

export default {
  run(editor, sender, opts = {}) {
    bindAll(
      this,
      'setPosition',
      'onStart',
      'onDrag',
      'onEnd',
      'getPosition',
      'getGuidesStatic',
      'renderGuide',
      'getGuidesTarget'
    );
    const { target, event, mode, dragger = {} } = opts;
    const el = target.getEl();
    const config = {
      doc: el.ownerDocument,
      onStart: this.onStart,
      onEnd: this.onEnd,
      onDrag: this.onDrag,
      getPosition: this.getPosition,
      setPosition: this.setPosition,
      guidesStatic: () => this.guidesStatic,
      guidesTarget: () => this.guidesTarget,
      ...dragger
    };
    this.setupGuides();
    this.opts = opts;
    this.editor = editor;
    this.em = editor.getModel();
    this.target = target;
    this.isTran = mode == 'translate';
    this.guidesContainer = this.getGuidesContainer();
    this.guidesTarget = this.getGuidesTarget();
    this.guidesStatic = this.getGuidesStatic();
    let drg = this.dragger;

    if (!drg) {
      drg = new Dragger(config);
      this.dragger = drg;
    } else {
      drg.setOptions(config);
    }

    event && drg.start(event);
    this.toggleDrag(1);
    this.em.trigger(`${evName}:start`, this.getEventOpts());

    return drg;
  },

  getEventOpts() {
    return {
      mode: this.opts.mode,
      target: this.target,
      guidesTarget: this.guidesTarget,
      guidesStatic: this.guidesStatic
    };
  },

  stop() {
    this.toggleDrag();
  },

  setupGuides() {
    (this.guides || []).forEach(item => {
      const { guide } = item;
      guide && guide.parentNode.removeChild(guide);
    });
    this.guides = [];
  },

  getGuidesContainer() {
    let { guidesEl } = this;

    if (!guidesEl) {
      const { editor, em, opts } = this;
      const pfx = editor.getConfig('stylePrefix');
      const elInfoX = document.createElement('div');
      const elInfoY = document.createElement('div');
      const guideContent = `

`;
      guidesEl = document.createElement('div');
      guidesEl.className = `${pfx}guides`;
      elInfoX.className = `${pfx}guide-info ${pfx}guide-info__x`;
      elInfoY.className = `${pfx}guide-info ${pfx}guide-info__y`;
      elInfoX.innerHTML = guideContent;
      elInfoY.innerHTML = guideContent;
      guidesEl.appendChild(elInfoX);
      guidesEl.appendChild(elInfoY);
      editor.Canvas.getGlobalToolsEl().appendChild(guidesEl);
      this.guidesEl = guidesEl;
      this.elGuideInfoX = elInfoX;
      this.elGuideInfoY = elInfoY;
      this.elGuideInfoContentX = elInfoX.querySelector(
        `.${pfx}guide-info__content`
      );
      this.elGuideInfoContentY = elInfoY.querySelector(
        `.${pfx}guide-info__content`
      );

      em.on(
        'canvas:update frame:scroll',
        debounce(() => {
          this.updateGuides();
          opts.debug && this.guides.forEach(item => this.renderGuide(item));
        }, 200)
      );
    }

    return guidesEl;
  },

  getGuidesStatic() {
    let result = [];
    const el = this.target.getEl();
    const { parentNode = {} } = el;
    each(
      parentNode.children,
      item =>
        (result = result.concat(el !== item ? this.getElementGuides(item) : []))
    );

    return result.concat(this.getElementGuides(parentNode));
  },

  getGuidesTarget() {
    return this.getElementGuides(this.target.getEl());
  },

  updateGuides(guides) {
    let lastEl, lastPos;
    (guides || this.guides).forEach(item => {
      const { origin } = item;
      const pos = lastEl === origin ? lastPos : this.getElementPos(origin);
      lastEl = origin;
      lastPos = pos;
      each(this.getGuidePosUpdate(item, pos), (val, key) => (item[key] = val));
      item.originRect = pos;
    });
  },

  getGuidePosUpdate(item, rect) {
    const result = {};
    const { top, height, left, width } = rect;

    switch (item.type) {
      case 't':
        result.y = top;
        break;
      case 'b':
        result.y = top + height;
        break;
      case 'l':
        result.x = left;
        break;
      case 'r':
        result.x = left + width;
        break;
      case 'x':
        result.x = left + width / 2;
        break;
      case 'y':
        result.y = top + height / 2;
        break;
    }

    return result;
  },

  renderGuide(item = {}) {
    const el = item.guide || document.createElement('div');
    const un = 'px';
    const guideSize = item.active ? 2 : 1;
    let numEl = el.children[0];
    el.style = `position: absolute; background-color: ${
      item.active ? 'green' : 'red'
    };`;

    if (!el.children.length) {
      numEl = document.createElement('div');
      numEl.style =
        'position: absolute; color: red; padding: 5px; top: 0; left: 0;';
      el.appendChild(numEl);
    }

    if (item.y) {
      el.style.width = '100%';
      el.style.height = `${guideSize}${un}`;
      el.style.top = `${item.y}${un}`;
      el.style.left = 0;
    } else {
      el.style.width = `${guideSize}${un}`;
      el.style.height = '100%';
      el.style.left = `${item.x}${un}`;
      el.style.top = `0${un}`;
    }

    !item.guide && this.guidesContainer.appendChild(el);
    return el;
  },

  getElementPos(el) {
    return this.editor.Canvas.getElementPos(el, { noScroll: 1 });
  },

  getElementGuides(el) {
    const { opts } = this;
    const originRect = this.getElementPos(el);
    const { top, height, left, width } = originRect;
    const guides = [
      { type: 't', y: top }, // Top
      { type: 'b', y: top + height }, // Bottom
      { type: 'l', x: left }, // Left
      { type: 'r', x: left + width }, // Right
      { type: 'x', x: left + width / 2 }, // Mid x
      { type: 'y', y: top + height / 2 } // Mid y
    ].map(item => ({
      ...item,
      origin: el,
      originRect,
      guide: opts.debug && this.renderGuide(item)
    }));
    guides.forEach(item => this.guides.push(item));

    return guides;
  },

  getTranslate(transform, axis = 'x') {
    let result = 0;
    (transform || '').split(' ').forEach(item => {
      const itemStr = item.trim();
      const fn = `translate${axis.toUpperCase()}(`;
      if (itemStr.indexOf(fn) === 0)
        result = parseFloat(itemStr.replace(fn, ''));
    });
    return result;
  },

  setTranslate(transform, axis, value) {
    const fn = `translate${axis.toUpperCase()}(`;
    const val = `${fn}${value})`;
    let result = (transform || '')
      .split(' ')
      .map(item => {
        const itemStr = item.trim();
        if (itemStr.indexOf(fn) === 0) item = val;
        return item;
      })
      .join(' ');
    if (result.indexOf(fn) < 0) result += ` ${val}`;

    return result;
  },

  getPosition() {
    const { target, isTran } = this;
    const { left, top, transform } = target.getStyle();
    let x = 0;
    let y = 0;

    if (isTran) {
      x = this.getTranslate(transform);
      y = this.getTranslate(transform, 'y');
    } else {
      x = parseFloat(left);
      y = parseFloat(top);
    }

    return { x, y };
  },

  setPosition({ x, y, end, position, width, height }) {
    const { target, isTran, em } = this;
    const unit = 'px';
    const en = !end ? 1 : ''; // this will trigger the final change
    const left = `${x}${unit}`;
    const top = `${y}${unit}`;
    let styleUp = {};

    if (isTran) {
      let transform = target.getStyle()['transform'] || '';
      transform = this.setTranslate(transform, 'x', left);
      transform = this.setTranslate(transform, 'y', top);
      styleUp = { transform, en };
      target.addStyle(styleUp, { avoidStore: !end });
    } else {
      const adds = { position, width, height };
      const style = { left, top, en };
      keys(adds).forEach(add => {
        const prop = adds[add];
        if (prop) style[add] = prop;
      });
      styleUp = style;
      target.addStyle(styleUp, { avoidStore: !end });
    }

    // Update StyleManager properties
    em.getSelected() &&
      keys(styleUp).forEach(i => em.trigger(`update:component:style:${i}`));
  },

  _getDragData() {
    const { target } = this;
    return {
      target,
      parent: target.parent(),
      index: target.index()
    };
  },

  onStart(event) {
    const { target, editor, isTran, opts } = this;
    const { center, onStart } = opts;
    const { Canvas } = editor;
    const style = target.getStyle();
    const position = 'absolute';
    onStart && onStart(this._getDragData());
    if (isTran) return;

    if (style.position !== position) {
      let { left, top, width, height } = Canvas.offset(target.getEl());

      // Check if to center the target to the pointer position
      if (center) {
        const { x, y } = Canvas.getMouseRelativeCanvas(event);
        left = x;
        top = y;
      }

      this.setPosition({
        x: left,
        y: top,
        width: `${width}px`,
        height: `${height}px`,
        position
      });
    }
  },

  onDrag(...args) {
    const { guidesTarget, opts } = this;
    const { onDrag } = opts;
    this.updateGuides(guidesTarget);
    opts.debug && guidesTarget.forEach(item => this.renderGuide(item));
    opts.guidesInfo &&
      this.renderGuideInfo(guidesTarget.filter(item => item.active));
    onDrag && onDrag(this._getDragData());
  },

  onEnd(ev, dragger, opt = {}) {
    const { editor, opts, id } = this;
    const { onEnd } = opts;
    onEnd && onEnd(ev, opt, { event: ev, ...opt, ...this._getDragData() });
    editor.stopCommand(id);
    this.hideGuidesInfo();
    this.em.trigger(`${evName}:end`, this.getEventOpts());
  },

  hideGuidesInfo() {
    ['X', 'Y'].forEach(item => {
      const guide = this[`elGuideInfo${item}`];
      if (guide) guide.style.display = 'none';
    });
  },

  /**
   * Render guides with spacing information
   */
  renderGuideInfo(guides = []) {
    const { guidesStatic } = this;
    this.hideGuidesInfo();
    guides.forEach(item => {
      const { origin, x } = item;
      const rectOrigin = this.getElementPos(origin);
      const axis = isUndefined(x) ? 'y' : 'x';
      const isY = axis === 'y';
      const origEdge1 = rectOrigin[isY ? 'left' : 'top'];
      const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top'];
      const origEdge2 = isY
        ? origEdge1 + rectOrigin.width
        : origEdge1 + rectOrigin.height;
      const origEdge2Raw = isY
        ? origEdge1Raw + rectOrigin.rect.width
        : origEdge1Raw + rectOrigin.rect.height;
      const elGuideInfo = this[`elGuideInfo${axis.toUpperCase()}`];
      const elGuideInfoCnt = this[`elGuideInfoContent${axis.toUpperCase()}`];
      const guideInfoStyle = elGuideInfo.style;

      // Find the nearest element
      const res = guidesStatic
        .filter(stat => stat.type === item.type)
        .map(stat => {
          const { left, width, top, height } = stat.originRect;
          const statEdge1 = isY ? left : top;
          const statEdge2 = isY ? left + width : top + height;
          return {
            gap:
              statEdge2 < origEdge1
                ? origEdge1 - statEdge2
                : statEdge1 - origEdge2,
            guide: stat
          };
        })
        .filter(item => item.gap > 0)
        .sort((a, b) => a.gap - b.gap)
        .map(item => item.guide)[0];

      if (res) {
        const { left, width, top, height, rect } = res.originRect;
        const isEdge1 = isY ? left < rectOrigin.left : top < rectOrigin.top;
        const statEdge1 = isY ? left : top;
        const statEdge1Raw = isY ? rect.left : rect.top;
        const statEdge2 = isY ? left + width : top + height;
        const statEdge2Raw = isY
          ? rect.left + rect.width
          : rect.top + rect.height;
        const posFirst = isY ? item.y : item.x;
        const posSecond = isEdge1 ? statEdge2 : origEdge2;
        const pos2 = `${posFirst}px`;
        const size = isEdge1 ? origEdge1 - statEdge2 : statEdge1 - origEdge2;
        const sizeRaw = isEdge1
          ? origEdge1Raw - statEdge2Raw
          : statEdge1Raw - origEdge2Raw;
        guideInfoStyle.display = '';
        guideInfoStyle[isY ? 'top' : 'left'] = pos2;
        guideInfoStyle[isY ? 'left' : 'top'] = `${posSecond}px`;
        guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`;
        elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`;
        this.em.trigger(`${evName}:active`, {
          ...this.getEventOpts(),
          guide: item,
          guidesStatic,
          matched: res,
          posFirst,
          posSecond,
          size,
          sizeRaw,
          elGuideInfo,
          elGuideInfoCnt
        });
      }
    });
  },

  toggleDrag(enable) {
    const { ppfx, editor } = this;
    const methodCls = enable ? 'add' : 'remove';
    const classes = [`${ppfx}is__grabbing`];
    const { Canvas } = editor;
    const body = Canvas.getBody();
    classes.forEach(cls => body.classList[methodCls](cls));
    Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll']();
  }
};
<|endoftext|>


<commit_before>

var magicSelection = {

    start: function($scope) {
        $("#main-canvas").css("cursor", "crosshair");

        /* working with $scope to share variables between functions in this file does not seem to work. */
        var scope = angular.element($("#main-canvas")).scope();

        scope.imgData = new ImgData($scope.renderEngine.getWidth(), $scope.renderEngine.getHeight());
        scope.imgData.data = $scope.renderEngine.renderAsUint8Array();
        scope.magic = new MagicSelection(scope.imgData);

        // $interval(callAtInterval, 1000);
        scope.sizeAnts = 4;
        scope.offset = 0;
    },

    mouseDown: function($scope) {
        var scope = angular.element($("#main-canvas")).scope();

        /* x and y coordinates in pixels relative to image. */
        xRelative = $scope.config.mouse.current.x - $scope.config.canvas.x;
        yRelative = $scope.config.mouse.current.y - $scope.config.canvas.y;

        /* Check wheter user has clicked inside of a selection. */
        if (scope.magic.isInSelection(xRelative, yRelative)) {
            scope.magic.removeSelection(xRelative, yRelative)
        } else {
            scope.magic.getMaskWand(xRelative, yRelative, $scope.tresholdValue);
        }

        /* Save border and marching ants mask in scope. */
        $scope.maskBorder = $scope.magic.getMaskBorder();
        $scope.maskAnts = $scope.magic.marchingAnts(scope.sizeAnts * 2, scope.offset);
    },

    mouseUp: function($scope) {
    },

    mouseMove: function($scope) {
        // TODO call mouseDown
    },

    callAtInterval: function($scope) {
        console.log("test");
        scope.offset++;
        $scope.maskAnts = $scope.magic.marchingAnts(scope.sizeAnts * 2, scope.offset);
    }
}<commit_msg>kleine aanpassing<commit_after>

var magicSelection = {

    start: function($scope) {
        $("#main-canvas").css("cursor", "crosshair");

        /* working with $scope to share variables between functions in this file does not seem to work. */
        var scope = angular.element($("#main-canvas")).scope();

        scope.imgData = new ImgData($scope.renderEngine.getWidth(), $scope.renderEngine.getHeight());
        scope.imgData.data = $scope.renderEngine.renderAsUint8Array();
        scope.magic = new MagicSelection(scope.imgData);

        // $interval(callAtInterval, 1000);
        scope.sizeAnts = 4;
        scope.offset = 0;
    },

    mouseDown: function($scope) {
        var scope = angular.element($("#main-canvas")).scope();

        /* x and y coordinates in pixels relative to image. */
        xRelative = $scope.config.mouse.current.x - $scope.config.canvas.x;
        yRelative = $scope.config.mouse.current.y - $scope.config.canvas.y;
        console.log(xRelative, yRelative);

        /* Check wheter user has clicked inside of a selection. */
        if (scope.magic.isInSelection(xRelative, yRelative)) {
            scope.magic.removeSelection(xRelative, yRelative)
        } else {
            scope.magic.getMaskWand(xRelative, yRelative, $scope.tresholdValue);
        }

        /* Save border and marching ants mask in scope. */
        $scope.maskBorder = $scope.magic.getMaskBorder();
        $scope.maskAnts = $scope.magic.marchingAnts(scope.sizeAnts * 2, scope.offset);
    },

    mouseUp: function($scope) {
    },

    mouseMove: function($scope) {
        // TODO call mouseDown
    },

    callAtInterval: function($scope) {
        console.log("test");
        scope.offset++;
        $scope.maskAnts = $scope.magic.marchingAnts(scope.sizeAnts * 2, scope.offset);
    }
}<|endoftext|>


<commit_before>import { deferred } from './deferred';
import { log } from 'util';

export const itemService = {
	async getItem(id) {
		var remoteDb = await window.db.getDb();
		return remoteDb
			.doc(`items/${id}`)
			.get()
			.then(doc => {
				return doc.data();
			});
	},
	async getUserItemIds() {
		if (window.user) {
			return new Promise(resolve => {
				resolve(window.user.items || {});
			});
		}
		var remoteDb = await window.db.getDb();
		return remoteDb
			.doc(`users/${window.user.uid}`)
			.get()
			.then(doc => {
				if (!doc.exists) {
					return {};
				}
				return doc.data().items;
			});
	},

	async getAllItems() {
		var t = Date.now();
		var d = deferred();
		let itemIds = await this.getUserItemIds();
		itemIds = Object.getOwnPropertyNames(itemIds || {});
		log('itemids', itemIds);

		if (!itemIds.length) {
			d.resolve([]);
		}
		var remoteDb = await window.db.getDb();
		const items = [];
		remoteDb
			.collection('items')
			.where('createdBy', '==', window.user.uid)
			.onSnapshot(
				function(querySnapshot) {
					querySnapshot.forEach(function(doc) {
						items.push(doc.data());
					});
					log('Items fetched in ', Date.now() - t, 'ms');

					d.resolve(items);
				},
				function() {
					d.resolve([]);
				}
			);
		return d.promise;
	},

	async setUser() {
		const remoteDb = await window.db.getDb();
		return remoteDb.doc(`users/${window.user.uid}`).set({
			items: {}
		});
	},

	async setItem(id, item) {
		const d = deferred();
		var remotePromise;
		// TODO: check why we need to save locally always?
		const obj = {
			[id]: item
		};
		db.local.set(obj, () => {
			// Is extension OR is app but logged out OR is logged in but offline
			// If logged in but offline, resolve immediately so
			// that you see the feedback msg immediately and not wait for
			// later sync.
			if (window.IS_EXTENSION || !window.user || !navigator.onLine) {
				d.resolve();
			}
		});

		// If `id` is `code`, this is a call on unloadbefore to save the last open thing.
		// Do not presist that on remote.
		if (id === 'code') {
			// No deferred required here as this gets called on unloadbefore
			return false;
		}
		if (window.user) {
			var remoteDb = await window.db.getDb();
			log(`Starting to save item ${id}`);
			item.createdBy = window.user.uid;
			remotePromise = remoteDb
				.collection('items')
				.doc(id)
				.set(item, {
					merge: true
				})
				.then(arg => {
					log('Document written', arg);
					d.resolve();
				})
				.catch(d.reject);
		}

		return window.user && navigator.onLine ? remotePromise : d.promise;
	},

	/**
	 * Saves the passed items in the database.
	 * @param {Array} items to be saved in DB
	 */
	saveItems(items) {
		var d = deferred();
		// When not logged in
		if (!window.user) {
			// save new items
			window.db.local.set(items, d.resolve);
			// Push in new item IDs
			window.db.local.get(
				{
					items: {}
				},
				function(result) {
					/* eslint-disable guard-for-in */
					for (var id in items) {
						result.items[id] = true;
					}
					window.db.local.set({
						items: result.items
					});
					/* eslint-enable guard-for-in */
				}
			);
		} else {
			window.db.getDb().then(remoteDb => {
				const batch = remoteDb.batch();
				/* eslint-disable guard-for-in */
				for (var id in items) {
					items[id].createdBy = window.user.uid;
					batch.set(remoteDb.doc(`items/${id}`), items[id]);
					batch.update(remoteDb.doc(`users/${window.user.uid}`), {
						[`items.${id}`]: true
					});
					// Set these items on our cached user object too
					window.user.items = window.user.items || {};
					window.user.items[id] = true;
				}
				batch.commit().then(d.resolve);
				/* eslint-enable guard-for-in */
			});
		}
		return d.promise;
	},

	async removeItem(id) {
		// When not logged in
		if (!window.user) {
			var d = deferred();
			window.db.local.remove(id, d.resolve);
			return d.promise;
		}
		const remoteDb = await window.db.getDb();
		log(`Starting to save item ${id}`);
		return remoteDb
			.collection('items')
			.doc(id)
			.delete()
			.then(arg => {
				log('Document removed', arg);
			})
			.catch(error => log(error));
	},

	async setItemForUser(itemId) {
		// When not logged in
		if (!window.user) {
			return window.db.local.get(
				{
					items: {}
				},
				function(result) {
					result.items[itemId] = true;
					window.db.local.set({
						items: result.items
					});
				}
			);
		}
		const remoteDb = await window.db.getDb();
		return remoteDb
			.collection('users')
			.doc(window.user.uid)
			.update({
				[`items.${itemId}`]: true
			})
			.then(arg => {
				log(`Item ${itemId} set for user`, arg);
				window.user.items = window.user.items || {};
				window.user.items[itemId] = true;
			})
			.catch(error => log(error));
	},

	async unsetItemForUser(itemId) {
		// When not logged in
		if (!window.user) {
			return window.db.local.get(
				{
					items: {}
				},
				function(result) {
					delete result.items[itemId];
					window.db.local.set({
						items: result.items
					});
				}
			);
		}
		const remoteDb = await window.db.getDb();
		return remoteDb
			.collection('users')
			.doc(window.user.uid)
			.update({
				[`items.${itemId}`]: firebase.firestore.FieldValue.delete()
			})
			.then(arg => {
				delete window.user.items[itemId];
				log(`Item ${itemId} unset for user`, arg);
			})
			.catch(error => log(error));
	}
};
<commit_msg>add missing import statement to fix the error when delete a document.<commit_after>import { deferred } from './deferred';
import { log } from 'util';
import * as firebase from 'firebase';

export const itemService = {
	async getItem(id) {
		var remoteDb = await window.db.getDb();
		return remoteDb
			.doc(`items/${id}`)
			.get()
			.then(doc => {
				return doc.data();
			});
	},
	async getUserItemIds() {
		if (window.user) {
			return new Promise(resolve => {
				resolve(window.user.items || {});
			});
		}
		var remoteDb = await window.db.getDb();
		return remoteDb
			.doc(`users/${window.user.uid}`)
			.get()
			.then(doc => {
				if (!doc.exists) {
					return {};
				}
				return doc.data().items;
			});
	},

	async getAllItems() {
		var t = Date.now();
		var d = deferred();
		let itemIds = await this.getUserItemIds();
		itemIds = Object.getOwnPropertyNames(itemIds || {});
		log('itemids', itemIds);

		if (!itemIds.length) {
			d.resolve([]);
		}
		var remoteDb = await window.db.getDb();
		const items = [];
		remoteDb
			.collection('items')
			.where('createdBy', '==', window.user.uid)
			.onSnapshot(
				function(querySnapshot) {
					querySnapshot.forEach(function(doc) {
						items.push(doc.data());
					});
					log('Items fetched in ', Date.now() - t, 'ms');

					d.resolve(items);
				},
				function() {
					d.resolve([]);
				}
			);
		return d.promise;
	},

	async setUser() {
		const remoteDb = await window.db.getDb();
		return remoteDb.doc(`users/${window.user.uid}`).set({
			items: {}
		});
	},

	async setItem(id, item) {
		const d = deferred();
		var remotePromise;
		// TODO: check why we need to save locally always?
		const obj = {
			[id]: item
		};
		db.local.set(obj, () => {
			// Is extension OR is app but logged out OR is logged in but offline
			// If logged in but offline, resolve immediately so
			// that you see the feedback msg immediately and not wait for
			// later sync.
			if (window.IS_EXTENSION || !window.user || !navigator.onLine) {
				d.resolve();
			}
		});

		// If `id` is `code`, this is a call on unloadbefore to save the last open thing.
		// Do not presist that on remote.
		if (id === 'code') {
			// No deferred required here as this gets called on unloadbefore
			return false;
		}
		if (window.user) {
			var remoteDb = await window.db.getDb();
			log(`Starting to save item ${id}`);
			item.createdBy = window.user.uid;
			remotePromise = remoteDb
				.collection('items')
				.doc(id)
				.set(item, {
					merge: true
				})
				.then(arg => {
					log('Document written', arg);
					d.resolve();
				})
				.catch(d.reject);
		}

		return window.user && navigator.onLine ? remotePromise : d.promise;
	},

	/**
	 * Saves the passed items in the database.
	 * @param {Array} items to be saved in DB
	 */
	saveItems(items) {
		var d = deferred();
		// When not logged in
		if (!window.user) {
			// save new items
			window.db.local.set(items, d.resolve);
			// Push in new item IDs
			window.db.local.get(
				{
					items: {}
				},
				function(result) {
					/* eslint-disable guard-for-in */
					for (var id in items) {
						result.items[id] = true;
					}
					window.db.local.set({
						items: result.items
					});
					/* eslint-enable guard-for-in */
				}
			);
		} else {
			window.db.getDb().then(remoteDb => {
				const batch = remoteDb.batch();
				/* eslint-disable guard-for-in */
				for (var id in items) {
					items[id].createdBy = window.user.uid;
					batch.set(remoteDb.doc(`items/${id}`), items[id]);
					batch.update(remoteDb.doc(`users/${window.user.uid}`), {
						[`items.${id}`]: true
					});
					// Set these items on our cached user object too
					window.user.items = window.user.items || {};
					window.user.items[id] = true;
				}
				batch.commit().then(d.resolve);
				/* eslint-enable guard-for-in */
			});
		}
		return d.promise;
	},

	async removeItem(id) {
		// When not logged in
		if (!window.user) {
			var d = deferred();
			window.db.local.remove(id, d.resolve);
			return d.promise;
		}
		const remoteDb = await window.db.getDb();
		log(`Starting to save item ${id}`);
		return remoteDb
			.collection('items')
			.doc(id)
			.delete()
			.then(arg => {
				log('Document removed', arg);
			})
			.catch(error => log(error));
	},

	async setItemForUser(itemId) {
		// When not logged in
		if (!window.user) {
			return window.db.local.get(
				{
					items: {}
				},
				function(result) {
					result.items[itemId] = true;
					window.db.local.set({
						items: result.items
					});
				}
			);
		}
		const remoteDb = await window.db.getDb();
		return remoteDb
			.collection('users')
			.doc(window.user.uid)
			.update({
				[`items.${itemId}`]: true
			})
			.then(arg => {
				log(`Item ${itemId} set for user`, arg);
				window.user.items = window.user.items || {};
				window.user.items[itemId] = true;
			})
			.catch(error => log(error));
	},

	async unsetItemForUser(itemId) {
		// When not logged in
		if (!window.user) {
			return window.db.local.get(
				{
					items: {}
				},
				function(result) {
					delete result.items[itemId];
					window.db.local.set({
						items: result.items
					});
				}
			);
		}
		const remoteDb = await window.db.getDb();
		return remoteDb
			.collection('users')
			.doc(window.user.uid)
			.update({
				[`items.${itemId}`]: firebase.firestore.FieldValue.delete()
			})
			.then(arg => {
				delete window.user.items[itemId];
				log(`Item ${itemId} unset for user`, arg);
			})
			.catch(error => log(error));
	}
};
<|endoftext|>


<commit_before>"use strict";
/**
 * JQuery Rest Client Plugin v1.3 https://git.oschina.net/anylain/RestClient
 *
 * Copyright 2014 Pan Ying Released under the Apache 2.0 License
 */
(function($) {

	$.RestClient = function(options) {

		var version = '1.3-alpha';
		var currOptions = null;
		var _this = this;

		// 获取当前版本
		this.getVersion = function() {
			return version;
		};

		// 替换字符串中的 {key} 内容, 一个简单的小模板引擎
		var _gsub = function(source, pattern, replacement) {
			var result = '', match;
			while (source.length > 0) {
				match = source.match(pattern);
				if (match) {
					result += source.slice(0, match.index);
					result += replacement(match).toString();
					source = source.slice(match.index + match[0].length);
				} else {
					result += source;
					source = '';
				}
			}
			return result;
		};

		// 克隆对象, 这个方法只能克隆简单对象
		this._clone = function(obj) {
			return $.extend(true, {}, obj);
		};

		// 更新 或者 叠加 options
		this._updateOptions = function(source, newValue, force) {
			var result;
			if (source === undefined) {
				result = newValue;
			} else if (source !== null && $.type(source) === 'object') {
				result = {};
				for ( var key in source) {
					if (!source.hasOwnProperty(key))
						continue;
					result[key] = source[key];
				}
				for ( var key in newValue) {
					if (!newValue.hasOwnProperty(key))
						continue;
					result[key] = this._updateOptions(source[key],
							newValue[key], force);
				}
			} else if (force) {
				result = newValue;
			} else {
				result = source;
			}
			return result;
		};

		// 兼容性处理,当浏览器不支持 HTTP 1.1 进行的一些方法替换方案
		this._compatibleHandler = function(method, request) {
			if (!request.compatible) {
				request.type = method;
				return;
			}

			if ($.isFunction(request.compatible)) {
				request.compatible(method, request);
				return;
			}

			switch (request.compatible) {
			case 'url':
				request.type = 'POST';
				request.urlParams = request.urlParams || {};
				request.urlParams['__method'] = method;
				break;

			case 'x-method':
				request.type = 'POST';
				request.headers = request.headers || {};
				request.headers['x-method-override'] = method;
				break;

			default:
				throw new Error('Unsupport rest compatible mode "'
						+ request.compatible + '"');
			}
		};

		// 更新当前配置
		this.updateOptions = function(newOptions) {
			currOptions = this._updateOptions(currOptions, newOptions, true);
			return this;
		};

		// 使用新配置替换当前配置
		this.setOptions = function(newOptions) {
			currOptions = newOptions;
			return this;
		};

		// 获取当前配置
		this.getOptions = function() {
			return currOptions;
		};

		// 构造请求地址
		this.buildUrl = function(sourceUrl, urlParams, pathParams, queryParams,
				ext) {
			pathParams = $.extend(true, {}, urlParams, pathParams);
			var usedPathParams = [];
			var result = !sourceUrl ? "" : _gsub(sourceUrl, /\{(.+?)\}/,
					function(match) {
						var key = match[1];
						var value = pathParams[key];
						if (value === undefined || value === null) {
							$.error("Can't found '" + key
									+ "' from pathParams or urlParams.");
						}
						usedPathParams.push(key);
						return encodeURIComponent(value);
					});

			if (ext) {
				result += ext;
			}

			queryParams = queryParams ? this._clone(queryParams) : {};
			if (urlParams) {
				$.each(urlParams, function(key, value) {
					if ($.inArray(key, usedPathParams) === -1
							&& queryParams[key] === undefined) {
						queryParams[key] = value;
					}
				});
			}
			var queryString = this.buildQueryString(queryParams);
			if (queryString) {
				result += '?' + queryString;
			}

			return result;
		};

		// 构造queryString
		this.buildQueryString = function(queryParams) {
			return $.param(queryParams);
		};

		// 构造请求对象, 该对象可以直接用于$.ajax方法
		// 该方法在构造失败时会调用$.error抛出异常
		this.buildRequest = function(method, request, addon) {
			var result = {};

			result = this._clone(request);

			if (addon) {
				result = this._updateOptions(result, addon, true);
			}

			result = this._updateOptions(result, currOptions);

			var successHandler = result.success;
			var errorHandler = result.error;

			// 序列化请求体
			if (result.serializeRequestBody) {
				try {
					result.data = result.serializeRequestBody(result);
				} catch (e) {
					$.error('Serialize request body fault. ' + e.message);
				}
			}

			// 请求成功回调包装方法, 会尝试反序列化返回体
			result.success = function(data, textStatus, jqXHR) {
				if (result.deserializeResponseBody) {
					try {
						data = result.deserializeResponseBody(data, result);
					} catch (e) {
						$
								.error('Deserialize response body fault. '
										+ e.message);
					}
				}
				if (successHandler)
					successHandler(data, textStatus, jqXHR);
			};

			// 请求失败回调包装方法, 会尝试反序列化返回体
			result.error = function(jqXHR, textStatus, errorThrown) {
				var message;
				if (result.deserializeError) {
					message = result.deserializeError(jqXHR, textStatus,
							errorThrown, result);
				} else {
					message = (jqXHR && jqXHR.responseText) || errorThrown
							|| textStatus;
				}
				if (errorHandler) {
					errorHandler(message, jqXHR, textStatus, errorThrown);
				}
			};

			// 构造请求地址
			result.url = this.buildUrl(result.url, result.urlParams,
					result.pathParams, result.queryParams, result.ext);
			if (result.baseUrl) {
				result.url = result.baseUrl + result.url;
			}

			// 兼容性处理
			this._compatibleHandler(method, result);

			return result;
		};

		// 立刻发送一个请求, 该请求会捕获构造期异常, 转而调用请求失败回调方法
		this.sendRequest = function(method, request, addon) {

			try {
				var r = this.buildRequest(method, request, addon);
				return $.ajax(r);
			} catch (e) {
				var tryCallComplete = function() {
					var completeHandler = (addon && addon.complete)
							|| (request && request.complete)
							|| _this.getOptions().complete;
					if (completeHandler)
						completeHandler();
				};
				var errorHandler = (addon && addon.error)
						|| (request && request.error)
						|| _this.getOptions().error;
				if (errorHandler) {
					errorHandler(e.message, null, null, e);
					tryCallComplete();
				} else {
					$.error(e.message);
				}
			}
		};

		this.get = function(request, addon) {
			return this.sendRequest('GET', request, addon);
		};

		this.post = function(request, addon) {
			return this.sendRequest('POST', request, addon);
		};

		this.put = function(request, addon) {
			return this.sendRequest('PUT', request, addon);
		};

		this.del = function(request, addon) {
			return this.sendRequest('DELETE', request, addon);
		};

		this.setOptions(options || $.RestClient.options.defaults);

	};

	// 配置模板容器
	$.RestClient.options = {};

	// json 默认配置模板
	$.RestClient.options.json = {
		baseUrl : null,
		compatible : null,
		dataType : "json",
		contentType : "application/json",
		headers : {
			accept : "application/json"
		},
		// ext: ".json", // 资源后缀，如果目标API以URL后缀区分资源类型可以加上这个参数
		serializeRequestBody : function(request) {
			if (JSON && JSON.stringify) {
				return JSON.stringify(request.data);
			} else if ($.toJSON) {
				return $.toJSON(request.data);
			}
			throw new Error(
					'Need jQuery json plugin to serialize request body.');
		},
		deserializeResponseBody : null,
		deserializeError : null,
		// 默认的错误处理
		error : function(message, jqXHR, textStatus, errorThrown) {
			alert(message);
		}

	};

	// xml默认配置模板
	$.RestClient.options.xml = {
		dataType : "xml",
		headers : {
			accept : "application/xml"
		},
		// ext: ".xml",
		contentType : "application/xml"
	};

	// 文本类型默认配置模板
	$.RestClient.options.text = {
		dataType : "text",
		headers : {
			accept : "text/plain"
		}
	};

	// 使用 json 默认配置
	$.RestClient.options.defaults = $.RestClient.options.json;

	// 初始化默认实例
	$.rest = new $.RestClient();

})(jQuery);
<commit_msg>* set version to v1.3<commit_after>"use strict";
/**
 * JQuery Rest Client Plugin v1.3 https://git.oschina.net/anylain/RestClient
 *
 * Copyright 2014 Pan Ying Released under the Apache 2.0 License
 */
(function($) {

	$.RestClient = function(options) {

		var version = '1.3';
		var currOptions = null;
		var _this = this;

		// 获取当前版本
		this.getVersion = function() {
			return version;
		};

		// 替换字符串中的 {key} 内容, 一个简单的小模板引擎
		var _gsub = function(source, pattern, replacement) {
			var result = '', match;
			while (source.length > 0) {
				match = source.match(pattern);
				if (match) {
					result += source.slice(0, match.index);
					result += replacement(match).toString();
					source = source.slice(match.index + match[0].length);
				} else {
					result += source;
					source = '';
				}
			}
			return result;
		};

		// 克隆对象, 这个方法只能克隆简单对象
		this._clone = function(obj) {
			return $.extend(true, {}, obj);
		};

		// 更新 或者 叠加 options
		this._updateOptions = function(source, newValue, force) {
			var result;
			if (source === undefined) {
				result = newValue;
			} else if (source !== null && $.type(source) === 'object') {
				result = {};
				for ( var key in source) {
					if (!source.hasOwnProperty(key))
						continue;
					result[key] = source[key];
				}
				for ( var key in newValue) {
					if (!newValue.hasOwnProperty(key))
						continue;
					result[key] = this._updateOptions(source[key],
							newValue[key], force);
				}
			} else if (force) {
				result = newValue;
			} else {
				result = source;
			}
			return result;
		};

		// 兼容性处理,当浏览器不支持 HTTP 1.1 进行的一些方法替换方案
		this._compatibleHandler = function(method, request) {
			if (!request.compatible) {
				request.type = method;
				return;
			}

			if ($.isFunction(request.compatible)) {
				request.compatible(method, request);
				return;
			}

			switch (request.compatible) {
			case 'url':
				request.type = 'POST';
				request.urlParams = request.urlParams || {};
				request.urlParams['__method'] = method;
				break;

			case 'x-method':
				request.type = 'POST';
				request.headers = request.headers || {};
				request.headers['x-method-override'] = method;
				break;

			default:
				throw new Error('Unsupport rest compatible mode "'
						+ request.compatible + '"');
			}
		};

		// 更新当前配置
		this.updateOptions = function(newOptions) {
			currOptions = this._updateOptions(currOptions, newOptions, true);
			return this;
		};

		// 使用新配置替换当前配置
		this.setOptions = function(newOptions) {
			currOptions = newOptions;
			return this;
		};

		// 获取当前配置
		this.getOptions = function() {
			return currOptions;
		};

		// 构造请求地址
		this.buildUrl = function(sourceUrl, urlParams, pathParams, queryParams,
				ext) {
			pathParams = $.extend(true, {}, urlParams, pathParams);
			var usedPathParams = [];
			var result = !sourceUrl ? "" : _gsub(sourceUrl, /\{(.+?)\}/,
					function(match) {
						var key = match[1];
						var value = pathParams[key];
						if (value === undefined || value === null) {
							$.error("Can't found '" + key
									+ "' from pathParams or urlParams.");
						}
						usedPathParams.push(key);
						return encodeURIComponent(value);
					});

			if (ext) {
				result += ext;
			}

			queryParams = queryParams ? this._clone(queryParams) : {};
			if (urlParams) {
				$.each(urlParams, function(key, value) {
					if ($.inArray(key, usedPathParams) === -1
							&& queryParams[key] === undefined) {
						queryParams[key] = value;
					}
				});
			}
			var queryString = this.buildQueryString(queryParams);
			if (queryString) {
				result += '?' + queryString;
			}

			return result;
		};

		// 构造queryString
		this.buildQueryString = function(queryParams) {
			return $.param(queryParams);
		};

		// 构造请求对象, 该对象可以直接用于$.ajax方法
		// 该方法在构造失败时会调用$.error抛出异常
		this.buildRequest = function(method, request, addon) {
			var result = {};

			result = this._clone(request);

			if (addon) {
				result = this._updateOptions(result, addon, true);
			}

			result = this._updateOptions(result, currOptions);

			var successHandler = result.success;
			var errorHandler = result.error;

			// 序列化请求体
			if (result.serializeRequestBody) {
				try {
					result.data = result.serializeRequestBody(result);
				} catch (e) {
					$.error('Serialize request body fault. ' + e.message);
				}
			}

			// 请求成功回调包装方法, 会尝试反序列化返回体
			result.success = function(data, textStatus, jqXHR) {
				if (result.deserializeResponseBody) {
					try {
						data = result.deserializeResponseBody(data, result);
					} catch (e) {
						$
								.error('Deserialize response body fault. '
										+ e.message);
					}
				}
				if (successHandler)
					successHandler(data, textStatus, jqXHR);
			};

			// 请求失败回调包装方法, 会尝试反序列化返回体
			result.error = function(jqXHR, textStatus, errorThrown) {
				var message;
				if (result.deserializeError) {
					message = result.deserializeError(jqXHR, textStatus,
							errorThrown, result);
				} else {
					message = (jqXHR && jqXHR.responseText) || errorThrown
							|| textStatus;
				}
				if (errorHandler) {
					errorHandler(message, jqXHR, textStatus, errorThrown);
				}
			};

			// 构造请求地址
			result.url = this.buildUrl(result.url, result.urlParams,
					result.pathParams, result.queryParams, result.ext);
			if (result.baseUrl) {
				result.url = result.baseUrl + result.url;
			}

			// 兼容性处理
			this._compatibleHandler(method, result);

			return result;
		};

		// 立刻发送一个请求, 该请求会捕获构造期异常, 转而调用请求失败回调方法
		this.sendRequest = function(method, request, addon) {

			try {
				var r = this.buildRequest(method, request, addon);
				return $.ajax(r);
			} catch (e) {
				var tryCallComplete = function() {
					var completeHandler = (addon && addon.complete)
							|| (request && request.complete)
							|| _this.getOptions().complete;
					if (completeHandler)
						completeHandler();
				};
				var errorHandler = (addon && addon.error)
						|| (request && request.error)
						|| _this.getOptions().error;
				if (errorHandler) {
					errorHandler(e.message, null, null, e);
					tryCallComplete();
				} else {
					$.error(e.message);
				}
			}
		};

		this.get = function(request, addon) {
			return this.sendRequest('GET', request, addon);
		};

		this.post = function(request, addon) {
			return this.sendRequest('POST', request, addon);
		};

		this.put = function(request, addon) {
			return this.sendRequest('PUT', request, addon);
		};

		this.del = function(request, addon) {
			return this.sendRequest('DELETE', request, addon);
		};

		this.setOptions(options || $.RestClient.options.defaults);

	};

	// 配置模板容器
	$.RestClient.options = {};

	// json 默认配置模板
	$.RestClient.options.json = {
		baseUrl : null,
		compatible : null,
		dataType : "json",
		contentType : "application/json",
		headers : {
			accept : "application/json"
		},
		// ext: ".json", // 资源后缀，如果目标API以URL后缀区分资源类型可以加上这个参数
		serializeRequestBody : function(request) {
			if (JSON && JSON.stringify) {
				return JSON.stringify(request.data);
			} else if ($.toJSON) {
				return $.toJSON(request.data);
			}
			throw new Error(
					'Need jQuery json plugin to serialize request body.');
		},
		deserializeResponseBody : null,
		deserializeError : null,
		// 默认的错误处理
		error : function(message, jqXHR, textStatus, errorThrown) {
			alert(message);
		}

	};

	// xml默认配置模板
	$.RestClient.options.xml = {
		dataType : "xml",
		headers : {
			accept : "application/xml"
		},
		// ext: ".xml",
		contentType : "application/xml"
	};

	// 文本类型默认配置模板
	$.RestClient.options.text = {
		dataType : "text",
		headers : {
			accept : "text/plain"
		}
	};

	// 使用 json 默认配置
	$.RestClient.options.defaults = $.RestClient.options.json;

	// 初始化默认实例
	$.rest = new $.RestClient();

})(jQuery);
<|endoftext|>


<commit_before>/* global vis, MashupPlatform, console, moment, EventEditor, CalendarAPI, RegionAPI, UserAPI */

var Calendar = (function (vis) {
  "use strict";

  var container;
  var events = new vis.DataSet();
  var regions = new vis.DataSet();
  var options = {};
  var timeline = {};
  var eventEditor;
  var calendarAPI;
  var userAPI;
  var regionAPI;
  var user;
  var userRol;

  var ROLES = {
    DEMO: "Demo",
    INFRASTRUCTUREOWNER: "InfrastructureOwner"
  };

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

  function Calendar () {}

  /******************************************************************/
  /*                P R I V A T E   F U N C T I O N S               */
  /******************************************************************/

  function updateEvents () { timeline.setItems(events); }

  function updateRegions () { timeline.setGroups(regions); }

  function updateOptions () { timeline.setOptions(options); }

  function obtainEvents () {
    calendarAPI.getAllEvents(
      function (response) {
        console.log("Success obtaining events.");
        events.clear();
        JSON.parse(response.response).events.forEach(function(event) {
          var newEvent = {
            id: event.uid,
            content: event.description,
            title: event.summary.replace(/\\\n/g, "\n"),
            group: event.location,
            start: event.dtstart,
            end: event.dtend,
            className: "",
            editable: false,
            type: 'range'
          };
          if (event.location === "Demos") {
            newEvent.className = "demo";
          } else {
            newEvent.className = "maintenance";
          }

          if (userRol === ROLES.DEMO && event.location === "Demos") {
            newEvent.editable = true;
          } else {
            if (isInfrastructureOwner(event.location)) {
              newEvent.editable = true;
            } else {
              newEvent.editable = false;
            }
          }

          events.add(newEvent);
        }, this);
      },
      function () {
        console.log("Error obtaining events.");
      }
    );
  }

  function obtainRegions () {
    regionAPI.getRegions(
      function(response) {
        console.log("Success obtaining regions.");
        var object = JSON.parse(response.response);

        regions.clear();
        regions.add({id: "Demos", content: "Demos"});

        object._embedded.regions.forEach(function(region) {
          regions.add({id: region.id, content: region.id});
        }, this);
      },
      function(response) {
        console.log("Error obtaining regions." + response);
    });
  }

  function obtainUser () {
    //TODO: Get User Access Token
    userAPI.getUser(
      function(response) {
        user = JSON.parse(response.response);
        if (isInfrastructureOwner()) {
          userRol = "InfrastructureOwner";
        } else {
          userRol = "Demo";
        }
        console.log("Success obtaining user.");
      },
      function(response) {
        console.log("Error obtaining user." + response);
    });
  }

  function isInfrastructureOwner (region) {
    if(region) {
      var FIDASHRegion = region + " FIDASH";
      for (var index = 0; index < user.organizations.length; index++) {
        if (FIDASHRegion === user.organizations[index].name) {
          return true;
        }
      }
      return false;
    } else {
      return user.organizations.length > 0;
    }
  }

  function saveNewEvent(event) {
    var eventAPI = {
      location: event.group,
      summary: event.title.replace(/\n/g, "\\n"),
      description: event.content,
      dtend: moment(event.end).format("YYYY-MM-DD HH:mm:ssZZ"),
      dtstart: moment(event.start).format("YYYY-MM-DD HH:mm:ssZZ")
    };

    calendarAPI.addEvent(eventAPI,
    function (response) {
        events.remove(event.id);
        console.log("Created Event Successfully.");
        event.id = JSON.parse(response.response).event.uid;
        events.add(event);
        eventEditor.hideEventEditor();
    },
    function () {
        console.log("Error creating Event");
    });

  }

  function saveEvent(event) {
    var eventAPI = {
      location: event.group,
      summary: event.title.replace(/\n/g, "\\n"),
      description: event.content,
      dtend: moment(event.end).format("YYYY-MM-DD HH:mm:ssZZ"),
      dtstart: moment(event.start).format("YYYY-MM-DD HH:mm:ssZZ"),
      uid: event.id
    };
    calendarAPI.updateEvent(eventAPI,
      function (response) {
        console.log("Event added (Update) successfully.");
        events.remove(event.id);
        event.id = JSON.parse(response.response).event.uid;
        events.update(event);
        eventEditor.hideEventEditor();
      },
      function () {
    });
  }

  function showEventEditor(props) {
    var event;
    if (props.what === "background") {
      event = {
        title: '',
        content: '',
        start: new Date(props.time.getTime()),
        end: new Date(props.time.getTime() + 21600000),
        group: props.group,
        type: 'range',
        className: (props.group === "Demos") ? 'demo' : 'maintenance',
        editable: true
      };
      eventEditor.showEventEditor(event, saveNewEvent);
    }
    if (props.what === "item") {
      event = events.get(props.item);
      if (event.editable)
        eventEditor.showEventEditor(event, saveEvent);
    }
  }

  function doubleClick (props) {
    switch (userRol) {
      case ROLES.DEMO:
        if (props.group === "Demos") {
          showEventEditor(props);
        }
        break;
      case ROLES.INFRASTRUCTUREOWNER:
        if (isInfrastructureOwner(props.group)) {
          showEventEditor(props);
        }
        break;
      default:
        break;
    }
  }

  function getDefaultOptions() {
    return {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 86400000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      },
      groupOrder: function (a, b) {
          if (a.id === "Demos") {
            return -1;
          }
          if (b.id === "Demos") {
            return 1;
          }

          var groups = [a.id, b.id];
          groups.sort();
          if (a.id === groups[0]) {
            return -1;
          } else {
            return 1;
          }
      }
    };
  }

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

  Calendar.prototype = {
    init: function (calendarContainer, calendarOptions) {
      console.log("Start Timeline v0.8.2");

      calendarAPI = new CalendarAPI();
      regionAPI = new RegionAPI();
      userAPI = new UserAPI();

      container = calendarContainer;
      if (typeof calendarOptions !== 'undefined') {
        options = getDefaultOptions();
      } else {
        options = calendarOptions;
      }

      options.onMove = function (item, callback) {
        item.title = item.content + "\n" + "Start: " + moment(item.start).format('YYYY-MM-DD HH:mm') + "\n" + "End: " + moment(item.end).format('YYYY-MM-DD HH:mm');
        var event = {
          location: item.group,
          summary: item.title.replace(/\n/g, "\\n"),
          description: item.content,
          dtend: moment(item.end).format("YYYY-MM-DD HH:mm:ssZZ"),
          dtstart: moment(item.start).format("YYYY-MM-DD HH:mm:ssZZ"),
          uid: item.id
        };
        calendarAPI.updateEvent(event,
          function (response) {
            console.log("Event added (Update) successfully.");
            events.remove(item.id);
            item.id = JSON.parse(response.response).event.uid;
            events.update(item);
            //callback(item);
          },
          function () {
            callback(null);
        });
      };
      options.onRemove = function (item, callback) {
        var event = {
          location: item.group,
          summary: item.title.replace(/\n/g, "\\n"),
          description: item.content,
          dtend: moment(item.end).format("YYYY-MM-DD HH:mm:ssZZ"),
          dtstart: moment(item.start).format("YYYY-MM-DD HH:mm:ssZZ"),
          uid: item.id
        };
        calendarAPI.deleteEvent(event,
          function () {
            callback(item);
          },
          function () {
            callback(null);
        });
      };

      regions.on("*", updateRegions);
      events.on("*", updateEvents);

      timeline = new vis.Timeline(container, events, regions, options);

      obtainUser();
      obtainRegions();
      obtainEvents();

      timeline.setOptions(options);

      //Events Editor
      eventEditor = new EventEditor();
      eventEditor.setUp();

      timeline.on('doubleClick', doubleClick);

      timeline.setWindow(moment().subtract(7, 'days'), moment().add(7, 'days'));
    }
  };

  return Calendar;
})(vis);<commit_msg>Add more logs<commit_after>/* global vis, MashupPlatform, console, moment, EventEditor, CalendarAPI, RegionAPI, UserAPI */

var Calendar = (function (vis) {
  "use strict";

  var container;
  var events = new vis.DataSet();
  var regions = new vis.DataSet();
  var options = {};
  var timeline = {};
  var eventEditor;
  var calendarAPI;
  var userAPI;
  var regionAPI;
  var user;
  var userRol;

  var ROLES = {
    DEMO: "Demo",
    INFRASTRUCTUREOWNER: "InfrastructureOwner"
  };

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

  function Calendar () {}

  /******************************************************************/
  /*                P R I V A T E   F U N C T I O N S               */
  /******************************************************************/

  function updateEvents () { timeline.setItems(events); }

  function updateRegions () { timeline.setGroups(regions); }

  function updateOptions () { timeline.setOptions(options); }

  function obtainEvents () {
    calendarAPI.getAllEvents(
      function (response) {
        console.log("Success obtaining events.");
        events.clear();
        JSON.parse(response.response).events.forEach(function(event) {
          var newEvent = {
            id: event.uid,
            content: event.description,
            title: event.summary.replace(/\\\n/g, "\n"),
            group: event.location,
            start: moment(event.dtstart),
            end: moment(event.dtend),
            className: "",
            editable: false,
            type: 'range'
          };
          if (event.location === "Demos") {
            newEvent.className = "demo";
          } else {
            newEvent.className = "maintenance";
          }

          if (userRol === ROLES.DEMO && event.location === "Demos") {
            newEvent.editable = true;
          } else {
            if (isInfrastructureOwner(event.location)) {
              newEvent.editable = true;
            } else {
              newEvent.editable = false;
            }
          }
          events.add(newEvent);
        }, this);
      },
      function (response) {
        console.log("Error obtaining events. " + response);
      }
    );
  }

  function obtainRegions () {
    regionAPI.getRegions(
      function(response) {
        console.log("Success obtaining regions.");
        var object = JSON.parse(response.response);

        regions.clear();
        if (userRol === ROLES.DEMO) {
          regions.add({id: "Demos", content: "Demos ", className: "editable"});
        } else {
          regions.add({id: "Demos", content: "Demos"});
        }

        object._embedded.regions.forEach(function(region) {
          if(isInfrastructureOwner(region.id)) {
            regions.add({id: region.id, content: region.id + " ", className: "editable"});
          } else {
            regions.add({id: region.id, content: region.id});
          }
        }, this);
      },
      function(response) {
        console.log("Error obtaining regions. " + response);
    });
  }

  function obtainUser () {
    userAPI.getUser(
      function(response) {
        user = JSON.parse(response.response);
        if (isInfrastructureOwner()) {
          userRol = "InfrastructureOwner";
        } else {
          userRol = "Demo";
        }
        console.log("Success obtaining user.");
      },
      function(response) {
        console.log("Error obtaining user. " + response);
    });
  }

  function isInfrastructureOwner (region) {
    if(region) {
      var FIDASHRegion = region + " FIDASH";
      for (var index = 0; index < user.organizations.length; index++) {
        if (FIDASHRegion === user.organizations[index].name) {
          return true;
        }
      }
      return false;
    } else {
      return user.organizations.length > 0;
    }
  }

  function saveNewEvent(event) {
    var eventAPI = {
      location: event.group,
      summary: event.title.replace(/\n/g, "\\n"),
      description: event.content,
      dtend: moment(event.end).format("YYYY-MM-DD HH:mm:ssZZ"),
      dtstart: moment(event.start).format("YYYY-MM-DD HH:mm:ssZZ")
    };

    calendarAPI.addEvent(eventAPI,
    function (response) {
        console.log("Created Event Successfully.");
        events.remove(event.id);
        event.id = JSON.parse(response.response).event.uid;
        events.add(event);
        eventEditor.hideEventEditor();
    },
    function (response) {
        console.log("Error creating Event. " + response);
    });

  }

  function saveEvent(event) {
    var eventAPI = {
      location: event.group,
      summary: event.title.replace(/\n/g, "\\n"),
      description: event.content,
      dtend: moment(event.end).format("YYYY-MM-DD HH:mm:ssZZ"),
      dtstart: moment(event.start).format("YYYY-MM-DD HH:mm:ssZZ"),
      uid: event.id
    };

    calendarAPI.updateEvent(eventAPI,
      function (response) {
        console.log("Event updated successfully.");
        events.remove(event.id);
        event.id = JSON.parse(response.response).event.uid;
        events.update(event);
        eventEditor.hideEventEditor();
      },
      function (response) {
        console.log("Error updating Event. " + response);
    });
  }

  function showEventEditor(props) {
    var event;
    if (props.what === "background") {
      event = {
        title: '',
        content: '',
        start: new Date(props.time.getTime()),
        end: new Date(props.time.getTime() + 21600000),
        group: props.group,
        type: 'range',
        className: (props.group === "Demos") ? 'demo' : 'maintenance',
        editable: true
      };
      eventEditor.showEventEditor(event, saveNewEvent);
    }
    if (props.what === "item") {
      event = events.get(props.item);
      if (event.editable)
        eventEditor.showEventEditor(event, saveEvent);
    }
  }

  function doubleClick (props) {
    switch (userRol) {
      case ROLES.DEMO:
        if (props.group === "Demos") {
          showEventEditor(props);
        }
        break;
      case ROLES.INFRASTRUCTUREOWNER:
        if (isInfrastructureOwner(props.group)) {
          showEventEditor(props);
        }
        break;
      default:
        break;
    }
  }

  function getDefaultOptions() {
    return {
      height: "100vh",
      orientation: "top",
      zoomKey: 'shiftKey',
      zoomMax: 315360000000,
      zoomMin: 86400000,
      editable: {
        add: false,
        updateTime: true,
        updateGroup: false,
        remove: true
      },
      groupOrder: function (a, b) {
          if (a.id === "Demos") {
            return -1;
          }
          if (b.id === "Demos") {
            return 1;
          }

          var groups = [a.id, b.id];
          groups.sort();
          if (a.id === groups[0]) {
            return -1;
          } else {
            return 1;
          }
      },
      moment: function(date) {
        return vis.moment(date).utcOffset('+01:00');
      }
    };
  }

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

  Calendar.prototype = {
    init: function (calendarContainer, calendarOptions) {
      console.log("Start Timeline v0.8.2");

      calendarAPI = new CalendarAPI();
      regionAPI = new RegionAPI();
      userAPI = new UserAPI();

      container = calendarContainer;
      if (typeof calendarOptions !== 'undefined') {
        options = getDefaultOptions();
      } else {
        options = calendarOptions;
      }

      options.onMove = function (item, callback) {
        item.title = item.content + "\n" + "Start: " + moment(item.start).format('YYYY-MM-DD HH:mm') + "\n" + "End: " + moment(item.end).format('YYYY-MM-DD HH:mm');
        var event = {
          location: item.group,
          summary: item.title.replace(/\n/g, "\\n"),
          description: item.content,
          dtend: moment(item.end).format("YYYY-MM-DD HH:mm:ssZZ"),
          dtstart: moment(item.start).format("YYYY-MM-DD HH:mm:ssZZ"),
          uid: item.id
        };
        calendarAPI.updateEvent(event,
          function (response) {
            console.log("Event updated successfully.");
            events.remove(item.id);
            item.id = JSON.parse(response.response).event.uid;
            events.update(item);
          },
          function (response) {
            console.log("Error updating Event. " + response);
            callback(null);
        });
      };
      options.onRemove = function (item, callback) {
        var event = {
          location: item.group,
          summary: item.title.replace(/\n/g, "\\n"),
          description: item.content,
          dtend: moment(item.end).format("YYYY-MM-DD HH:mm:ssZZ"),
          dtstart: moment(item.start).format("YYYY-MM-DD HH:mm:ssZZ"),
          uid: item.id
        };
        calendarAPI.deleteEvent(event,
          function () {
            console.log("Event deleted successfully.");
            callback(item);
          },
          function (response) {
            console.log("Error deleting Event. " + response);
            callback(null);
        });
      };

      regions.on("*", updateRegions);
      events.on("*", updateEvents);

      timeline = new vis.Timeline(container, events, regions, options);

      obtainUser();
      obtainEvents();
      obtainRegions();

      timeline.setOptions(options);

      //Events Editor
      eventEditor = new EventEditor();
      eventEditor.setUp();

      timeline.on('doubleClick', doubleClick);
      timeline.setWindow(moment().subtract(2, 'days'), moment().add(13, 'days'));
    }
  };

  return Calendar;
})(vis);<|endoftext|>