<commit_before>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Bootstrap support for Dart scripts on the page as this script.
if (navigator.webkitStartDart) {
  if (!navigator.webkitStartDart()) {
    document.body.innerHTML = 'This build has expired.  Please download a new Dartium at http://www.dartlang.org/dartium/index.html';
  }
} else {
  // TODO:
  // - Support in-browser compilation.
  // - Handle inline Dart scripts.
  window.addEventListener("DOMContentLoaded", function (e) {
    // Fall back to compiled JS. Run through all the scripts and
    // replace them if they have a type that indicate that they source
    // in Dart code.
    //
    //   <script type="application/dart" src="..."></script>
    //
    var scripts = document.getElementsByTagName("script");
    var length = scripts.length;
    for (var i = 0; i < length; ++i) {
      if (scripts[i].type == "application/dart") {
        // Remap foo.dart to foo.dart.js.
        if (scripts[i].src && scripts[i].src != '') {
          var script = document.createElement('script');
          script.src = scripts[i].src + '.js';
          var parent = scripts[i].parentNode;
          parent.replaceChild(script, scripts[i]);
        }
      }
    }
  }, false);
}

// ---------------------------------------------------------------------------
// Experimental support for JS interoperability
// ---------------------------------------------------------------------------
function SendPortSync() {
}

function ReceivePortSync() {
  this.id = ReceivePortSync.id++;
  ReceivePortSync.map[this.id] = this;
}

(function() {
  // Serialize the following types as follows:
  //  - primitives / null: unchanged
  //  - lists: [ 'list', internal id, list of recursively serialized elements ]
  //  - maps: [ 'map', internal id, map of keys and recursively serialized values ]
  //  - send ports: [ 'sendport', type, isolate id, port id ]
  //
  // Note, internal id's are for cycle detection.
  function serialize(message) {
    var visited = [];
    function checkedSerialization(obj, serializer) {
      // Implementation detail: for now use linear search.
      // Another option is expando, but it may prohibit
      // VM optimizations (like putting object into slow mode
      // on property deletion.)
      var id = visited.indexOf(obj);
      if (id != -1) return [ 'ref', id ];
      var id = visited.length;
      visited.push(obj);
      return serializer(id);
    }

    function doSerialize(message) {
      if (message == null) {
        return null;  // Convert undefined to null.
      } else if (typeof(message) == 'string' ||
                 typeof(message) == 'number' ||
                 typeof(message) == 'boolean') {
        return message;
      } else if (message instanceof Array) {
        return checkedSerialization(message, function(id) {
          var values = new Array(message.length);
          for (var i = 0; i < message.length; i++) {
            values[i] = doSerialize(message[i]);
          }
          return [ 'list', id, values ];
        });
      } else if (message instanceof LocalSendPortSync) {
        return [ 'sendport', 'nativejs', message.receivePort.id ];
      } else if (message instanceof DartSendPortSync) {
        return [ 'sendport', 'dart', message.isolateId, message.portId ];
      } else {
        return checkedSerialization(message, function(id) {
          var keys = Object.getOwnPropertyNames(message);
          var values = new Array(keys.length);
          for (var i = 0; i < keys.length; i++) {
            values[i] = doSerialize(message[keys[i]]);
          }
          return [ 'map', id, keys, values ];
        });
      }
    }
    return doSerialize(message);
  }

  function deserialize(message) {
    return deserializeHelper(message);
  }

  function deserializeHelper(message) {
    if (message == null ||
        typeof(message) == 'string' ||
        typeof(message) == 'number' ||
        typeof(message) == 'boolean') {
      return message;
    }
    switch (message[0]) {
      case 'map': return deserializeMap(message);
      case 'sendport': return deserializeSendPort(message);
      case 'list': return deserializeList(message);
      default: throw 'unimplemented';
    }
  }

  function deserializeMap(message) {
    var result = { };
    var id = message[1];
    var keys = message[2];
    var values = message[3];
    for (var i = 0, length = keys.length; i < length; i++) {
      var key = deserializeHelper(keys[i]);
      var value = deserializeHelper(values[i]);
      result[key] = value;
    }
    return result;
  }

  function deserializeSendPort(message) {
    var tag = message[1];
    switch (tag) {
      case 'nativejs':
        var id = message[2];
        return new LocalSendPortSync(ReceivePortSync.map[id]);
      case 'dart':
        var isolateId = message[2];
        var portId = message[3];
        return new DartSendPortSync(isolateId, portId);
      default:
        throw 'Illegal SendPortSync type: $tag';
    }
  }

  function deserializeList(message) {
    var values = message[2];
    var length = values.length;
    var result = new Array(length);
    for (var i = 0; i < length; i++) {
      result[i] = deserializeHelper(values[i]);
    }
    return result;
  }

  window.registerPort = function(name, port) {
    var stringified = JSON.stringify(serialize(port));
    window.localStorage['dart-port:' + name] = stringified;
  };

  window.lookupPort = function(name) {
    var stringified = window.localStorage['dart-port:' + name];
    return deserialize(JSON.parse(stringified));
  };

  ReceivePortSync.id = 0;
  ReceivePortSync.map = {};

  ReceivePortSync.dispatchCall = function(id, message) {
    // TODO(vsm): Handle and propagate exceptions.
    var deserialized = deserialize(message);
    var result = ReceivePortSync.map[id].callback(deserialized);
    return serialize(result);
  };

  ReceivePortSync.prototype.receive = function(callback) {
    this.callback = callback;
  };

  ReceivePortSync.prototype.toSendPort = function() {
    return new LocalSendPortSync(this);
  };

  ReceivePortSync.prototype.close = function() {
    delete ReceivePortSync.map[this.id];
  };

  if (navigator.webkitStartDart) {
    window.addEventListener('js-sync-message', function(event) {
      var data = JSON.parse(getPortSyncEventData(event));
      var deserialized = deserialize(data.message);
      var result = ReceivePortSync.map[data.id].callback(deserialized);
      // TODO(vsm): Handle and propagate exceptions.
      dispatchEvent('js-result', serialize(result));
    }, false);
  }

  function LocalSendPortSync(receivePort) {
    this.receivePort = receivePort;
  }

  LocalSendPortSync.prototype = new SendPortSync();

  LocalSendPortSync.prototype.callSync = function(message) {
    // TODO(vsm): Do a direct deepcopy.
    message = deserialize(serialize(message));
    return this.receivePort.callback(message);
  }

  function DartSendPortSync(isolateId, portId) {
    this.isolateId = isolateId;
    this.portId = portId;
  }

  DartSendPortSync.prototype = new SendPortSync();

  function dispatchEvent(receiver, message) {
    var string = JSON.stringify(message);
    var event = document.createEvent('CustomEvent');
    event.initCustomEvent(receiver, false, false, string);
    window.dispatchEvent(event);
  }

  function getPortSyncEventData(event) {
    return event.detail;
  }

  DartSendPortSync.prototype.callSync = function(message) {
    var serialized = serialize(message);
    var target = 'dart-port-' + this.isolateId + '-' + this.portId;
    // TODO(vsm): Make this re-entrant.
    // TODO(vsm): Set this up set once, on the first call.
    var source = target + '-result';
    var result = null;
    var listener = function (e) {
      result = JSON.parse(getPortSyncEventData(e));
    };
    window.addEventListener(source, listener, false);
    dispatchEvent(target, [source, serialized]);
    window.removeEventListener(source, listener, false);
    return deserialize(result);
  }
})();
<commit_msg>update to latest dart.js<commit_after>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Bootstrap support for Dart scripts on the page as this script.
if (navigator.webkitStartDart) {
  if (!navigator.webkitStartDart()) {
    document.body.innerHTML = 'This build has expired.  Please download a new Dartium at http://www.dartlang.org/dartium/index.html';
  }
} else {
  // TODO:
  // - Support in-browser compilation.
  // - Handle inline Dart scripts.
  window.addEventListener("DOMContentLoaded", function (e) {
    // Fall back to compiled JS. Run through all the scripts and
    // replace them if they have a type that indicate that they source
    // in Dart code.
    //
    //   <script type="application/dart" src="..."></script>
    //
    var scripts = document.getElementsByTagName("script");
    var length = scripts.length;
    for (var i = 0; i < length; ++i) {
      if (scripts[i].type == "application/dart") {
        // Remap foo.dart to foo.dart.js.
        if (scripts[i].src && scripts[i].src != '') {
          var script = document.createElement('script');
          script.src = scripts[i].src + '.js';
          var parent = scripts[i].parentNode;
          parent.replaceChild(script, scripts[i]);
        }
      }
    }
  }, false);
}

// ---------------------------------------------------------------------------
// Experimental support for JS interoperability
// ---------------------------------------------------------------------------
function SendPortSync() {
}

function ReceivePortSync() {
  this.id = ReceivePortSync.id++;
  ReceivePortSync.map[this.id] = this;
}

(function() {
  // Serialize the following types as follows:
  //  - primitives / null: unchanged
  //  - lists: [ 'list', internal id, list of recursively serialized elements ]
  //  - maps: [ 'map', internal id, map of keys and recursively serialized values ]
  //  - send ports: [ 'sendport', type, isolate id, port id ]
  //
  // Note, internal id's are for cycle detection.
  function serialize(message) {
    var visited = [];
    function checkedSerialization(obj, serializer) {
      // Implementation detail: for now use linear search.
      // Another option is expando, but it may prohibit
      // VM optimizations (like putting object into slow mode
      // on property deletion.)
      var id = visited.indexOf(obj);
      if (id != -1) return [ 'ref', id ];
      var id = visited.length;
      visited.push(obj);
      return serializer(id);
    }

    function doSerialize(message) {
      if (message == null) {
        return null;  // Convert undefined to null.
      } else if (typeof(message) == 'string' ||
                 typeof(message) == 'number' ||
                 typeof(message) == 'boolean') {
        return message;
      } else if (message instanceof Array) {
        return checkedSerialization(message, function(id) {
          var values = new Array(message.length);
          for (var i = 0; i < message.length; i++) {
            values[i] = doSerialize(message[i]);
          }
          return [ 'list', id, values ];
        });
      } else if (message instanceof LocalSendPortSync) {
        return [ 'sendport', 'nativejs', message.receivePort.id ];
      } else if (message instanceof DartSendPortSync) {
        return [ 'sendport', 'dart', message.isolateId, message.portId ];
      } else {
        return checkedSerialization(message, function(id) {
          var keys = Object.getOwnPropertyNames(message);
          var values = new Array(keys.length);
          for (var i = 0; i < keys.length; i++) {
            values[i] = doSerialize(message[keys[i]]);
          }
          return [ 'map', id, keys, values ];
        });
      }
    }
    return doSerialize(message);
  }

  function deserialize(message) {
    return deserializeHelper(message);
  }

  function deserializeHelper(message) {
    if (message == null ||
        typeof(message) == 'string' ||
        typeof(message) == 'number' ||
        typeof(message) == 'boolean') {
      return message;
    }
    switch (message[0]) {
      case 'map': return deserializeMap(message);
      case 'sendport': return deserializeSendPort(message);
      case 'list': return deserializeList(message);
      default: throw 'unimplemented';
    }
  }

  function deserializeMap(message) {
    var result = { };
    var id = message[1];
    var keys = message[2];
    var values = message[3];
    for (var i = 0, length = keys.length; i < length; i++) {
      var key = deserializeHelper(keys[i]);
      var value = deserializeHelper(values[i]);
      result[key] = value;
    }
    return result;
  }

  function deserializeSendPort(message) {
    var tag = message[1];
    switch (tag) {
      case 'nativejs':
        var id = message[2];
        return new LocalSendPortSync(ReceivePortSync.map[id]);
      case 'dart':
        var isolateId = message[2];
        var portId = message[3];
        return new DartSendPortSync(isolateId, portId);
      default:
        throw 'Illegal SendPortSync type: $tag';
    }
  }

  function deserializeList(message) {
    var values = message[2];
    var length = values.length;
    var result = new Array(length);
    for (var i = 0; i < length; i++) {
      result[i] = deserializeHelper(values[i]);
    }
    return result;
  }

  window.registerPort = function(name, port) {
    var stringified = JSON.stringify(serialize(port));
    var attrName = 'dart-port:' + name;
    document.documentElement.setAttribute(attrName, stringified);
    // TODO(vsm): Phase out usage of localStorage.  We're leaving it in
    // temporarily for backwards compatibility.
    window.localStorage[attrName] = stringified;
  };

  window.lookupPort = function(name) {
    var attrName = 'dart-port:' + name;
    var stringified = document.documentElement.getAttribute(attrName);
    // TODO(vsm): Phase out usage of localStorage.  We're leaving it in
    // temporarily for backwards compatibility.
    if (!stringified) {
      stringified = window.localStorage[attrName];
    }
    return deserialize(JSON.parse(stringified));
  };

  ReceivePortSync.id = 0;
  ReceivePortSync.map = {};

  ReceivePortSync.dispatchCall = function(id, message) {
    // TODO(vsm): Handle and propagate exceptions.
    var deserialized = deserialize(message);
    var result = ReceivePortSync.map[id].callback(deserialized);
    return serialize(result);
  };

  ReceivePortSync.prototype.receive = function(callback) {
    this.callback = callback;
  };

  ReceivePortSync.prototype.toSendPort = function() {
    return new LocalSendPortSync(this);
  };

  ReceivePortSync.prototype.close = function() {
    delete ReceivePortSync.map[this.id];
  };

  if (navigator.webkitStartDart) {
    window.addEventListener('js-sync-message', function(event) {
      var data = JSON.parse(getPortSyncEventData(event));
      var deserialized = deserialize(data.message);
      var result = ReceivePortSync.map[data.id].callback(deserialized);
      // TODO(vsm): Handle and propagate exceptions.
      dispatchEvent('js-result', serialize(result));
    }, false);
  }

  function LocalSendPortSync(receivePort) {
    this.receivePort = receivePort;
  }

  LocalSendPortSync.prototype = new SendPortSync();

  LocalSendPortSync.prototype.callSync = function(message) {
    // TODO(vsm): Do a direct deepcopy.
    message = deserialize(serialize(message));
    return this.receivePort.callback(message);
  }

  function DartSendPortSync(isolateId, portId) {
    this.isolateId = isolateId;
    this.portId = portId;
  }

  DartSendPortSync.prototype = new SendPortSync();

  function dispatchEvent(receiver, message) {
    var string = JSON.stringify(message);
    var event = document.createEvent('CustomEvent');
    event.initCustomEvent(receiver, false, false, string);
    window.dispatchEvent(event);
  }

  function getPortSyncEventData(event) {
    return event.detail;
  }

  DartSendPortSync.prototype.callSync = function(message) {
    var serialized = serialize(message);
    var target = 'dart-port-' + this.isolateId + '-' + this.portId;
    // TODO(vsm): Make this re-entrant.
    // TODO(vsm): Set this up set once, on the first call.
    var source = target + '-result';
    var result = null;
    var listener = function (e) {
      result = JSON.parse(getPortSyncEventData(e));
    };
    window.addEventListener(source, listener, false);
    dispatchEvent(target, [source, serialized]);
    window.removeEventListener(source, listener, false);
    return deserialize(result);
  }
})();
<|endoftext|>


<commit_before>"use strict";

/**************************
 * Import important stuff *
 **************************/

const moment = require("moment");

/********************
 * Create the class *
 ********************/

/**
 * Handles mapping between message IDs in discord and telegram, for message editing purposes
 */
class MessageMap {
	constructor() {
		/**
		 * The map itself
		 *
		 * @private
		 */
		this._map = new Map();

		/**
		 * The timeout map
		 *
		 * @private
		 */
		this._timeoutMap = new Map();
	}

	/**
	 * Inserts a mapping into the map
	 *
	 * @param {String} direction	One of the two direction constants of this class
	 * @param {Bridge} bridge	The bridge this mapping is for
	 * @param {String} fromId	Message ID to map from, i.e. the ID of the message the bot received
	 * @param {String} toId	Message ID to map to, i.e. the ID of the message the bot sent
	 */
	insert(direction, bridge, fromId, toId) {
		// Get/create the entry for the bridge
		let keyToIdsMap = this._map.get(bridge);
		if (keyToIdsMap === undefined) {
			keyToIdsMap = new Map();
			this._map.set(bridge, keyToIdsMap);
		}

		// Generate the key and get the corresponding IDs
		const key = `${direction} ${fromId}`;
		let toIds = keyToIdsMap.get(key);
		if (toIds === undefined) {
			toIds = new Set();
			keyToIdsMap.set(key, toIds);
		}

		// Shove the new ID into it
		toIds.add(toId);

		// Start a timeout removing it again after 24 hours
		setTimeout(() => {
			keyToIdsMap.delete(key);
		}, moment.duration(24, "hours").asMilliseconds());
	}

	/**
	 * Gets the ID of a message the bot sent based on the ID of the message the bot received
	 *
	 * @param {String} direction	One of the two direction constants of this class
	 * @param {Bridge} bridge	The bridge this mapping is for
	 * @param {String} fromId	Message ID to get corresponding ID for, i.e. the ID of the message the bot received the message
	 *
	 * @returns {String[]}	Message IDs of the corresponding message, i.e. the IDs of the messages the bot sent
	 */
	getCorresponding(direction, bridge, fromId) {
		try {
			// Get the key-to-IDs map
			const keyToIdsMap = this._map.get(bridge);

			// Create the key
			const key = `${direction} ${fromId}`;

			// Extract the IDs
			const toIds = keyToIdsMap.get(key);

			// Return the ID
			return [...toIds];
		} catch (err) {
			// Unknown message ID. Don't do anything
			return [];
		}
	}

	getCorrespondingReverse(direction, bridge, toId) {
		// The ID to return
		let fromId = [];

		// Get the mappings for this bridge
		const keyToIdsMap = this._map.get(bridge);
		if (keyToIdsMap !== undefined) {
			// Find the ID
			const [key]  = [...keyToIdsMap].find(([, ids]) => ids.has(toId));
			fromId = key.split(" ");
			fromId.shift();
		}

		return fromId;
	}

	/**
	 * Constant indicating direction discord to telegram
	 */
	static get DISCORD_TO_TELEGRAM() {
		return "d2t";
	}

	/**
	 * Constant indicating direction telegram to discord
	 */
	static get TELEGRAM_TO_DISCORD() {
		return "t2d";
	}
}

/********************
 * Export the class *
 ********************/

module.exports = MessageMap;
<commit_msg>Removed a rouge space<commit_after>"use strict";

/**************************
 * Import important stuff *
 **************************/

const moment = require("moment");

/********************
 * Create the class *
 ********************/

/**
 * Handles mapping between message IDs in discord and telegram, for message editing purposes
 */
class MessageMap {
	constructor() {
		/**
		 * The map itself
		 *
		 * @private
		 */
		this._map = new Map();

		/**
		 * The timeout map
		 *
		 * @private
		 */
		this._timeoutMap = new Map();
	}

	/**
	 * Inserts a mapping into the map
	 *
	 * @param {String} direction	One of the two direction constants of this class
	 * @param {Bridge} bridge	The bridge this mapping is for
	 * @param {String} fromId	Message ID to map from, i.e. the ID of the message the bot received
	 * @param {String} toId	Message ID to map to, i.e. the ID of the message the bot sent
	 */
	insert(direction, bridge, fromId, toId) {
		// Get/create the entry for the bridge
		let keyToIdsMap = this._map.get(bridge);
		if (keyToIdsMap === undefined) {
			keyToIdsMap = new Map();
			this._map.set(bridge, keyToIdsMap);
		}

		// Generate the key and get the corresponding IDs
		const key = `${direction} ${fromId}`;
		let toIds = keyToIdsMap.get(key);
		if (toIds === undefined) {
			toIds = new Set();
			keyToIdsMap.set(key, toIds);
		}

		// Shove the new ID into it
		toIds.add(toId);

		// Start a timeout removing it again after 24 hours
		setTimeout(() => {
			keyToIdsMap.delete(key);
		}, moment.duration(24, "hours").asMilliseconds());
	}

	/**
	 * Gets the ID of a message the bot sent based on the ID of the message the bot received
	 *
	 * @param {String} direction	One of the two direction constants of this class
	 * @param {Bridge} bridge	The bridge this mapping is for
	 * @param {String} fromId	Message ID to get corresponding ID for, i.e. the ID of the message the bot received the message
	 *
	 * @returns {String[]}	Message IDs of the corresponding message, i.e. the IDs of the messages the bot sent
	 */
	getCorresponding(direction, bridge, fromId) {
		try {
			// Get the key-to-IDs map
			const keyToIdsMap = this._map.get(bridge);

			// Create the key
			const key = `${direction} ${fromId}`;

			// Extract the IDs
			const toIds = keyToIdsMap.get(key);

			// Return the ID
			return [...toIds];
		} catch (err) {
			// Unknown message ID. Don't do anything
			return [];
		}
	}

	getCorrespondingReverse(direction, bridge, toId) {
		// The ID to return
		let fromId = [];

		// Get the mappings for this bridge
		const keyToIdsMap = this._map.get(bridge);
		if (keyToIdsMap !== undefined) {
			// Find the ID
			const [key] = [...keyToIdsMap].find(([, ids]) => ids.has(toId));
			fromId = key.split(" ");
			fromId.shift();
		}

		return fromId;
	}

	/**
	 * Constant indicating direction discord to telegram
	 */
	static get DISCORD_TO_TELEGRAM() {
		return "d2t";
	}

	/**
	 * Constant indicating direction telegram to discord
	 */
	static get TELEGRAM_TO_DISCORD() {
		return "t2d";
	}
}

/********************
 * Export the class *
 ********************/

module.exports = MessageMap;
<|endoftext|>


<commit_before>/*jslint indent:2*/
'use strict';

var i,
  j,
  maxVal,
  orderedList,
  start,
  end,
  time,
  getRandomNumbers = require('./getRandomNumbers');

// push all elements of an array into another
Array.prototype.pushAll = function (valuesArray) {
  for (i = 0; i < valuesArray.length; i = i + 1) {
    this.push(valuesArray[i]);
  }
  return this;
};

function merge(left, right) {
  var mergedList = [],
    // keep a reference to the position of the next
    // element to be compared in each list
    idxLeft = 0,
    idxRight = 0,
    leftValue, // current element in the left list
    rightValue; // current element in the right list

  // while there are more elements in both lists
  while (idxLeft < left.length && idxRight < right.length) {
    // find the smallest element of the two elements
    // at the specified positions of the lists

    // get the next element of each list
    leftValue = left[idxLeft];
    rightValue = right[idxRight];

    if (leftValue < rightValue) { // push the element of the left list
      mergedList.push(leftValue);
      // move to the next element in the left list
      idxLeft = idxLeft + 1;
    } else if (rightValue < leftValue) { // push the element of the right list
      mergedList.push(rightValue);
      // move to the next element in the right list
      idxRight = idxRight + 1;
    } else if (leftValue === rightValue) { // push the current element of both lists
      mergedList.push(leftValue);
      mergedList.push(rightValue);
      // move to the next element in both lists
      idxLeft = idxLeft + 1;
      idxRight = idxRight + 1;
    }
    // continue until you reach the end of both lists
  }

  // there are no more elements in the left list
  if (idxLeft === left.length
      // and there are elements in the right list
      && idxRight < right.length) {
    // push all the remaining elements of the right list
    mergedList.pushAll(right.slice(idxRight));

    // there are no more elements in the right list
  } else if (idxRight === right.length
      // there are more elements in the left list
      && idxLeft < left.length) {
    // push all remaining elemens of the left list
    mergedList.pushAll(left.slice(idxLeft));
  }

  return mergedList;
}

function sort(list) {
  var orderedList,
    middle,
    left,
    right;

  // if the list is of length one
  // it's already ordered
  if (1 === list.length) {
    return list;
  }

  // for lists with more than one element
  // find the middle of the list
  middle = Math.ceil(list.length / 2);

  // recursively sort the left and right parts of the list
  // the left list includes all elements from 0 to middle-1
  left = sort(list.slice(0, middle));
  // the right list includes all elements from middle to list.length
  right = sort(list.slice(middle, list.length));
  // then merge the result
  orderedList = merge(left, right);

  return orderedList;
}

var list = [2, 4, 9, 5, 3, 8, 7, 5];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, 4, 69, 5, 43, 8, 7, 25];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, 4, 69, 5, 43, 8, 7, 25, 0, -12, -45, 5, 7, -2];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, -4, -69, 5, 43];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

// test with random numbers
getRandomNumbers(function (list) {
  start = new Date().getTime();
  orderedList = sort(list);
  end = new Date().getTime();
  time = end - start;

  console.log('Original list:' + list);
  console.log('Sorted list:' + orderedList);
  console.log('Execution time:' + time);
});
<commit_msg>Simplify merge routine by using sentinels. (See Intro to Algs. Thomas Cormen 3rd Ed., Part I Section 2.3, pg. 31)<commit_after>/*jslint indent:2*/
'use strict';

var i,
  j,
  maxVal,
  orderedList,
  start,
  end,
  time,
  getRandomNumbers = require('./getRandomNumbers');

// push all elements of an array into another
Array.prototype.pushAll = function (valuesArray) {
  for (i = 0; i < valuesArray.length; i = i + 1) {
    this.push(valuesArray[i]);
  }
  return this;
};

function merge(left, right) {
  var mergedList = [],
    remaining = left.length + right.length,
    // keep a reference to the position of the next
    // element to be compared in each list
    idxLeft = 0,
    idxRight = 0,
    leftValue, // current element in the left list
    rightValue; // current element in the right list

  // add sentinels to both lists
  left.push(Number.POSITIVE_INFINITY);
  right.push(Number.POSITIVE_INFINITY);

  // while there are more elements in both lists
  for (j = 0; j < remaining; j = j + 1) {
    // find the smallest element of the two elements
    // at the specified positions of the lists

    // get the next element of each list
    leftValue = left[idxLeft];
    rightValue = right[idxRight];

    if (leftValue <= rightValue) {
      mergedList.push(leftValue);
      idxLeft = idxLeft + 1;
    } else {
      mergedList.push(rightValue);
      idxRight = idxRight + 1;
  }
  }

  return mergedList;
}

function sort(list) {
  var orderedList,
    middle,
    left,
    right;

  // if the list is of length one
  // it's already ordered
  if (1 === list.length) {
    return list;
  }

  // for lists with more than one element
  // find the middle of the list
  middle = Math.ceil(list.length / 2);

  // recursively sort the left and right parts of the list
  // the left list includes all elements from 0 to middle-1
  left = sort(list.slice(0, middle));
  // the right list includes all elements from middle to list.length
  right = sort(list.slice(middle, list.length));
  // then merge the result
  orderedList = merge(left, right);

  return orderedList;
}

var list = [2, 4, 9, 5, 3, 8, 7, 5];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, 4, 69, 5, 43, 8, 7, 25];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, 4, 69, 5, 43, 8, 7, 25, 0, -12, -45, 5, 7, -2];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

list = [72, -4, -69, 5, 43];
console.log('Original:' + list);
console.log('Sorted:' + sort(list));

// test with random numbers
getRandomNumbers(function (list) {
  start = new Date().getTime();
  orderedList = sort(list);
  end = new Date().getTime();
  time = end - start;

  console.log('Original list:' + list);
  console.log('Sorted list:' + orderedList);
  console.log('Execution time:' + time);
});
<|endoftext|>


<commit_before>// url: url to JSON in format { tiles : [[1,2,3],[3,2,1]] }
//        where tiles[x][y] = $number, such that there is a valid ".wt-$number" css class
//          [0][0] is lower left
//        all columns tiles[$i] should be equal length
// grid: id of the div to populate with tiles
function webtiles_load($, grid, url) {
  $(document).ready(function(){
      $("#" + grid).addClass("webtiles-grid");
      $.getJSON(url, function(result){
        var tiles = result.tiles;
        // console.log(result);
        for(var y=tiles[0].length - 1; y>= 0; y--){
          var row = $("

");
          row.addClass("webtiles-row");
          for(var x=0; x < tiles.length; x++){
            var tile = $("

");
            tile.addClass("webtile");
            tile.addClass("wt-" + tiles[x][y]);
            row.append(tile);
          }
          $("#grid").append(row);
        }
      });
  });
}

function webtiles_generate_tileset_css($, output_css_pre_id, output_html_pre_id, image_ids, tile_width, tile_height) {
  // window load to wait for images to load
  $(window).on("load", function(){
    var out_css = $("#" + output_css_pre_id);
    var out_html = $("#" + output_html_pre_id);

    // css prelude
    out_css.append(".webtiles-grid div {\n");
    out_css.append(" min-width: " + tile_width + "px;\n");
    out_css.append(" width: " + tile_width + "px;\n");
    out_css.append(" height: " + tile_width + "px;\n");
    out_css.append("}\n");

    out_css.append("\n");

    out_css.append(".webtile {\n");
    out_css.append("  line-height: " + tile_height + "px;\n");
    out_css.append("}\n");

    function output_tile(i,x_offset,y_offset,img_url) {
      //background: url(house.png);
      //background-position: -32px -128px;
      out_css.append(".wt-" + i + " {\n");
      out_css.append("  background: url(" + img_url + ") " + x_offset + "px " + y_offset + "px;\n");
      out_css.append("}\n");

      out_html.append(document.createTextNode("
" + i +"
\n"));
    }

    var tile_id = 0;

    $.each(image_ids, function(i, img_id) {
      var img = document.getElementById(img_id);

      var w = img.width;
      var h = img.height;
      var src = img.src;

      console.log("img id: " + img_id);
      console.log("img url: " + src);
      console.log("img width: " + w);
      console.log("img height: " + h);

      if( w == 0 || h == 0 || (w % tile_width != 0) || (h % tile_height != 0)){
        console.log("Warning: non compatible images for w=" + tile_width + " and h=" + tile_height + ": " + src);
      }

      var num_x = w / tile_width;
      var num_y = h / tile_height;
      out_html.append(document.createTextNode("\n"));
      for(var y=0; y < num_y; y++){
        out_html.append(document.createTextNode("
\n"));
        for(var x=0; x < num_x; x++){
          var ox = -x * tile_width;
          var oy = -y * tile_height;
          out_css.append("/* " + src + " (" + x + ", " + y + ") */\n");
          output_tile(tile_id++,ox,oy,src);
        }

        out_html.append(document.createTextNode("
\n"));
      }
    });
  });
}

<commit_msg>Add debug flag for webtiles_load<commit_after>// url: url to JSON in format { tiles : [[1,2,3],[3,2,1]] }
//        where tiles[x][y] = $number, such that there is a valid ".wt-$number" css class
//          [0][0] is lower left
//        all columns tiles[$i] should be equal length
// grid: id of the div to populate with tiles
function webtiles_load($, grid, url, is_debug) {
  $(document).ready(function(){
      $("#" + grid).addClass("webtiles-grid");
      $.getJSON(url, function(result){
        var tiles = result.tiles;
        // console.log(result);
        for(var y=tiles[0].length - 1; y>= 0; y--){
          var row = $("

");
          row.addClass("webtiles-row");
          for(var x=0; x < tiles.length; x++){
            var debug_str = "";
            if(is_debug){
              debug_str = "" + tiles[x][y];
            }
            var tile = $("
" + debug_str +"
");
            tile.addClass("webtile");
            tile.addClass("wt-" + tiles[x][y]);
            row.append(tile);
          }
          $("#grid").append(row);
        }
      });
  });
}

function webtiles_generate_tileset_css($, output_css_pre_id, output_html_pre_id, image_ids, tile_width, tile_height) {
  // window load to wait for images to load
  $(window).on("load", function(){
    var out_css = $("#" + output_css_pre_id);
    var out_html = $("#" + output_html_pre_id);

    // css prelude
    out_css.append(".webtiles-grid div {\n");
    out_css.append(" min-width: " + tile_width + "px;\n");
    out_css.append(" width: " + tile_width + "px;\n");
    out_css.append(" height: " + tile_width + "px;\n");
    out_css.append("}\n");

    out_css.append("\n");

    out_css.append(".webtile {\n");
    out_css.append("  line-height: " + tile_height + "px;\n");
    out_css.append("}\n");

    function output_tile(i,x_offset,y_offset,img_url) {
      //background: url(house.png);
      //background-position: -32px -128px;
      out_css.append(".wt-" + i + " {\n");
      out_css.append("  background: url(" + img_url + ") " + x_offset + "px " + y_offset + "px;\n");
      out_css.append("}\n");

      out_html.append(document.createTextNode("
" + i +"
\n"));
    }

    var tile_id = 0;

    $.each(image_ids, function(i, img_id) {
      var img = document.getElementById(img_id);

      var w = img.width;
      var h = img.height;
      var src = img.src;

      console.log("img id: " + img_id);
      console.log("img url: " + src);
      console.log("img width: " + w);
      console.log("img height: " + h);

      if( w == 0 || h == 0 || (w % tile_width != 0) || (h % tile_height != 0)){
        console.log("Warning: non compatible images for w=" + tile_width + " and h=" + tile_height + ": " + src);
      }

      var num_x = w / tile_width;
      var num_y = h / tile_height;
      out_html.append(document.createTextNode("\n"));
      for(var y=0; y < num_y; y++){
        out_html.append(document.createTextNode("
\n"));
        for(var x=0; x < num_x; x++){
          var ox = -x * tile_width;
          var oy = -y * tile_height;
          out_css.append("/* " + src + " (" + x + ", " + y + ") */\n");
          output_tile(tile_id++,ox,oy,src);
        }

        out_html.append(document.createTextNode("
\n"));
      }
    });
  });
}

<|endoftext|>


<commit_before>import Reflux from 'reflux'
import _ from 'lodash'
import moment from 'moment'
import d3 from 'd3'

import ChartWizardActions from 'actions/ChartWizardActions'
import api from 'data/api'
import processChartData from 'stores/chartBuilder/processChartData'
import builderDefinitions from 'stores/chartBuilder/builderDefinitions'
import treeify from 'data/transform/treeify'
import ancestryString from 'data/transform/ancestryString'

let ChartWizardStore = Reflux.createStore({
  listenables: ChartWizardActions,
  data: {
    indicatorList: [],
    indicatorSelected: [],
    locationList: [],
    locationSelected: null,
    campaignFilteredList: [],
    timeRangeFilteredList: [],
    groupByValue: 0,
    locationLevelValue: 0,
    timeValue: 0,
    yFormatValue: 0,
    xFormatValue: 0,
    canDisplayChart: false,
    isLoading: true,
    chartOptions: {},
    chartData: [],
    chartDef: {}
  },

  filterCampaignByLocation(campaigns, location) {
    return campaigns.filter(campaign => {
      return campaign.office_id == location.office_id
    })
  },

  filterTimeRangeByChartType(timeRanges, chartType) {
    let expectTimes = _.find(builderDefinitions.charts, {name: chartType}).timeRadios
    return timeRanges.filter(time => {
      return _.includes(expectTimes, time.value)
    })
  },

  applyChartDef(chartDef) {
    this.data.locationLevelValue = Math.max(_.findIndex(builderDefinitions.locationLevels, {value: chartDef.locations}), 0)
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.groupByValue = Math.max(_.findIndex(builderDefinitions.groups, {value: chartDef.groupBy}), 0)
    this.data.timeValue = Math.max(_.findIndex(this.data.timeRangeFilteredList, {json: chartDef.timeRange}), 0)
    this.data.yFormatValue = Math.max(_.findIndex(builderDefinitions.formats, {value: chartDef.yFormat}), 0)
    this.data.xFormatValue = Math.max(_.findIndex(builderDefinitions.formats, {value: chartDef.xFormat}), 0)
  },

  integrateChartOption(chartOption) {
    if (!chartOption.yFormat) {
      chartOption.yFormat = d3.format(builderDefinitions.formats[this.data.yFormatValue].value)
    }
    if(!chartOption.xFormat){
      chartOption.xFormat = d3.format(builderDefinitions.formats[this.data.xFormatValue].value)
    }
    return chartOption
  },

  getInitialState() {
    return this.data
  },

  onInitialize(chartDef, location, campaign) {
    this.data.chartDef = _.clone(chartDef)
    this.data.location = location
    this.data.campaign = campaign

    Promise.all([api.indicatorsTree(), api.locations(), api.campaign(), api.office()]).
      then(([indicators, locations, campaigns, offices]) => {
        this.indicatorIndex = _.indexBy(indicators.flat, 'id')
        this.data.indicatorList = _.sortBy(indicators.objects, 'title')
        this.data.indicatorSelected = chartDef.indicators.map(id => {
          return this.indicatorIndex[id]
        })

        this.locationIndex = _.indexBy(locations.objects, 'id')
        this.data.locationList = _(locations.objects)
          .map(location => {
            return {
              'title': location.name,
              'value': location.id,
              'parent': location.parent_location_id
            }
          })
          .sortBy('title')
          .reverse()
          .thru(_.curryRight(treeify)('value'))
          .map(ancestryString)
          .value()

        let officeIndex = _.indexBy(offices.objects, 'id')
        this.campaignList = _(campaigns.objects)
          .map(campaign => {
            return _.assign({}, campaign, {
              'start_date': moment(campaign.start_date, 'YYYY-MM-DD').toDate(),
              'end_date': moment(campaign.end_date, 'YYYY-MM-DD').toDate(),
              'office': officeIndex[campaign.office_id]
            });
          })
          .sortBy(_.method('start_date.getTime'))
          .reverse()
          .value()

        this.campaignIndex = _.indexBy(this.campaignList, 'id')

        this.data.campaignFilteredList = this.filterCampaignByLocation(this.campaignList, this.data.location)
        this.data.timeRangeFilteredList = this.filterTimeRangeByChartType(builderDefinitions.times, this.data.chartDef.type)
        this.applyChartDef(chartDef)

        this.previewChart()
    })
  },

  onEditTitle(value) {
    this.data.chartDef.title = value
  },

  onAddLocation(index) {
    this.data.location = this.locationIndex[index]
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.campaignFilteredList = this.filterCampaignByLocation(this.campaignList, this.data.location)
    let newCampaign = this.data.campaignFilteredList.filter(campaign => {
      return moment(campaign.start_date).format() == moment(this.data.campaign.start_date).format()
    })
    this.data.campaign = newCampaign.length > 0 ? newCampaign[0] : this.data.campaignFilteredList[0]
    this.previewChart()
  },

  onAddIndicator(index) {
    this.data.indicatorSelected.push(this.indicatorIndex[index])
    this.previewChart()
  },

  onRemoveIndicator(id) {
    _.remove(this.data.indicatorSelected, {id: id})
    this.previewChart()
  },

  onAddCampaign(index) {
    this.data.campaign = this.campaignIndex[index]
    this.previewChart()
  },

  onChangeChart(value) {
    this.data.chartDef.type = value
    this.data.timeRangeFilteredList = this.filterTimeRangeByChartType(builderDefinitions.times, this.data.chartDef.type)
    this.data.locationLevelValue = _.findIndex(builderDefinitions.locationLevels, {value: 'sublocations'})
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.chartData = []
    this.previewChart()
  },

  onChangeGroupRadio(value) {
    this.data.groupByValue = value
    this.previewChart()
  },

  onChangeLocationLevelRadio(value) {
    this.data.locationLevelValue = value
    this.data.locationSelected = builderDefinitions.locationLevels[value].getAggregated(this.data.location, this.locationIndex)
    this.previewChart()
  },

  onChangeTimeRadio(value) {
    this.data.timeValue = value
    this.previewChart()
  },

  onChangeYFormatRadio(value) {
    this.data.yFormatValue = value
    this.previewChart()
  },

  onChangeXFormatRadio(value) {
    this.data.xFormatValue = value
    this.previewChart()
  },

  onChangeXAxis(value){
    this.data.chartDef.x = value
    this.previewChart()
  },

  onChangeYAxis(value){
    this.data.chartDef.y = value
    this.previewChart()
  },

  onSaveChart(callback) {
    callback(_.merge(this.data.chartDef, {
      indicators: this.data.indicatorSelected.map(item => {
        return item.id
      }),
      groupBy: builderDefinitions.groups[this.data.groupByValue].value,
      locations: builderDefinitions.locationLevels[this.data.locationLevelValue].value,
      timeRange: this.data.timeRangeFilteredList[this.data.timeValue].json,
      yFormat: builderDefinitions.formats[this.data.yFormatValue].value,
      xFormat: builderDefinitions.formats[this.data.xFormatValue].value
    }, (a, b) => {
      return b
    }))
  },

  previewChart() {
    if (!this.data.indicatorSelected.length) {
      this.data.canDisplayChart = false
      this.trigger(this.data)
      return
    }

    this.data.isLoading = true
    this.trigger(this.data)

    let chartType = this.data.chartDef.type
    let groupBy = builderDefinitions.groups[this.data.groupByValue].value
    let indicatorIndex = _.indexBy(this.data.indicatorSelected, 'id')
    let locationIndex = _.indexBy(this.data.locationSelected, 'id')
    let groups = this.data.groupByValue == 0 ? indicatorIndex : locationIndex
    let start = moment(this.data.campaign.start_date)
    let lower = this.data.timeRangeFilteredList[this.data.timeValue].getLower(start)
    let upper = start.clone().startOf('month')
    let indicatorArray = _.map(this.data.indicatorSelected, _.property('id'))
    let query = {
      indicator__in: indicatorArray,
      location__in: _.map(this.data.locationSelected, _.property('id')),
      campaign_start: (lower ? lower.format('YYYY-MM-DD') : null),
      campaign_end: upper.format('YYYY-MM-DD')
    }

    processChartData.init(api.datapoints(query),
      chartType,
      this.data.indicatorSelected,
      this.data.locationSelected,
      lower,
      upper,
      groups,
      groupBy,
      this.data.chartDef.x,
      this.data.chartDef.y)
    .then(chart => {
      if (!chart.options || !chart.data) {
        this.data.canDisplayChart = false
      } else {
        this.data.canDisplayChart = true
        this.data.chartOptions = this.integrateChartOption(chart.options)
        this.data.chartData = chart.data
      }
      this.data.isLoading = false
      this.trigger(this.data)
    });
  }
})

export default ChartWizardStore
<commit_msg>show no data instead of loading if no indicator selected<commit_after>import Reflux from 'reflux'
import _ from 'lodash'
import moment from 'moment'
import d3 from 'd3'

import ChartWizardActions from 'actions/ChartWizardActions'
import api from 'data/api'
import processChartData from 'stores/chartBuilder/processChartData'
import builderDefinitions from 'stores/chartBuilder/builderDefinitions'
import treeify from 'data/transform/treeify'
import ancestryString from 'data/transform/ancestryString'

let ChartWizardStore = Reflux.createStore({
  listenables: ChartWizardActions,
  data: {
    indicatorList: [],
    indicatorSelected: [],
    locationList: [],
    locationSelected: null,
    campaignFilteredList: [],
    timeRangeFilteredList: [],
    groupByValue: 0,
    locationLevelValue: 0,
    timeValue: 0,
    yFormatValue: 0,
    xFormatValue: 0,
    canDisplayChart: false,
    isLoading: true,
    chartOptions: {},
    chartData: [],
    chartDef: {}
  },

  filterCampaignByLocation(campaigns, location) {
    return campaigns.filter(campaign => {
      return campaign.office_id == location.office_id
    })
  },

  filterTimeRangeByChartType(timeRanges, chartType) {
    let expectTimes = _.find(builderDefinitions.charts, {name: chartType}).timeRadios
    return timeRanges.filter(time => {
      return _.includes(expectTimes, time.value)
    })
  },

  applyChartDef(chartDef) {
    this.data.locationLevelValue = Math.max(_.findIndex(builderDefinitions.locationLevels, {value: chartDef.locations}), 0)
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.groupByValue = Math.max(_.findIndex(builderDefinitions.groups, {value: chartDef.groupBy}), 0)
    this.data.timeValue = Math.max(_.findIndex(this.data.timeRangeFilteredList, {json: chartDef.timeRange}), 0)
    this.data.yFormatValue = Math.max(_.findIndex(builderDefinitions.formats, {value: chartDef.yFormat}), 0)
    this.data.xFormatValue = Math.max(_.findIndex(builderDefinitions.formats, {value: chartDef.xFormat}), 0)
  },

  integrateChartOption(chartOption) {
    if (!chartOption.yFormat) {
      chartOption.yFormat = d3.format(builderDefinitions.formats[this.data.yFormatValue].value)
    }
    if(!chartOption.xFormat){
      chartOption.xFormat = d3.format(builderDefinitions.formats[this.data.xFormatValue].value)
    }
    return chartOption
  },

  getInitialState() {
    return this.data
  },

  onInitialize(chartDef, location, campaign) {
    this.data.chartDef = _.clone(chartDef)
    this.data.location = location
    this.data.campaign = campaign

    Promise.all([api.indicatorsTree(), api.locations(), api.campaign(), api.office()]).
      then(([indicators, locations, campaigns, offices]) => {
        this.indicatorIndex = _.indexBy(indicators.flat, 'id')
        this.data.indicatorList = _.sortBy(indicators.objects, 'title')
        this.data.indicatorSelected = chartDef.indicators.map(id => {
          return this.indicatorIndex[id]
        })

        this.locationIndex = _.indexBy(locations.objects, 'id')
        this.data.locationList = _(locations.objects)
          .map(location => {
            return {
              'title': location.name,
              'value': location.id,
              'parent': location.parent_location_id
            }
          })
          .sortBy('title')
          .reverse()
          .thru(_.curryRight(treeify)('value'))
          .map(ancestryString)
          .value()

        let officeIndex = _.indexBy(offices.objects, 'id')
        this.campaignList = _(campaigns.objects)
          .map(campaign => {
            return _.assign({}, campaign, {
              'start_date': moment(campaign.start_date, 'YYYY-MM-DD').toDate(),
              'end_date': moment(campaign.end_date, 'YYYY-MM-DD').toDate(),
              'office': officeIndex[campaign.office_id]
            });
          })
          .sortBy(_.method('start_date.getTime'))
          .reverse()
          .value()

        this.campaignIndex = _.indexBy(this.campaignList, 'id')

        this.data.campaignFilteredList = this.filterCampaignByLocation(this.campaignList, this.data.location)
        this.data.timeRangeFilteredList = this.filterTimeRangeByChartType(builderDefinitions.times, this.data.chartDef.type)
        this.applyChartDef(chartDef)

        this.previewChart()
    })
  },

  onEditTitle(value) {
    this.data.chartDef.title = value
  },

  onAddLocation(index) {
    this.data.location = this.locationIndex[index]
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.campaignFilteredList = this.filterCampaignByLocation(this.campaignList, this.data.location)
    let newCampaign = this.data.campaignFilteredList.filter(campaign => {
      return moment(campaign.start_date).format() == moment(this.data.campaign.start_date).format()
    })
    this.data.campaign = newCampaign.length > 0 ? newCampaign[0] : this.data.campaignFilteredList[0]
    this.previewChart()
  },

  onAddIndicator(index) {
    this.data.indicatorSelected.push(this.indicatorIndex[index])
    this.previewChart()
  },

  onRemoveIndicator(id) {
    _.remove(this.data.indicatorSelected, {id: id})
    this.previewChart()
  },

  onAddCampaign(index) {
    this.data.campaign = this.campaignIndex[index]
    this.previewChart()
  },

  onChangeChart(value) {
    this.data.chartDef.type = value
    this.data.timeRangeFilteredList = this.filterTimeRangeByChartType(builderDefinitions.times, this.data.chartDef.type)
    this.data.locationLevelValue = _.findIndex(builderDefinitions.locationLevels, {value: 'sublocations'})
    this.data.locationSelected = builderDefinitions.locationLevels[this.data.locationLevelValue].getAggregated(this.data.location, this.locationIndex)
    this.data.chartData = []
    this.previewChart()
  },

  onChangeGroupRadio(value) {
    this.data.groupByValue = value
    this.previewChart()
  },

  onChangeLocationLevelRadio(value) {
    this.data.locationLevelValue = value
    this.data.locationSelected = builderDefinitions.locationLevels[value].getAggregated(this.data.location, this.locationIndex)
    this.previewChart()
  },

  onChangeTimeRadio(value) {
    this.data.timeValue = value
    this.previewChart()
  },

  onChangeYFormatRadio(value) {
    this.data.yFormatValue = value
    this.previewChart()
  },

  onChangeXFormatRadio(value) {
    this.data.xFormatValue = value
    this.previewChart()
  },

  onChangeXAxis(value){
    this.data.chartDef.x = value
    this.previewChart()
  },

  onChangeYAxis(value){
    this.data.chartDef.y = value
    this.previewChart()
  },

  onSaveChart(callback) {
    callback(_.merge(this.data.chartDef, {
      indicators: this.data.indicatorSelected.map(item => {
        return item.id
      }),
      groupBy: builderDefinitions.groups[this.data.groupByValue].value,
      locations: builderDefinitions.locationLevels[this.data.locationLevelValue].value,
      timeRange: this.data.timeRangeFilteredList[this.data.timeValue].json,
      yFormat: builderDefinitions.formats[this.data.yFormatValue].value,
      xFormat: builderDefinitions.formats[this.data.xFormatValue].value
    }, (a, b) => {
      return b
    }))
  },

  previewChart() {
    if (!this.data.indicatorSelected.length) {
      this.data.canDisplayChart = false
      this.data.isLoading = false
      this.trigger(this.data)
      return
    }

    this.data.isLoading = true
    this.trigger(this.data)

    let chartType = this.data.chartDef.type
    let groupBy = builderDefinitions.groups[this.data.groupByValue].value
    let indicatorIndex = _.indexBy(this.data.indicatorSelected, 'id')
    let locationIndex = _.indexBy(this.data.locationSelected, 'id')
    let groups = this.data.groupByValue == 0 ? indicatorIndex : locationIndex
    let start = moment(this.data.campaign.start_date)
    let lower = this.data.timeRangeFilteredList[this.data.timeValue].getLower(start)
    let upper = start.clone().startOf('month')
    let indicatorArray = _.map(this.data.indicatorSelected, _.property('id'))
    let query = {
      indicator__in: indicatorArray,
      location__in: _.map(this.data.locationSelected, _.property('id')),
      campaign_start: (lower ? lower.format('YYYY-MM-DD') : null),
      campaign_end: upper.format('YYYY-MM-DD')
    }

    processChartData.init(api.datapoints(query),
      chartType,
      this.data.indicatorSelected,
      this.data.locationSelected,
      lower,
      upper,
      groups,
      groupBy,
      this.data.chartDef.x,
      this.data.chartDef.y)
    .then(chart => {
      if (!chart.options || !chart.data) {
        this.data.canDisplayChart = false
      } else {
        this.data.canDisplayChart = true
        this.data.chartOptions = this.integrateChartOption(chart.options)
        this.data.chartData = chart.data
      }
      this.data.isLoading = false
      this.trigger(this.data)
    });
  }
})

export default ChartWizardStore
<|endoftext|>


<commit_before>import clone from 'clone';
import equal from 'deep-equal';
import extend from 'extend';
import DeltaOp from 'quill-delta/lib/op';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';

let debug = logger('quill:keyboard');

const SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';

class Keyboard extends Module {
  static match(evt, binding) {
    binding = normalize(binding);
    if (!!binding.shortKey !== evt[SHORTKEY] && binding.shortKey !== null) return false;
    if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function(key) {
      return (key != SHORTKEY && !!binding[key] !== evt[key] && binding[key] !== null);
    })) {
      return false;
    }
    return binding.key === (evt.which || evt.keyCode);
  }

  constructor(quill, options) {
    super(quill, options);
    this.bindings = {};
    Object.keys(this.options.bindings).forEach((name) => {
      if (this.options.bindings[name]) {
        this.addBinding(this.options.bindings[name]);
      }
    });
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);
    this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function() {});
    if (/Firefox/i.test(navigator.userAgent)) {
      // Need to handle delete and backspace for Firefox in the general case #1171
      this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);
      this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);
    } else {
      this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);
      this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);
    }
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);
    this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);
    this.listen();
  }

  addBinding(key, context = {}, handler = {}) {
    let binding = normalize(key);
    if (binding == null || binding.key == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    if (typeof context === 'function') {
      context = { handler: context };
    }
    if (typeof handler === 'function') {
      handler = { handler: handler };
    }
    binding = extend(binding, context, handler);
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push(binding);
  }

  listen() {
    this.quill.root.addEventListener('keydown', (evt) => {
      if (evt.defaultPrevented) return;
      let which = evt.which || evt.keyCode;
      let bindings = (this.bindings[which] || []).filter(function(binding) {
        return Keyboard.match(evt, binding);
      });
      if (bindings.length === 0) return;
      let range = this.quill.getSelection();
      if (range == null || !this.quill.hasFocus()) return;
      let [line, offset] = this.quill.getLine(range.index);
      let [leafStart, offsetStart] = this.quill.getLeaf(range.index);
      let [leafEnd, offsetEnd] = range.length === 0 ? [leafStart, offsetStart] : this.quill.getLeaf(range.index + range.length);
      let prefixText = leafStart instanceof Parchment.Text ? leafStart.value().slice(0, offsetStart) : '';
      let suffixText = leafEnd instanceof Parchment.Text ? leafEnd.value().slice(offsetEnd) : '';
      let curContext = {
        collapsed: range.length === 0,
        empty: range.length === 0 && line.length() <= 1,
        format: this.quill.getFormat(range),
        offset: offset,
        prefix: prefixText,
        suffix: suffixText
      };
      let prevented = bindings.some((binding) => {
        if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;
        if (binding.empty != null && binding.empty !== curContext.empty) return false;
        if (binding.offset != null && binding.offset !== curContext.offset) return false;
        if (Array.isArray(binding.format)) {
          // any format is present
          if (binding.format.every(function(name) {
            return curContext.format[name] == null;
          })) {
            return false;
          }
        } else if (typeof binding.format === 'object') {
          // all formats must match
          if (!Object.keys(binding.format).every(function(name) {
            if (binding.format[name] === true) return curContext.format[name] != null;
            if (binding.format[name] === false) return curContext.format[name] == null;
            return equal(binding.format[name], curContext.format[name]);
          })) {
            return false;
          }
        }
        if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;
        if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;
        return binding.handler.call(this, range, curContext) !== true;
      });
      if (prevented) {
        evt.preventDefault();
      }
    });
  }
}

Keyboard.keys = {
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  DELETE: 46
};

Keyboard.DEFAULTS = {
  bindings: {
    'bold'      : makeFormatHandler('bold'),
    'italic'    : makeFormatHandler('italic'),
    'underline' : makeFormatHandler('underline'),
    'indent': {
      // highlight tab or tab at beginning of list, indent or blockquote
      key: Keyboard.keys.TAB,
      format: ['blockquote', 'indent', 'list'],
      handler: function(range, context) {
        if (context.collapsed && context.offset !== 0) return true;
        this.quill.format('indent', '+1', Quill.sources.USER);
      }
    },
    'outdent': {
      key: Keyboard.keys.TAB,
      shiftKey: true,
      format: ['blockquote', 'indent', 'list'],
      // highlight tab or tab at beginning of list, indent or blockquote
      handler: function(range, context) {
        if (context.collapsed && context.offset !== 0) return true;
        this.quill.format('indent', '-1', Quill.sources.USER);
      }
    },
    'outdent backspace': {
      key: Keyboard.keys.BACKSPACE,
      collapsed: true,
      format: ['blockquote', 'indent', 'list'],
      offset: 0,
      handler: function(range, context) {
        if (context.format.indent != null) {
          this.quill.format('indent', '-1', Quill.sources.USER);
        } else if (context.format.blockquote != null) {
          this.quill.format('blockquote', false, Quill.sources.USER);
        } else if (context.format.list != null) {
          this.quill.format('list', false, Quill.sources.USER);
        }
      }
    },
    'indent code-block': makeCodeBlockHandler(true),
    'outdent code-block': makeCodeBlockHandler(false),
    'remove tab': {
      key: Keyboard.keys.TAB,
      shiftKey: true,
      collapsed: true,
      prefix: /\t$/,
      handler: function(range) {
        this.quill.deleteText(range.index - 1, 1, Quill.sources.USER);
      }
    },
    'tab': {
      key: Keyboard.keys.TAB,
      handler: function(range, context) {
        if (!context.collapsed) {
          this.quill.scroll.deleteAt(range.index, range.length);
        }
        this.quill.insertText(range.index, '\t', Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
      }
    },
    'list empty enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['list'],
      empty: true,
      handler: function(range, context) {
        this.quill.format('list', false, Quill.sources.USER);
        if (context.format.indent) {
          this.quill.format('indent', false, Quill.sources.USER);
        }
      }
    },
    'checklist enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: { list: 'checked' },
      handler: function(range) {
        this.quill.scroll.insertAt(range.index, '\n');
        let [line, ] = this.quill.getLine(range.index + 1);
        line.format('list', 'unchecked');
        this.quill.update(Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
        this.quill.selection.scrollIntoView();
      }
    },
    'header enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['header'],
      suffix: /^$/,
      handler: function(range) {
        this.quill.scroll.insertAt(range.index, '\n');
        this.quill.formatText(range.index + 1, 1, 'header', false, Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
        this.quill.selection.scrollIntoView();
      }
    },
    'list autofill': {
      key: ' ',
      collapsed: true,
      format: { list: false },
      prefix: /^(1\.|-)$/,
      handler: function(range, context) {
        if (this.quill.scroll.whitelist != null && !this.quill.scroll.whitelist['list']) return true;
        let length = context.prefix.length;
        this.quill.scroll.deleteAt(range.index - length, length);
        this.quill.formatLine(range.index - length, 1, 'list', length === 1 ? 'bullet' : 'ordered', Quill.sources.USER);
        this.quill.setSelection(range.index - length, Quill.sources.SILENT);
      }
    },
    'code exit': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['code-block'],
      prefix: /\n\n$/,
      suffix: /^\s+$/,
      handler: function(range) {
        this.quill.format('code-block', false, Quill.sources.USER);
        this.quill.deleteText(range.index - 2, 1, Quill.sources.USER);
      }
    }
  }
};

function handleBackspace(range, context) {
  if (range.index === 0) return;
  let [line, ] = this.quill.getLine(range.index);
  let formats = {};
  if (context.offset === 0) {
    let curFormats = line.formats();
    let prevFormats = this.quill.getFormat(range.index-1, 1);
    formats = DeltaOp.attributes.diff(curFormats, prevFormats) || {};
  }
  // Check for astral symbols
  let length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1;
  this.quill.deleteText(range.index-length, length, Quill.sources.USER);
  if (Object.keys(formats).length > 0) {
    this.quill.formatLine(range.index-length, length, formats, Quill.sources.USER);
  }
  this.quill.selection.scrollIntoView();
}

function handleDelete(range, context) {
  // Check for astral symbols
  let length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1;
  if (range.index >= this.quill.getLength() - length) return;
  this.quill.deleteText(range.index, length, Quill.sources.USER);
}

function handleDeleteRange(range) {
  this.quill.deleteText(range, Quill.sources.USER);
  this.quill.setSelection(range.index, Quill.sources.SILENT);
  this.quill.selection.scrollIntoView();
}

function handleEnter(range, context) {
  if (range.length > 0) {
    this.quill.scroll.deleteAt(range.index, range.length);  // So we do not trigger text-change
  }
  let lineFormats = Object.keys(context.format).reduce(function(lineFormats, format) {
    if (Parchment.query(format, Parchment.Scope.BLOCK) && !Array.isArray(context.format[format])) {
      lineFormats[format] = context.format[format];
    }
    return lineFormats;
  }, {});
  this.quill.insertText(range.index, '\n', lineFormats, Quill.sources.USER);
  // Earlier scroll.deleteAt might have messed up our selection,
  // so insertText's built in selection preservation is not reliable
  this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
  this.quill.selection.scrollIntoView();
  Object.keys(context.format).forEach((name) => {
    if (lineFormats[name] != null) return;
    if (Array.isArray(context.format[name])) return;
    if (name === 'link') return;
    this.quill.format(name, context.format[name], Quill.sources.USER);
  });
}

function makeCodeBlockHandler(indent) {
  return {
    key: Keyboard.keys.TAB,
    shiftKey: !indent,
    format: {'code-block': true },
    handler: function(range) {
      let CodeBlock = Parchment.query('code-block');
      let index = range.index, length = range.length;
      let [block, offset] = this.quill.scroll.descendant(CodeBlock, index);
      if (block == null) return;
      let scrollIndex = this.quill.getIndex(block);
      let start = block.newlineIndex(offset, true) + 1;
      let end = block.newlineIndex(scrollIndex + offset + length);
      let lines = block.domNode.textContent.slice(start, end).split('\n');
      offset = 0;
      lines.forEach((line, i) => {
        if (indent) {
          block.insertAt(start + offset, CodeBlock.TAB);
          offset += CodeBlock.TAB.length;
          if (i === 0) {
            index += CodeBlock.TAB.length;
          } else {
            length += CodeBlock.TAB.length;
          }
        } else if (line.startsWith(CodeBlock.TAB)) {
          block.deleteAt(start + offset, CodeBlock.TAB.length);
          offset -= CodeBlock.TAB.length;
          if (i === 0) {
            index -= CodeBlock.TAB.length;
          } else {
            length -= CodeBlock.TAB.length;
          }
        }
        offset += line.length + 1;
      });
      this.quill.update(Quill.sources.USER);
      this.quill.setSelection(index, length, Quill.sources.SILENT);
    }
  };
}

function makeFormatHandler(format) {
  return {
    key: format[0].toUpperCase(),
    shortKey: true,
    handler: function(range, context) {
      this.quill.format(format, !context.format[format], Quill.sources.USER);
    }
  };
}

function normalize(binding) {
  if (typeof binding === 'string' || typeof binding === 'number') {
    return normalize({ key: binding });
  }
  if (typeof binding === 'object') {
    binding = clone(binding, false);
  }
  if (typeof binding.key === 'string') {
    if (Keyboard.keys[binding.key.toUpperCase()] != null) {
      binding.key = Keyboard.keys[binding.key.toUpperCase()];
    } else if (binding.key.length === 1) {
      binding.key = binding.key.toUpperCase().charCodeAt(0);
    } else {
      return null;
    }
  }
  return binding;
}

export default Keyboard;
<commit_msg>allow whitespace to precede list autofill<commit_after>import clone from 'clone';
import equal from 'deep-equal';
import extend from 'extend';
import DeltaOp from 'quill-delta/lib/op';
import Parchment from 'parchment';
import Quill from '../core/quill';
import logger from '../core/logger';
import Module from '../core/module';

let debug = logger('quill:keyboard');

const SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey';

class Keyboard extends Module {
  static match(evt, binding) {
    binding = normalize(binding);
    if (!!binding.shortKey !== evt[SHORTKEY] && binding.shortKey !== null) return false;
    if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function(key) {
      return (key != SHORTKEY && !!binding[key] !== evt[key] && binding[key] !== null);
    })) {
      return false;
    }
    return binding.key === (evt.which || evt.keyCode);
  }

  constructor(quill, options) {
    super(quill, options);
    this.bindings = {};
    Object.keys(this.options.bindings).forEach((name) => {
      if (this.options.bindings[name]) {
        this.addBinding(this.options.bindings[name]);
      }
    });
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, handleEnter);
    this.addBinding({ key: Keyboard.keys.ENTER, metaKey: null, ctrlKey: null, altKey: null }, function() {});
    if (/Firefox/i.test(navigator.userAgent)) {
      // Need to handle delete and backspace for Firefox in the general case #1171
      this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true }, handleBackspace);
      this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true }, handleDelete);
    } else {
      this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: true, prefix: /^.?$/ }, handleBackspace);
      this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: true, suffix: /^.?$/ }, handleDelete);
    }
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, { collapsed: false }, handleDeleteRange);
    this.addBinding({ key: Keyboard.keys.DELETE }, { collapsed: false }, handleDeleteRange);
    this.listen();
  }

  addBinding(key, context = {}, handler = {}) {
    let binding = normalize(key);
    if (binding == null || binding.key == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    if (typeof context === 'function') {
      context = { handler: context };
    }
    if (typeof handler === 'function') {
      handler = { handler: handler };
    }
    binding = extend(binding, context, handler);
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push(binding);
  }

  listen() {
    this.quill.root.addEventListener('keydown', (evt) => {
      if (evt.defaultPrevented) return;
      let which = evt.which || evt.keyCode;
      let bindings = (this.bindings[which] || []).filter(function(binding) {
        return Keyboard.match(evt, binding);
      });
      if (bindings.length === 0) return;
      let range = this.quill.getSelection();
      if (range == null || !this.quill.hasFocus()) return;
      let [line, offset] = this.quill.getLine(range.index);
      let [leafStart, offsetStart] = this.quill.getLeaf(range.index);
      let [leafEnd, offsetEnd] = range.length === 0 ? [leafStart, offsetStart] : this.quill.getLeaf(range.index + range.length);
      let prefixText = leafStart instanceof Parchment.Text ? leafStart.value().slice(0, offsetStart) : '';
      let suffixText = leafEnd instanceof Parchment.Text ? leafEnd.value().slice(offsetEnd) : '';
      let curContext = {
        collapsed: range.length === 0,
        empty: range.length === 0 && line.length() <= 1,
        format: this.quill.getFormat(range),
        offset: offset,
        prefix: prefixText,
        suffix: suffixText
      };
      let prevented = bindings.some((binding) => {
        if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false;
        if (binding.empty != null && binding.empty !== curContext.empty) return false;
        if (binding.offset != null && binding.offset !== curContext.offset) return false;
        if (Array.isArray(binding.format)) {
          // any format is present
          if (binding.format.every(function(name) {
            return curContext.format[name] == null;
          })) {
            return false;
          }
        } else if (typeof binding.format === 'object') {
          // all formats must match
          if (!Object.keys(binding.format).every(function(name) {
            if (binding.format[name] === true) return curContext.format[name] != null;
            if (binding.format[name] === false) return curContext.format[name] == null;
            return equal(binding.format[name], curContext.format[name]);
          })) {
            return false;
          }
        }
        if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false;
        if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false;
        return binding.handler.call(this, range, curContext) !== true;
      });
      if (prevented) {
        evt.preventDefault();
      }
    });
  }
}

Keyboard.keys = {
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  DELETE: 46
};

Keyboard.DEFAULTS = {
  bindings: {
    'bold'      : makeFormatHandler('bold'),
    'italic'    : makeFormatHandler('italic'),
    'underline' : makeFormatHandler('underline'),
    'indent': {
      // highlight tab or tab at beginning of list, indent or blockquote
      key: Keyboard.keys.TAB,
      format: ['blockquote', 'indent', 'list'],
      handler: function(range, context) {
        if (context.collapsed && context.offset !== 0) return true;
        this.quill.format('indent', '+1', Quill.sources.USER);
      }
    },
    'outdent': {
      key: Keyboard.keys.TAB,
      shiftKey: true,
      format: ['blockquote', 'indent', 'list'],
      // highlight tab or tab at beginning of list, indent or blockquote
      handler: function(range, context) {
        if (context.collapsed && context.offset !== 0) return true;
        this.quill.format('indent', '-1', Quill.sources.USER);
      }
    },
    'outdent backspace': {
      key: Keyboard.keys.BACKSPACE,
      collapsed: true,
      format: ['blockquote', 'indent', 'list'],
      offset: 0,
      handler: function(range, context) {
        if (context.format.indent != null) {
          this.quill.format('indent', '-1', Quill.sources.USER);
        } else if (context.format.blockquote != null) {
          this.quill.format('blockquote', false, Quill.sources.USER);
        } else if (context.format.list != null) {
          this.quill.format('list', false, Quill.sources.USER);
        }
      }
    },
    'indent code-block': makeCodeBlockHandler(true),
    'outdent code-block': makeCodeBlockHandler(false),
    'remove tab': {
      key: Keyboard.keys.TAB,
      shiftKey: true,
      collapsed: true,
      prefix: /\t$/,
      handler: function(range) {
        this.quill.deleteText(range.index - 1, 1, Quill.sources.USER);
      }
    },
    'tab': {
      key: Keyboard.keys.TAB,
      handler: function(range, context) {
        if (!context.collapsed) {
          this.quill.scroll.deleteAt(range.index, range.length);
        }
        this.quill.insertText(range.index, '\t', Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
      }
    },
    'list empty enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['list'],
      empty: true,
      handler: function(range, context) {
        this.quill.format('list', false, Quill.sources.USER);
        if (context.format.indent) {
          this.quill.format('indent', false, Quill.sources.USER);
        }
      }
    },
    'checklist enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: { list: 'checked' },
      handler: function(range) {
        this.quill.scroll.insertAt(range.index, '\n');
        let [line, ] = this.quill.getLine(range.index + 1);
        line.format('list', 'unchecked');
        this.quill.update(Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
        this.quill.selection.scrollIntoView();
      }
    },
    'header enter': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['header'],
      suffix: /^$/,
      handler: function(range) {
        this.quill.scroll.insertAt(range.index, '\n');
        this.quill.formatText(range.index + 1, 1, 'header', false, Quill.sources.USER);
        this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
        this.quill.selection.scrollIntoView();
      }
    },
    'list autofill': {
      key: ' ',
      collapsed: true,
      format: { list: false },
      prefix: /^\s*?(1\.|-)$/,
      handler: function(range, context) {
        if (this.quill.scroll.whitelist != null && !this.quill.scroll.whitelist['list']) return true;
        let length = context.prefix.length;
        let value = context.prefix.trim().length === 1 ? 'bullet' : 'ordered'
        this.quill.scroll.deleteAt(range.index - length, length);
        this.quill.formatLine(range.index - length, 1, 'list', value, Quill.sources.USER);
        this.quill.setSelection(range.index - length, Quill.sources.SILENT);
      }
    },
    'code exit': {
      key: Keyboard.keys.ENTER,
      collapsed: true,
      format: ['code-block'],
      prefix: /\n\n$/,
      suffix: /^\s+$/,
      handler: function(range) {
        this.quill.format('code-block', false, Quill.sources.USER);
        this.quill.deleteText(range.index - 2, 1, Quill.sources.USER);
      }
    }
  }
};

function handleBackspace(range, context) {
  if (range.index === 0) return;
  let [line, ] = this.quill.getLine(range.index);
  let formats = {};
  if (context.offset === 0) {
    let curFormats = line.formats();
    let prevFormats = this.quill.getFormat(range.index-1, 1);
    formats = DeltaOp.attributes.diff(curFormats, prevFormats) || {};
  }
  // Check for astral symbols
  let length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1;
  this.quill.deleteText(range.index-length, length, Quill.sources.USER);
  if (Object.keys(formats).length > 0) {
    this.quill.formatLine(range.index-length, length, formats, Quill.sources.USER);
  }
  this.quill.selection.scrollIntoView();
}

function handleDelete(range, context) {
  // Check for astral symbols
  let length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1;
  if (range.index >= this.quill.getLength() - length) return;
  this.quill.deleteText(range.index, length, Quill.sources.USER);
}

function handleDeleteRange(range) {
  this.quill.deleteText(range, Quill.sources.USER);
  this.quill.setSelection(range.index, Quill.sources.SILENT);
  this.quill.selection.scrollIntoView();
}

function handleEnter(range, context) {
  if (range.length > 0) {
    this.quill.scroll.deleteAt(range.index, range.length);  // So we do not trigger text-change
  }
  let lineFormats = Object.keys(context.format).reduce(function(lineFormats, format) {
    if (Parchment.query(format, Parchment.Scope.BLOCK) && !Array.isArray(context.format[format])) {
      lineFormats[format] = context.format[format];
    }
    return lineFormats;
  }, {});
  this.quill.insertText(range.index, '\n', lineFormats, Quill.sources.USER);
  // Earlier scroll.deleteAt might have messed up our selection,
  // so insertText's built in selection preservation is not reliable
  this.quill.setSelection(range.index + 1, Quill.sources.SILENT);
  this.quill.selection.scrollIntoView();
  Object.keys(context.format).forEach((name) => {
    if (lineFormats[name] != null) return;
    if (Array.isArray(context.format[name])) return;
    if (name === 'link') return;
    this.quill.format(name, context.format[name], Quill.sources.USER);
  });
}

function makeCodeBlockHandler(indent) {
  return {
    key: Keyboard.keys.TAB,
    shiftKey: !indent,
    format: {'code-block': true },
    handler: function(range) {
      let CodeBlock = Parchment.query('code-block');
      let index = range.index, length = range.length;
      let [block, offset] = this.quill.scroll.descendant(CodeBlock, index);
      if (block == null) return;
      let scrollIndex = this.quill.getIndex(block);
      let start = block.newlineIndex(offset, true) + 1;
      let end = block.newlineIndex(scrollIndex + offset + length);
      let lines = block.domNode.textContent.slice(start, end).split('\n');
      offset = 0;
      lines.forEach((line, i) => {
        if (indent) {
          block.insertAt(start + offset, CodeBlock.TAB);
          offset += CodeBlock.TAB.length;
          if (i === 0) {
            index += CodeBlock.TAB.length;
          } else {
            length += CodeBlock.TAB.length;
          }
        } else if (line.startsWith(CodeBlock.TAB)) {
          block.deleteAt(start + offset, CodeBlock.TAB.length);
          offset -= CodeBlock.TAB.length;
          if (i === 0) {
            index -= CodeBlock.TAB.length;
          } else {
            length -= CodeBlock.TAB.length;
          }
        }
        offset += line.length + 1;
      });
      this.quill.update(Quill.sources.USER);
      this.quill.setSelection(index, length, Quill.sources.SILENT);
    }
  };
}

function makeFormatHandler(format) {
  return {
    key: format[0].toUpperCase(),
    shortKey: true,
    handler: function(range, context) {
      this.quill.format(format, !context.format[format], Quill.sources.USER);
    }
  };
}

function normalize(binding) {
  if (typeof binding === 'string' || typeof binding === 'number') {
    return normalize({ key: binding });
  }
  if (typeof binding === 'object') {
    binding = clone(binding, false);
  }
  if (typeof binding.key === 'string') {
    if (Keyboard.keys[binding.key.toUpperCase()] != null) {
      binding.key = Keyboard.keys[binding.key.toUpperCase()];
    } else if (binding.key.length === 1) {
      binding.key = binding.key.toUpperCase().charCodeAt(0);
    } else {
      return null;
    }
  }
  return binding;
}

export default Keyboard;
<|endoftext|>


<commit_before>import clone from 'clone';
import equal from 'deep-equal';
import Delta from 'rich-text/lib/delta';
import Emitter from '../core/emitter';
import logger from '../core/logger';
import Module from '../core/module';
import { Range } from '../core/selection';
import Block from '../blots/block';

let debug = logger('quill:keyboard');

class Keyboard extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.bindings = {};
    this.addBinding({ key: 'B', metaKey: true }, this.onFormat.bind(this, 'bold'));
    this.addBinding({ key: 'I', metaKey: true }, this.onFormat.bind(this, 'italic'));
    this.addBinding({ key: 'U', metaKey: true }, this.onFormat.bind(this, 'underline'));
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, this.onEnter.bind(this));
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDelete.bind(this, true));
    this.addBinding({ key: Keyboard.keys.DELETE }, this.onDelete.bind(this, false));
    // TODO implement
    // this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDeleteWord.bind(this, true));
    // this.addBinding({ key: Keyboard.keys.DELETE }, this.onDeleteWord.bind(this, false));
    this.addBinding({ key: Keyboard.keys.TAB, shiftKey: null }, this.onTab.bind(this));
    this.quill.root.addEventListener('keydown', (evt) => {
      let which = evt.which || evt.keyCode;
      let handlers = (this.bindings[which] || []).reduce(function(handlers, binding) {
        let [key, handler] = binding;
        if (match(evt, key)) handlers.push(handler);
        return handlers;
      }, []);
      if (handlers.length > 0) {
        let range = this.quill.getSelection();
        handlers.forEach((handler) => {
          handler(range, evt);
        });
        evt.preventDefault();
      }
    });
  }

  addBinding(binding, handler) {
    binding = normalize(binding);
    if (binding == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push([binding, handler]);
  }

  onDelete(backspace, range) {
    if (range.length > 0) {
      this.quill.deleteText(range, Emitter.sources.USER);
    } else if (!backspace) {
      this.quill.deleteText(range.index, 1, Emitter.sources.USER);
    } else {
      let [line, offset] = this.quill.scroll.descendant(Block, range.index);
      let formats = this.quill.getFormat(range);
      if (line != null && offset === 0 && (formats['indent'] || formats['list'])) {
        if (formats['indent'] != null) {
          line.format('indent', parseInt(formats['indent']) - 1, Emitter.sources.USER);
        } else {
          line.format('list', false);
        }
      } else {
        this.quill.deleteText(range.index - 1, 1, Emitter.sources.USER);
        range = new Range(Math.max(0, range.index - 1));
      }
    }
    this.quill.setSelection(range.index, Emitter.sources.SILENT);
  }

  onEnter(range) {
    let formats = this.quill.getFormat(range);
    let lineFormats = Object.keys(formats, function(lineFormats, format) {
      if (Parchment.query(format, Parchment.Scope.BLOCK)) {
        lineFormats[name] = formats[name];
      }
      return lineFormats;
    }, {});
    let delta = new Delta()
      .retain(range.index)
      .insert('\n', lineFormats)
      .delete(range.length);
    this.quill.updateContents(delta, Emitter.sources.USER);
    this.quill.setSelection(range.index + 1, Emitter.sources.SILENT);
    Object.keys(formats).forEach((name) => {
      if (lineFormats[name] == null) {
        this.quill.formatCursor(name, formats[name]);
      }
    });
  }

  onFormat(format, range) {
    let formats = this.quill.getFormat(range);
    this.quill.format(format, !formats[format], Emitter.sources.USER);
  }

  onTab(range, evt) {
    if (range.length === 0) {
      let delta = new Delta().retain(range.index).insert('\t').delete(range.length);
      this.quill.updateContents(delta, Emitter.sources.USER);
      this.quill.setSelection(range.index + 1, Emitter.sources.SILENT);
    } else {
      let modifier = evt.shiftKey ? -1 : 1;
      this.quill.scroll.descendants(Block, range.index, range.length).forEach(function(line) {
        let format = line.formats();
        let indent = parseInt(format['indent'] || 0);
        line.format('indent', Math.max(0, indent + modifier));
      });
      this.quill.update(Emitter.sources.USER);
      this.quill.setSelection(range, Emitter.sources.SILENT);
    }
  }

  removeAllBindings(binding, handler = null) {
    binding = normalize(binding);
    if (binding == null || this.bindings[binding.key] == null) return [];
    let removed = [];
    this.bindings[binding.key] = this.bindings[binding.key].filter(function(target) {
      let [key, callback] = target;
      if (equal(key, binding) && (handler == null || callback === handler)) {
        removed.push(handler);
        return false;
      }
      return true;
    });
    return removed;
  }

  removeBinding(binding, handler) {
    this.removeAllBindings(binding, handler);
  }
}

Keyboard.keys = {
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  DELETE: 46
}

function bindKeys(target, keys) {
  target.addEventListener('keydown', function(evt) {
    if (Object.keys(keys).some(function(key) {
      let which = evt.which || evt.keyCode;
      if (which === key || which === Keyboard.keys[key.toUpperCase()]) {
        keys[key]();
        return true;
      }
      return false;
    })) {
      event.preventDefault();
    }
  });
}

function match(evt, binding) {
  let metaKey = /Mac/i.test(navigator.platform) ? evt.metaKey : evt.metaKey || evt.ctrlKey;
  if (!!binding.metaKey !== metaKey && binding.metaKey !== null) return false;
  if (!!binding.shiftKey !== evt.shiftKey && binding.shiftKey !== null) return false;
  if (!!binding.altKey !== evt.altKey && binding.altKey !== null) return false;
  return true;
}

function normalize(binding) {
  switch (typeof binding) {
    case 'string':
      if (Keyboard.keys[binding.toUpperCase()] != null) {
        binding = clone(Keyboard.keys[binding.toUpperCase()], false);
      } else if (binding.length === 1) {
        binding = { key: binding };
      } else {
        return null;
      }
      break;
    case 'number':
      binding = { key: binding };
      break;
    case 'object':
      binding = clone(binding, false);
      break;
    default:
      return null;
  }
  if (typeof binding.key === 'string') {
    binding.key = binding.key.toUpperCase().charCodeAt(0);
  }
  return binding;
}

export { Keyboard as default, bindKeys };
<commit_msg>fix enter preserving formats<commit_after>import clone from 'clone';
import equal from 'deep-equal';
import Delta from 'rich-text/lib/delta';
import Parchment from 'parchment';
import Emitter from '../core/emitter';
import logger from '../core/logger';
import Module from '../core/module';
import { Range } from '../core/selection';
import Block from '../blots/block';

let debug = logger('quill:keyboard');

class Keyboard extends Module {
  constructor(quill, options) {
    super(quill, options);
    this.bindings = {};
    this.addBinding({ key: 'B', metaKey: true }, this.onFormat.bind(this, 'bold'));
    this.addBinding({ key: 'I', metaKey: true }, this.onFormat.bind(this, 'italic'));
    this.addBinding({ key: 'U', metaKey: true }, this.onFormat.bind(this, 'underline'));
    this.addBinding({ key: Keyboard.keys.ENTER, shiftKey: null }, this.onEnter.bind(this));
    this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDelete.bind(this, true));
    this.addBinding({ key: Keyboard.keys.DELETE }, this.onDelete.bind(this, false));
    // TODO implement
    // this.addBinding({ key: Keyboard.keys.BACKSPACE }, this.onDeleteWord.bind(this, true));
    // this.addBinding({ key: Keyboard.keys.DELETE }, this.onDeleteWord.bind(this, false));
    this.addBinding({ key: Keyboard.keys.TAB, shiftKey: null }, this.onTab.bind(this));
    this.quill.root.addEventListener('keydown', (evt) => {
      let which = evt.which || evt.keyCode;
      let handlers = (this.bindings[which] || []).reduce(function(handlers, binding) {
        let [key, handler] = binding;
        if (match(evt, key)) handlers.push(handler);
        return handlers;
      }, []);
      if (handlers.length > 0) {
        let range = this.quill.getSelection();
        handlers.forEach((handler) => {
          handler(range, evt);
        });
        evt.preventDefault();
      }
    });
  }

  addBinding(binding, handler) {
    binding = normalize(binding);
    if (binding == null) {
      return debug.warn('Attempted to add invalid keyboard binding', binding);
    }
    this.bindings[binding.key] = this.bindings[binding.key] || [];
    this.bindings[binding.key].push([binding, handler]);
  }

  onDelete(backspace, range) {
    if (range.length > 0) {
      this.quill.deleteText(range, Emitter.sources.USER);
    } else if (!backspace) {
      this.quill.deleteText(range.index, 1, Emitter.sources.USER);
    } else {
      let [line, offset] = this.quill.scroll.descendant(Block, range.index);
      let formats = this.quill.getFormat(range);
      if (line != null && offset === 0 && (formats['indent'] || formats['list'])) {
        if (formats['indent'] != null) {
          line.format('indent', parseInt(formats['indent']) - 1, Emitter.sources.USER);
        } else {
          line.format('list', false);
        }
      } else {
        this.quill.deleteText(range.index - 1, 1, Emitter.sources.USER);
        range = new Range(Math.max(0, range.index - 1));
      }
    }
    this.quill.setSelection(range.index, Emitter.sources.SILENT);
  }

  onEnter(range) {
    let formats = this.quill.getFormat(range);
    let lineFormats = Object.keys(formats).reduce(function(lineFormats, format) {
      if (Parchment.query(format, Parchment.Scope.BLOCK)) {
        lineFormats[format] = formats[format];
      }
      return lineFormats;
    }, {});
    let delta = new Delta()
      .retain(range.index)
      .insert('\n', lineFormats)
      .delete(range.length);
    this.quill.updateContents(delta, Emitter.sources.USER);
    this.quill.setSelection(range.index + 1, Emitter.sources.SILENT);
    Object.keys(formats).forEach((name) => {
      if (lineFormats[name] == null) {
        this.quill.format(name, formats[name]);
      }
    });
  }

  onFormat(format, range) {
    let formats = this.quill.getFormat(range);
    this.quill.format(format, !formats[format], Emitter.sources.USER);
  }

  onTab(range, evt) {
    if (range.length === 0) {
      let delta = new Delta().retain(range.index).insert('\t').delete(range.length);
      this.quill.updateContents(delta, Emitter.sources.USER);
      this.quill.setSelection(range.index + 1, Emitter.sources.SILENT);
    } else {
      let modifier = evt.shiftKey ? -1 : 1;
      this.quill.scroll.descendants(Block, range.index, range.length).forEach(function(line) {
        let format = line.formats();
        let indent = parseInt(format['indent'] || 0);
        line.format('indent', Math.max(0, indent + modifier));
      });
      this.quill.update(Emitter.sources.USER);
      this.quill.setSelection(range, Emitter.sources.SILENT);
    }
  }

  removeAllBindings(binding, handler = null) {
    binding = normalize(binding);
    if (binding == null || this.bindings[binding.key] == null) return [];
    let removed = [];
    this.bindings[binding.key] = this.bindings[binding.key].filter(function(target) {
      let [key, callback] = target;
      if (equal(key, binding) && (handler == null || callback === handler)) {
        removed.push(handler);
        return false;
      }
      return true;
    });
    return removed;
  }

  removeBinding(binding, handler) {
    this.removeAllBindings(binding, handler);
  }
}

Keyboard.keys = {
  BACKSPACE: 8,
  TAB: 9,
  ENTER: 13,
  ESCAPE: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  DELETE: 46
}

function bindKeys(target, keys) {
  target.addEventListener('keydown', function(evt) {
    if (Object.keys(keys).some(function(key) {
      let which = evt.which || evt.keyCode;
      if (which === key || which === Keyboard.keys[key.toUpperCase()]) {
        keys[key]();
        return true;
      }
      return false;
    })) {
      event.preventDefault();
    }
  });
}

function match(evt, binding) {
  let metaKey = /Mac/i.test(navigator.platform) ? evt.metaKey : evt.metaKey || evt.ctrlKey;
  if (!!binding.metaKey !== metaKey && binding.metaKey !== null) return false;
  if (!!binding.shiftKey !== evt.shiftKey && binding.shiftKey !== null) return false;
  if (!!binding.altKey !== evt.altKey && binding.altKey !== null) return false;
  return true;
}

function normalize(binding) {
  switch (typeof binding) {
    case 'string':
      if (Keyboard.keys[binding.toUpperCase()] != null) {
        binding = clone(Keyboard.keys[binding.toUpperCase()], false);
      } else if (binding.length === 1) {
        binding = { key: binding };
      } else {
        return null;
      }
      break;
    case 'number':
      binding = { key: binding };
      break;
    case 'object':
      binding = clone(binding, false);
      break;
    default:
      return null;
  }
  if (typeof binding.key === 'string') {
    binding.key = binding.key.toUpperCase().charCodeAt(0);
  }
  return binding;
}

export { Keyboard as default, bindKeys };
<|endoftext|>


<commit_before>module.exports = function(priority, func, early) {
        this.modify = func;
        this.priority = priority;
        this.early = early;
}
<commit_msg>Update modifier.js<commit_after>module.exports = function(priority, early,value,isMultiply,resource) {
        this.modifier = value;
        this.multiply = isMultiply?true:false;
        this.resource = resource;
        this.priority = priority;
        this.early = early;
}
<|endoftext|>