<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOControllerConfig',

  documentation: `
    A customizable model to configure any DAOController
  `,

  requires: [
    'foam.comics.SearchMode',
    'foam.comics.v2.CannedQuery',
    'foam.comics.v2.namedViews.NamedViewCollection'
  ],

  properties: [
    {
      class: 'String',
      name: 'daoKey'
    },
    {
      class: 'Class',
      name: 'factory'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.mlang.predicate.Predicate',
      name: 'predicate',
      view: { class: 'foam.u2.view.JSONTextView' }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'dao',
      hidden: true,
      expression: function(daoKey, predicate) {
        var dao = this.__context__[daoKey] || foam.dao.NullDAO.create({of: foam.core.FObject});
        if ( this.hasOwnProperty('of') ) {
          dao = foam.dao.ProxyDAO.create({
            of: this.of,
            delegate: dao
          });
        }
        if ( predicate ) {
          dao = dao.where(predicate);
        }
        return dao;
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'unfilteredDAO',
      hidden: true,
      expression: function(dao) {
        var delegate = dao;
        while ( delegate && foam.dao.ProxyDAO.isInstance(delegate) ) {
          if ( foam.dao.FilteredDAO.isInstance(delegate) ) {
            return delegate.delegate;
          }
          delegate = delegate.delegate;
        }
        return dao;
      }
    },
    {
      class: 'Class',
      name: 'of',
      expression: function(dao$of) { return dao$of; }
    },
    {
      class: 'String',
      name: 'browseTitle',
      factory: function() { return this.of.model_.plural; }
    },
    {
      class: 'String',
      name: 'browseSubtitle',
      factory: function() { return 'View all ' + this.browseTitle.toLowerCase() + '.' }
    },
    {
      // TODO: Make ViewSpecWithJava a refinement to ViewSpec and change below to a ViewSpec
      class: 'foam.u2.ViewSpecWithJava',
      name: 'summaryView',
      expression: function(defaultColumns) {
        return {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false,
          columns: defaultColumns,
          css: {
            width: '100%',
            height: '424px'
          }
        };
      }
    },
    {
      class: 'String',
      name: 'createTitle',
      expression: function(of) { return 'Create a New ' + of.model_.label; }
    },
    {
      class: 'Array',
      name: 'defaultColumns',
      factory: null,
      expression: function(of) {
        var tableColumns = of.getAxiomByName('tableColumns');

        return tableColumns
          ? tableColumns.columns
          : of.getAxiomsByClass(foam.core.Property).map(p => p.name);
      }
    },
    {
      class: 'Enum',
      of: 'foam.comics.SearchMode',
      name: 'searchMode',
      help: `
        The level of search capabilities that the controller should have.
      `,
      value: 'FULL'
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'browseBorder',
      expression: function() {
        // Can't use a value here because java tries to generate a HasMap
        // for it which doesn't jive with the AbstractFObjectPropertyInfo.
        return { class: 'foam.u2.borders.NullBorder' };
      }
    },
    {
      class: 'FObjectArray',
      of: 'foam.comics.v2.namedViews.NamedViewCollection',
      name: 'browseViews',
      factory: null,
      expression: function(of) {
        return of.getAxiomsByClass(this.NamedViewCollection);
      }
    },
    {
      class: 'FObjectArray',
      of: 'foam.comics.v2.CannedQuery',
      name: 'cannedQueries',
      factory: null,
      expression: function(of) {
        return of.getAxiomsByClass(this.CannedQuery);
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewBorder',
      factory: function() {
        // Can't use a value here because java tries to generate a HasMap
        // for it which doesn't jive with the AbstractFObjectPropertyInfo.
        return { class: 'foam.u2.borders.NullBorder' };
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'createPredicate',
      documentation: 'If set to false, the "Create" button will not be visible.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'editPredicate',
      documentation: 'True to enable the edit button.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'deletePredicate',
      documentation: 'True to enable the delete button.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      of: 'foam.mlang.predicate.Predicate',
      name: 'filterExportPredicate',
      documentation: 'Filtering the types of formats user is able to export from TableView'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.CRUDEnabledActionsAuth',
      name: 'CRUDEnabledActionsAuth'
    },
    {
      class: 'Boolean',
      name: 'hideQueryBar'
    }
  ]
});
<commit_msg>Adjust summary view height<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOControllerConfig',

  documentation: `
    A customizable model to configure any DAOController
  `,

  requires: [
    'foam.comics.SearchMode',
    'foam.comics.v2.CannedQuery',
    'foam.comics.v2.namedViews.NamedViewCollection'
  ],

  properties: [
    {
      class: 'String',
      name: 'daoKey'
    },
    {
      class: 'Class',
      name: 'factory'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.mlang.predicate.Predicate',
      name: 'predicate',
      view: { class: 'foam.u2.view.JSONTextView' }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'dao',
      hidden: true,
      expression: function(daoKey, predicate) {
        var dao = this.__context__[daoKey] || foam.dao.NullDAO.create({of: foam.core.FObject});
        if ( this.hasOwnProperty('of') ) {
          dao = foam.dao.ProxyDAO.create({
            of: this.of,
            delegate: dao
          });
        }
        if ( predicate ) {
          dao = dao.where(predicate);
        }
        return dao;
      }
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'unfilteredDAO',
      hidden: true,
      expression: function(dao) {
        var delegate = dao;
        while ( delegate && foam.dao.ProxyDAO.isInstance(delegate) ) {
          if ( foam.dao.FilteredDAO.isInstance(delegate) ) {
            return delegate.delegate;
          }
          delegate = delegate.delegate;
        }
        return dao;
      }
    },
    {
      class: 'Class',
      name: 'of',
      expression: function(dao$of) { return dao$of; }
    },
    {
      class: 'String',
      name: 'browseTitle',
      factory: function() { return this.of.model_.plural; }
    },
    {
      class: 'String',
      name: 'browseSubtitle',
      factory: function() { return 'View all ' + this.browseTitle.toLowerCase() + '.' }
    },
    {
      // TODO: Make ViewSpecWithJava a refinement to ViewSpec and change below to a ViewSpec
      class: 'foam.u2.ViewSpecWithJava',
      name: 'summaryView',
      expression: function(defaultColumns) {
        return {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false,
          columns: defaultColumns,
          css: {
            width: '100%',
            'min-height': '424px'
          }
        };
      }
    },
    {
      class: 'String',
      name: 'createTitle',
      expression: function(of) { return 'Create a New ' + of.model_.label; }
    },
    {
      class: 'Array',
      name: 'defaultColumns',
      factory: null,
      expression: function(of) {
        var tableColumns = of.getAxiomByName('tableColumns');

        return tableColumns
          ? tableColumns.columns
          : of.getAxiomsByClass(foam.core.Property).map(p => p.name);
      }
    },
    {
      class: 'Enum',
      of: 'foam.comics.SearchMode',
      name: 'searchMode',
      help: `
        The level of search capabilities that the controller should have.
      `,
      value: 'FULL'
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'browseBorder',
      expression: function() {
        // Can't use a value here because java tries to generate a HasMap
        // for it which doesn't jive with the AbstractFObjectPropertyInfo.
        return { class: 'foam.u2.borders.NullBorder' };
      }
    },
    {
      class: 'FObjectArray',
      of: 'foam.comics.v2.namedViews.NamedViewCollection',
      name: 'browseViews',
      factory: null,
      expression: function(of) {
        return of.getAxiomsByClass(this.NamedViewCollection);
      }
    },
    {
      class: 'FObjectArray',
      of: 'foam.comics.v2.CannedQuery',
      name: 'cannedQueries',
      factory: null,
      expression: function(of) {
        return of.getAxiomsByClass(this.CannedQuery);
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewBorder',
      factory: function() {
        // Can't use a value here because java tries to generate a HasMap
        // for it which doesn't jive with the AbstractFObjectPropertyInfo.
        return { class: 'foam.u2.borders.NullBorder' };
      }
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'createPredicate',
      documentation: 'If set to false, the "Create" button will not be visible.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'editPredicate',
      documentation: 'True to enable the edit button.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      class: 'foam.mlang.predicate.PredicateProperty',
      name: 'deletePredicate',
      documentation: 'True to enable the delete button.',
      factory: function() {
        return foam.mlang.predicate.True.create();
      },
      javaFactory: `
        return foam.mlang.MLang.TRUE;
      `
    },
    {
      of: 'foam.mlang.predicate.Predicate',
      name: 'filterExportPredicate',
      documentation: 'Filtering the types of formats user is able to export from TableView'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.CRUDEnabledActionsAuth',
      name: 'CRUDEnabledActionsAuth'
    },
    {
      class: 'Boolean',
      name: 'hideQueryBar'
    }
  ]
});
<|endoftext|>


<commit_before>function HistoryOptions(wsId) {

	var worksheetId = wsId;
	var historyOptionsDiv;

	var options = [
		{
			name: "Delete Commands",
			func: deleteHistory
		},
	    {
			name: "Export Commands",
			func: exportHistory
		}
	];

	function hideDropdown() {
		$('.dropdown.open .dropdown-toggle').dropdown('toggle');
	}

	function getCheckboxState(event) {
		var target = event.target;
		var checkbox;
		if (target.localName == "input") {
			checkbox = target;
		} else {
			checkbox = $("input[type='checkbox']", target)[0];
			$(checkbox).prop('checked', !checkbox.checked);
		}
		return checkbox.checked;
	}

	function deleteHistory() {
		hideDropdown();
		var response = extractHistoryCheckboxes();
		var info = generateInfoObject(worksheetId, "", "ExportOrDeleteHistoryCommand");

		var newInfo = info['newInfo'];
		newInfo.push(getParamObject("commandList", response, "other"));
		newInfo.push(getParamObject("isDelete", "true", "other"));
		newInfo.push(getParamObject("tripleStoreUrl", $('#txtModel_URL').text(), "other"));
		newInfo.push(getParamObject("requestUrl", window.location.href.replace("/#", ""), "other"));
		info["newInfo"] = JSON.stringify(newInfo);
		showLoading(info["worksheetId"]);
		sendRequest(info, worksheetId);
	}

	function exportHistory() {
		hideDropdown();
		var response = extractHistoryCheckboxes();
		var info = generateInfoObject(worksheetId, "", "ExportOrDeleteHistoryCommand");

		var newInfo = info['newInfo'];
		newInfo.push(getParamObject("commandList", response, "other"));
		newInfo.push(getParamObject("isDelete", "false", "other"));
		newInfo.push(getParamObject("tripleStoreUrl", $('#txtModel_URL').text(), "other"));
		newInfo.push(getParamObject("requestUrl", window.location.href.replace("/#", ""), "other"));
		info["newInfo"] = JSON.stringify(newInfo);
		showLoading(info["worksheetId"]);
		sendRequest(info, worksheetId);
	}

	function extractHistoryCheckboxes() {
		var checkboxes = $("#commandHistory").find(":checked");
		var response = "";
		for (var i = 0; i < checkboxes.length; i++) {
			if (i == 0) {
				response += checkboxes[i].value;
			}
			else {
				response += "," + checkboxes[i].value;
			}
		}
		console.log(response);
		return response;
	}

	this.generateJS = function() {
		var div =
			$("<div>")
			.attr("id", "HistoryOptionsDiv")
			.data("worksheetId", worksheetId)
			.addClass("dropdown")
			.append($("<a>")
				.attr("href", "#")
				.addClass("dropdown-toggle")
				.attr("id", "optionsButton" + worksheetId)
				.data("worksheetId", worksheetId)
				.attr("data-toggle", "dropdown")
				.text("Command History")
				.append($("<span>").addClass("caret"))
			);

		var ul = $("<ul>").addClass("dropdown-menu");
		//console.log("There are " + options.length + " menu items");
		for (var i = 0; i < options.length; i++) {
			var option = options[i];
			var li = $("<li>");
			//console.log("Got option" +  option);
			var title = option.name;
			if (title == "divider")
				li.addClass("divider");
			else {
				var func = option.func;
				var a = $("<a>")
					.attr("href", "#");
				if (option.showCheckbox) {
					var checkbox = $("<input>").attr("type", "checkbox");
					if (option.defaultChecked)
						checkbox.attr("checked", "checked");
					var label = $("<span>").append(checkbox).append(" ").append(title);
					a.append(label);
					a.click(func);
				} else if (option.addLevel) {
					addLevels(li, a, option, worksheetId);
				} else {
					a.text(title);
					a.click(func);
				}

				li.append(a);
			}
			if (option.initFunc)
				option.initFunc();
			ul.append(li);
		};
		div.append(ul);
		historyOptionsDiv = div;
		return div;
	};
}<commit_msg>fix history delete to use the worksheetId<commit_after>function HistoryOptions(wsId) {

	var worksheetId = wsId;
	var historyOptionsDiv;

	var options = [
		{
			name: "Delete Commands",
			func: deleteHistory
		},
	    {
			name: "Export Commands",
			func: exportHistory
		}
	];

	function hideDropdown() {
		$('.dropdown.open .dropdown-toggle').dropdown('toggle');
	}

	function getCheckboxState(event) {
		var target = event.target;
		var checkbox;
		if (target.localName == "input") {
			checkbox = target;
		} else {
			checkbox = $("input[type='checkbox']", target)[0];
			$(checkbox).prop('checked', !checkbox.checked);
		}
		return checkbox.checked;
	}

	function deleteHistory() {
		hideDropdown();
		var response = extractHistoryCheckboxes();
		var info = generateInfoObject(worksheetId, "", "ExportOrDeleteHistoryCommand");

		var newInfo = info['newInfo'];
		newInfo.push(getParamObject("commandList", response, "other"));
		newInfo.push(getParamObject("isDelete", "true", "other"));
		newInfo.push(getParamObject("tripleStoreUrl", $('#txtModel_URL').text(), "other"));
		newInfo.push(getParamObject("requestUrl", window.location.href.replace("/#", ""), "other"));
		info["newInfo"] = JSON.stringify(newInfo);
		showLoading(info["worksheetId"]);
		sendRequest(info, worksheetId);
	}

	function exportHistory() {
		hideDropdown();
		var response = extractHistoryCheckboxes();
		var info = generateInfoObject(worksheetId, "", "ExportOrDeleteHistoryCommand");

		var newInfo = info['newInfo'];
		newInfo.push(getParamObject("commandList", response, "other"));
		newInfo.push(getParamObject("isDelete", "false", "other"));
		newInfo.push(getParamObject("tripleStoreUrl", $('#txtModel_URL').text(), "other"));
		newInfo.push(getParamObject("requestUrl", window.location.href.replace("/#", ""), "other"));
		info["newInfo"] = JSON.stringify(newInfo);
		showLoading(info["worksheetId"]);
		sendRequest(info, worksheetId);
	}

	function extractHistoryCheckboxes() {
		var checkboxes = $("#commandHistoryBody_" + worksheetId).find(":checked");
		var response = "";
		for (var i = 0; i < checkboxes.length; i++) {
			if (i == 0) {
				response += checkboxes[i].value;
			}
			else {
				response += "," + checkboxes[i].value;
			}
		}
		console.log(response);
		return response;
	}

	this.generateJS = function() {
		var div =
			$("<div>")
			.attr("id", "HistoryOptionsDiv")
			.data("worksheetId", worksheetId)
			.addClass("dropdown")
			.append($("<a>")
				.attr("href", "#")
				.addClass("dropdown-toggle")
				.attr("id", "optionsButton" + worksheetId)
				.data("worksheetId", worksheetId)
				.attr("data-toggle", "dropdown")
				.text("Command History")
				.append($("<span>").addClass("caret"))
			);

		var ul = $("<ul>").addClass("dropdown-menu");
		//console.log("There are " + options.length + " menu items");
		for (var i = 0; i < options.length; i++) {
			var option = options[i];
			var li = $("<li>");
			//console.log("Got option" +  option);
			var title = option.name;
			if (title == "divider")
				li.addClass("divider");
			else {
				var func = option.func;
				var a = $("<a>")
					.attr("href", "#");
				if (option.showCheckbox) {
					var checkbox = $("<input>").attr("type", "checkbox");
					if (option.defaultChecked)
						checkbox.attr("checked", "checked");
					var label = $("<span>").append(checkbox).append(" ").append(title);
					a.append(label);
					a.click(func);
				} else if (option.addLevel) {
					addLevels(li, a, option, worksheetId);
				} else {
					a.text(title);
					a.click(func);
				}

				li.append(a);
			}
			if (option.initFunc)
				option.initFunc();
			ul.append(li);
		};
		div.append(ul);
		historyOptionsDiv = div;
		return div;
	};
}<|endoftext|>


<commit_before>var Schema = require("truffle-contract-schema");
var expect = require("truffle-expect");
var fs = require("fs-extra");
var path = require("path");
var async = require("async");
var _ = require("lodash");

function Artifactor(destination, source_directory) {
  this.destination = destination;
  this.source_directory = source_directory;
};

Artifactor.prototype.save = function(object) {
  var self = this;

  return new Promise(function(accept, reject) {
    object = Schema.normalize(object);

    if (object.contractName == null) {
      return reject(new Error("You must specify a contract name."));
    }

    // Build the source path from input object.
    var output_path = object.sourcePath || object.contractName;

    // Remove source directory if prefixed.
    output_path = output_path.replace(self.source_directory, "");

    // Remove .sol extension
    output_path = output_path.replace(".sol");

    // Create new path off of contracts_build_directory.
    output_path = path.join(self.destination.replace(self.source_directory, ""), output_path);
    output_path = path.resolve(output_path);

    // Add json extension.
    output_path = output_path + ".json";

    fs.readFile(output_path, {encoding: "utf8"}, function(err, json) {
      // No need to handle the error. If the file doesn't exist then we'll start afresh
      // with a new object.

      var finalObject = object;

      if (!err) {
        var existingObjDirty;
        try {
          existingObjDirty = JSON.parse(json);
        } catch (e) {
          reject(e);
        }

        // normalize existing and merge into final
        finalObject = Schema.normalize(existingObjDirty);
        _.merge(finalObject, object);
      }

      // update timestamp
      finalObject.updateAt = new Date().toISOString();

      // output object
      fs.outputFile(output_path, JSON.stringify(finalObject, null, 2), "utf8", function(err) {
        if (err) return reject(err);
        accept();
      });
    });
  });
};

Artifactor.prototype.saveAll = function(objects) {
  var self = this;

  if (Array.isArray(objects)) {
    var array = objects;
    objects = {};

    array.forEach(function(item) {
      objects[item.contract_name] = item;
    })
  }

  return new Promise(function(accept, reject) {
    fs.stat(self.destination, function(err, stat) {
      if (err) {
        return reject(new Error("Desination " + self.destination + " doesn't exist!"));
      }

      async.each(Object.keys(objects), function(contractName, done) {
        var object = objects[contractName];
        object.contractName = contractName;
        self.save(object).then(done).catch(done);
      }, function(err) {
        if (err) return reject(err);
        accept();
      });
    });
  });
};

module.exports = Artifactor;
<commit_msg>Remove bad code. Refactored too.<commit_after>var Schema = require("truffle-contract-schema");
var expect = require("truffle-expect");
var fs = require("fs-extra");
var path = require("path");
var async = require("async");
var _ = require("lodash");

function Artifactor(destination) {
  this.destination = destination;
};

Artifactor.prototype.save = function(object) {
  var self = this;

  return new Promise(function(accept, reject) {
    object = Schema.normalize(object);

    if (object.contractName == null) {
      return reject(new Error("You must specify a contract name."));
    }

    var output_path = object.contractName;

    // Create new path off of destination.
    output_path = path.join(self.destination, output_path);
    output_path = path.resolve(output_path);

    // Add json extension.
    output_path = output_path + ".json";

    fs.readFile(output_path, {encoding: "utf8"}, function(err, json) {
      // No need to handle the error. If the file doesn't exist then we'll start afresh
      // with a new object.

      var finalObject = object;

      if (!err) {
        var existingObjDirty;
        try {
          existingObjDirty = JSON.parse(json);
        } catch (e) {
          reject(e);
        }

        // normalize existing and merge into final
        finalObject = Schema.normalize(existingObjDirty);
        _.merge(finalObject, object);
      }

      // update timestamp
      finalObject.updatedAt = new Date().toISOString();

      // output object
      fs.outputFile(output_path, JSON.stringify(finalObject, null, 2), "utf8", function(err) {
        if (err) return reject(err);
        accept();
      });
    });
  });
};

Artifactor.prototype.saveAll = function(objects) {
  var self = this;

  if (Array.isArray(objects)) {
    var array = objects;
    objects = {};

    array.forEach(function(item) {
      objects[item.contract_name] = item;
    })
  }

  return new Promise(function(accept, reject) {
    fs.stat(self.destination, function(err, stat) {
      if (err) {
        return reject(new Error("Desination " + self.destination + " doesn't exist!"));
      }
      accept();
    });
  }).then(function() {
    var promises = [];

    Object.keys(objects).forEach(function(contractName) {
      var object = objects[contractName];
      object.contractName = contractName;
      promises.push(self.save(object));
    });

    return Promise.all(promises);
  });
};

module.exports = Artifactor;
<|endoftext|>


<commit_before>/* jshint scripturl:true */

import DOMHelper from "../dom-helper";
import SafeString from "htmlbars-util/safe-string";

var svgNamespace = "http://www.w3.org/2000/svg",
    xlinkNamespace = "http://www.w3.org/1999/xlink";
var domHelper = new DOMHelper();

QUnit.module('AttrMorph');

test("can update a dom node", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent('twang');
  equal(element.id, 'twang', 'id property is set');
  equal(element.getAttribute('id'), 'twang', 'id attribute is set');
});

test("can clear", function(){
  expect(0);
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.clear();
});

test("calling destroy does not throw", function(){
  expect(1);
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');

  morph.destroy();

  equal(morph.element, null, 'clears element from morph');
});

test("can update property", function(){
  var element = domHelper.createElement('input');
  var morph = domHelper.createAttrMorph(element, 'disabled');
  morph.setContent(true);
  equal(element.disabled, true, 'disabled property is set');
  morph.setContent(false);
  equal(element.disabled, false, 'disabled property is set');
});

test("does not add undefined properties on initial render", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent(undefined);
  equal(element.id, '', 'property should not be set');
  morph.setContent('foo-bar');
  equal(element.id, 'foo-bar', 'property should be set');
});

test("does not add null properties on initial render", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent(null);
  equal(element.id, '', 'property should not be set');
  morph.setContent('foo-bar');
  equal(element.id, 'foo-bar', 'property should be set');
});

test("can update attribute", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'data-bop');
  morph.setContent('kpow');
  equal(element.getAttribute('data-bop'), 'kpow', 'data-bop attribute is set');
  morph.setContent(null);
  equal(element.getAttribute('data-bop'), undefined, 'data-bop attribute is removed');
});

test("can remove ns attribute with null", function(){
  var element = domHelper.createElement('svg');
  domHelper.setAttribute(element, 'xlink:title', 'Great Title', xlinkNamespace);
  var morph = domHelper.createAttrMorph(element, 'xlink:title', xlinkNamespace);
  morph.setContent(null);
  equal(element.getAttribute('xlink:title'), undefined, 'ns attribute is removed');
});

test("can remove attribute with undefined", function(){
  var element = domHelper.createElement('div');
  element.setAttribute('data-bop', 'kpow');
  var morph = domHelper.createAttrMorph(element, 'data-bop');
  morph.setContent(undefined);
  equal(element.getAttribute('data-bop'), undefined, 'data-bop attribute is removed');
});

test("can remove ns attribute with undefined", function(){
  var element = domHelper.createElement('svg');
  domHelper.setAttribute(element, 'xlink:title', 'Great Title', xlinkNamespace);
  var morph = domHelper.createAttrMorph(element, 'xlink:title', xlinkNamespace);
  morph.setContent(undefined);
  equal(element.getAttribute('xlink:title'), undefined, 'ns attribute is removed');
});

test("can update svg attribute", function(){
  domHelper.setNamespace(svgNamespace);
  var element = domHelper.createElement('svg');
  var morph = domHelper.createAttrMorph(element, 'height');
  morph.setContent('50%');
  equal(element.getAttribute('height'), '50%', 'svg attr is set');
  morph.setContent(null);
  equal(element.getAttribute('height'), undefined, 'svg attr is removed');
});

test("can update style attribute", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'style');
  morph.setContent('color: red;');
  equal(element.getAttribute('style'), 'color: red;', 'style attr is set');
  morph.setContent(null);
  equal(element.getAttribute('style'), undefined, 'style attr is removed');
});

var badTags = [
  { tag: 'a', attr: 'href' },
  { tag: 'body', attr: 'background' },
  { tag: 'link', attr: 'href' },
  { tag: 'img', attr: 'src' },
  { tag: 'iframe', attr: 'src'}
];

for (var i=0, l=badTags.length; i<l; i++) {
  (function(){
    var subject = badTags[i];

    test(subject.tag +" "+subject.attr+" is sanitized when using blacklisted protocol", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createAttrMorph(element, subject.attr);
      morph.setContent('javascript://example.com');

      equal( element.getAttribute(subject.attr),
            'unsafe:javascript://example.com',
            'attribute is escaped');
    });

    test(subject.tag +" "+subject.attr+" is not sanitized when using non-whitelisted protocol with a SafeString", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createAttrMorph(element, subject.attr);
      try {
        morph.setContent(new SafeString('javascript://example.com'));

        equal( element.getAttribute(subject.attr),
              'javascript://example.com',
              'attribute is not escaped');
      } catch(e) {
        // IE does not allow javascript: to be set on img src
        ok(true, 'caught exception '+e);
      }
    });

    test(subject.tag +" "+subject.attr+" is not sanitized when using unsafe attr morph", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createUnsafeAttrMorph(element, subject.attr);
      try {
        morph.setContent('javascript://example.com');

        equal( element.getAttribute(subject.attr),
              'javascript://example.com',
              'attribute is not escaped');
      } catch(e) {
        // IE does not allow javascript: to be set on img src
        ok(true, 'caught exception '+e);
      }
    });

  })(); //jshint ignore:line
}

if (document && document.createElementNS) {

test("detects attribute's namespace if it is not passed as an argument", function () {
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'xlink:href');
  morph.setContent('#circle');
  equal(element.attributes[0].namespaceURI, 'http://www.w3.org/1999/xlink', 'attribute has correct namespace');
});

test("can update namespaced attribute", function(){
  domHelper.setNamespace(svgNamespace);
  var element = domHelper.createElement('svg');
  var morph = domHelper.createAttrMorph(element, 'xlink:href', 'http://www.w3.org/1999/xlink');
  morph.setContent('#other');
  equal(element.getAttributeNS('http://www.w3.org/1999/xlink','href'), '#other', 'namespaced attr is set');
  equal(element.attributes[0].namespaceURI, 'http://www.w3.org/1999/xlink');
  equal(element.attributes[0].name, 'xlink:href');
  equal(element.attributes[0].localName, 'href');
  equal(element.attributes[0].value, '#other');
  morph.setContent(null);
  // safari returns '' while other browsers return undefined
  equal(!!element.getAttributeNS('http://www.w3.org/1999/xlink','href'), false, 'namespaced attr is removed');
});

}

test("embed src as data uri is sanitized", function() {
  var element = document.createElement('embed');
  var morph = domHelper.createAttrMorph(element, 'src');
  morph.setContent('data:image/svg+xml;base64,PH');

  equal( element.getAttribute('src'),
        'unsafe:data:image/svg+xml;base64,PH',
        'attribute is escaped');
});
<commit_msg>extra tests for input.maxLength<commit_after>/* jshint scripturl:true */

import DOMHelper from "../dom-helper";
import SafeString from "htmlbars-util/safe-string";

var svgNamespace = "http://www.w3.org/2000/svg",
    xlinkNamespace = "http://www.w3.org/1999/xlink";
var domHelper = new DOMHelper();

QUnit.module('AttrMorph');

test("can update a dom node", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent('twang');
  equal(element.id, 'twang', 'id property is set');
  equal(element.getAttribute('id'), 'twang', 'id attribute is set');
});

test("can clear", function(){
  expect(0);
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.clear();
});

test("calling destroy does not throw", function(){
  expect(1);
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');

  morph.destroy();

  equal(morph.element, null, 'clears element from morph');
});

test("can update property", function(){
  var element = domHelper.createElement('input');
  var morph = domHelper.createAttrMorph(element, 'disabled');
  morph.setContent(true);
  equal(element.disabled, true, 'disabled property is set');
  morph.setContent(false);
  equal(element.disabled, false, 'disabled property is set');
});

test("input.maxLength", function(){
  var element = domHelper.createElement('input');
  var morph = domHelper.createAttrMorph(element, 'maxLength');
  // different browsers have different defaults FF: -1, Chrome/Blink: 524288;
  var MAX_LENGTH = element.maxLength;

  morph.setContent(null);
  equal(element.maxLength, MAX_LENGTH, 'property is w/e is default');

  morph.setContent(1);
  equal(element.maxLength, 1, 'should be 1');

  morph.setContent(null);
  equal(element.maxLength, 0, 'property 0, result of element.maxLength = ""');
});

test("input.maxlength (all lowercase)", function(){
  var element = domHelper.createElement('input');
  var morph = domHelper.createAttrMorph(element, 'maxlength');
  // different browsers have different defaults FF: -1, Chrome/Blink: 524288;
  var DEFAULT_MAX_LENGTH = element.maxLength;

  morph.setContent(null);
  equal(element.maxLength, DEFAULT_MAX_LENGTH, 'property is w/e is default');

  morph.setContent(1);
  equal(element.maxLength, 1, 'property is w/e is default');

  morph.setContent(null);
  equal(element.maxLength, DEFAULT_MAX_LENGTH, 'property is w/e is default');
});

test("does not add undefined properties on initial render", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent(undefined);
  equal(element.id, '', 'property should not be set');
  morph.setContent('foo-bar');
  equal(element.id, 'foo-bar', 'property should be set');
});

test("does not add null properties on initial render", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'id');
  morph.setContent(null);
  equal(element.id, '', 'property should not be set');
  morph.setContent('foo-bar');
  equal(element.id, 'foo-bar', 'property should be set');
});

test("can update attribute", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'data-bop');
  morph.setContent('kpow');
  equal(element.getAttribute('data-bop'), 'kpow', 'data-bop attribute is set');
  morph.setContent(null);
  equal(element.getAttribute('data-bop'), undefined, 'data-bop attribute is removed');
});

test("can remove ns attribute with null", function(){
  var element = domHelper.createElement('svg');
  domHelper.setAttribute(element, 'xlink:title', 'Great Title', xlinkNamespace);
  var morph = domHelper.createAttrMorph(element, 'xlink:title', xlinkNamespace);
  morph.setContent(null);
  equal(element.getAttribute('xlink:title'), undefined, 'ns attribute is removed');
});

test("can remove attribute with undefined", function(){
  var element = domHelper.createElement('div');
  element.setAttribute('data-bop', 'kpow');
  var morph = domHelper.createAttrMorph(element, 'data-bop');
  morph.setContent(undefined);
  equal(element.getAttribute('data-bop'), undefined, 'data-bop attribute is removed');
});

test("can remove ns attribute with undefined", function(){
  var element = domHelper.createElement('svg');
  domHelper.setAttribute(element, 'xlink:title', 'Great Title', xlinkNamespace);
  var morph = domHelper.createAttrMorph(element, 'xlink:title', xlinkNamespace);
  morph.setContent(undefined);
  equal(element.getAttribute('xlink:title'), undefined, 'ns attribute is removed');
});

test("can update svg attribute", function(){
  domHelper.setNamespace(svgNamespace);
  var element = domHelper.createElement('svg');
  var morph = domHelper.createAttrMorph(element, 'height');
  morph.setContent('50%');
  equal(element.getAttribute('height'), '50%', 'svg attr is set');
  morph.setContent(null);
  equal(element.getAttribute('height'), undefined, 'svg attr is removed');
});

test("can update style attribute", function(){
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'style');
  morph.setContent('color: red;');
  equal(element.getAttribute('style'), 'color: red;', 'style attr is set');
  morph.setContent(null);
  equal(element.getAttribute('style'), undefined, 'style attr is removed');
});

var badTags = [
  { tag: 'a', attr: 'href' },
  { tag: 'body', attr: 'background' },
  { tag: 'link', attr: 'href' },
  { tag: 'img', attr: 'src' },
  { tag: 'iframe', attr: 'src'}
];

for (var i=0, l=badTags.length; i<l; i++) {
  (function(){
    var subject = badTags[i];

    test(subject.tag +" "+subject.attr+" is sanitized when using blacklisted protocol", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createAttrMorph(element, subject.attr);
      morph.setContent('javascript://example.com');

      equal( element.getAttribute(subject.attr),
            'unsafe:javascript://example.com',
            'attribute is escaped');
    });

    test(subject.tag +" "+subject.attr+" is not sanitized when using non-whitelisted protocol with a SafeString", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createAttrMorph(element, subject.attr);
      try {
        morph.setContent(new SafeString('javascript://example.com'));

        equal( element.getAttribute(subject.attr),
              'javascript://example.com',
              'attribute is not escaped');
      } catch(e) {
        // IE does not allow javascript: to be set on img src
        ok(true, 'caught exception '+e);
      }
    });

    test(subject.tag +" "+subject.attr+" is not sanitized when using unsafe attr morph", function() {
      var element = document.createElement(subject.tag);
      var morph = domHelper.createUnsafeAttrMorph(element, subject.attr);
      try {
        morph.setContent('javascript://example.com');

        equal( element.getAttribute(subject.attr),
              'javascript://example.com',
              'attribute is not escaped');
      } catch(e) {
        // IE does not allow javascript: to be set on img src
        ok(true, 'caught exception '+e);
      }
    });

  })(); //jshint ignore:line
}

if (document && document.createElementNS) {

test("detects attribute's namespace if it is not passed as an argument", function () {
  var element = domHelper.createElement('div');
  var morph = domHelper.createAttrMorph(element, 'xlink:href');
  morph.setContent('#circle');
  equal(element.attributes[0].namespaceURI, 'http://www.w3.org/1999/xlink', 'attribute has correct namespace');
});

test("can update namespaced attribute", function(){
  domHelper.setNamespace(svgNamespace);
  var element = domHelper.createElement('svg');
  var morph = domHelper.createAttrMorph(element, 'xlink:href', 'http://www.w3.org/1999/xlink');
  morph.setContent('#other');
  equal(element.getAttributeNS('http://www.w3.org/1999/xlink','href'), '#other', 'namespaced attr is set');
  equal(element.attributes[0].namespaceURI, 'http://www.w3.org/1999/xlink');
  equal(element.attributes[0].name, 'xlink:href');
  equal(element.attributes[0].localName, 'href');
  equal(element.attributes[0].value, '#other');
  morph.setContent(null);
  // safari returns '' while other browsers return undefined
  equal(!!element.getAttributeNS('http://www.w3.org/1999/xlink','href'), false, 'namespaced attr is removed');
});

}

test("embed src as data uri is sanitized", function() {
  var element = document.createElement('embed');
  var morph = domHelper.createAttrMorph(element, 'src');
  morph.setContent('data:image/svg+xml;base64,PH');

  equal( element.getAttribute('src'),
        'unsafe:data:image/svg+xml;base64,PH',
        'attribute is escaped');
});
<|endoftext|>


<commit_before>exports.BattleFormatsData = {
        volcanion: {
                inherit: true,
                tier: 'Uber'
        },
        arceusgod: {
                inherit: true,
                tier: 'Uber'
        }
};
<commit_msg>Update formats-data.js<commit_after>exports.BattleFormatsData = {
        volcanion: {
                inherit: true,
                tier: 'Uber'
        },
        arceusgod: {
                inherit: true,
                tier: 'Uber'
        },
        meloettapirouette: {
                inherit: true,
                tier: 'OU'
        }
};
<|endoftext|>


<commit_before>'use strict';

var angularMeteorCollections = angular.module('angular-meteor.meteor-collection',
  ['angular-meteor.subscribe', 'angular-meteor.utils', 'diffArray']);

var AngularMeteorCollection = function (cursor, $q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout) {

  var self = [];

  self.__proto__ = AngularMeteorCollection.prototype;
  self.__proto__.$q = $q;
  self.__proto__.$meteorSubscribe = $meteorSubscribe;
  self.__proto__.$rootScope = $rootScope;
  self.__proto__.$timeout = $timeout;

  self.$$collection = $meteorUtils.getCollectionByName(cursor.collection.name);

  return self;
};

AngularMeteorCollection.prototype = [];

AngularMeteorCollection.prototype.subscribe = function () {
  var self = this;
  self.$meteorSubscribe.subscribe.apply(this, arguments);
  return this;
};

AngularMeteorCollection.prototype.save = function save(docs, useUnsetModifier) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The upsertObject function will either update an object if the _id exists
   * or insert an object if the _id is not set in the collection.
   * Returns a promise.
   */
  function upsertObject(item, $q) {
    var deferred = $q.defer();

    item = angular.copy(item);

    if (item._id) { // Performs an update if the _id property is set.
      var item_id = item._id; // Store the _id in temporary variable
      delete item._id; // Remove the _id property so that it can be $set using update.
      var objectId = (item_id._str) ? new Meteor.Collection.ObjectID(item_id._str) : item_id;
      var modifier = (useUnsetModifier) ? {$unset: item} : {$set: item};

      collection.update(objectId, modifier, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "updated"});
        }
      });
    } else { // Performs an insert if the _id property isn't set.
      collection.insert(item, function (error, result) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: result, action: "inserted"});
        }
      });
    }

    return deferred.promise;
  }

  /*
   * How to update the collection depending on the 'docs' argument passed.
   */
  if (docs) { // Checks if a 'docs' argument was passed.
    if (angular.isArray(docs)) { // If an array of objects were passed.
      angular.forEach(docs, function (doc) {
        this.push(upsertObject(doc, $q));
      }, promises);
    } else { // If a single object was passed.
      promises.push(upsertObject(docs, $q));
    }
  } else { // If no 'docs' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(upsertObject(doc, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.remove = function remove(keys) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The removeObject function will delete an object with the _id property
   * equal to the specified key.
   * Returns a promise.
   */
  function removeObject(key, $q) {
    var deferred = $q.defer();

    if (key) { // Checks if 'key' argument is set.
      if (key._id) {
        key = key._id;
      }
      var objectId = (key._str) ? new Meteor.Collection.ObjectID(key._str) : key;

      collection.remove(objectId, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "removed"});
        }
      });
    } else {
      deferred.reject("key cannot be null");
    }

    return deferred.promise;
  }

  /*
   * What to remove from collection depending on the 'keys' argument passed.
   */
  if (keys) { // Checks if a 'keys' argument was passed.
    if (angular.isArray(keys)) { // If an array of keys were passed.
      angular.forEach(keys, function (key) {
        this.push(removeObject(key, $q));
      }, promises);
    } else { // If a single key was passed.
      promises.push(removeObject(keys, $q));
    }
  } else { // If no 'keys' argument was passed, save the entire collection.
    // When removing all, we do not use collection.remove({}) because Meteor doesn't give the client side that permissions
    // http://stackoverflow.com/a/15465286/1426570
    var originalSelf = angular.copy(self);
    angular.forEach(originalSelf, function (doc) {
      this.push(removeObject(doc._id, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.updateCursor = function (cursor) {
  var self = this,
    $rootScope = self.$rootScope,
    $timeout = self.$timeout;

  function safeApply() {
    // Clearing the watch is needed so no updates are sent to server
    // while handling updates from the server
    self.UPDATING_FROM_SERVER = true;
    if (!$rootScope.$$phase) $rootScope.$apply();
    // Making sure we are setting to false only after one digest cycle and not before
    $timeout(function(){
      self.UPDATING_FROM_SERVER = false;
    },0,false);
  }

  // XXX - consider adding an option for a non-orderd result
  // for faster performance
  if (self.observeHandle) {
    self.observeHandle.stop();
  }

  self.observeHandle = cursor.observeChanges({
    addedBefore: function (id, fields, before) {
      var newItem = angular.extend(fields, {_id: id});
      if (before == null) {
        self.push(newItem);
      }
      else {
        var beforeIndex = _.indexOf(self, _.findWhere(self, { _id: before}));
        self.splice(beforeIndex, 0, newItem);
      }
      safeApply();
    },
    changed: function (id, fields) {
      angular.extend(_.findWhere(self, {_id: id}), fields);
      safeApply();
    },
    movedBefore: function (id, before) {
      var index = self.indexOf(_.findWhere(self, {_id: id}));
      var removed = self.splice(index, 1)[0];
      if (before == null) {
        self.push(removed);
      }
      else {
        var beforeIndex = _.indexOf(self, _.findWhere(self, { _id: before}));
        self.splice(beforeIndex, 0, removed);
      }
      safeApply();
    },
    removed: function (id) {
      var removedObject = _.findWhere(self, {_id: id});
      if (removedObject){
        self.splice(self.indexOf(removedObject), 1);
        safeApply();
      }
    }
  });
};

AngularMeteorCollection.prototype.stop = function () {
  if (this.unregisterAutoBind)
    this.unregisterAutoBind();

  this.observeHandle.stop();
  while (this.length > 0) {
    this.pop();
  }
};

angularMeteorCollections.factory('$meteorCollection', ['$q', '$meteorSubscribe', '$meteorUtils', '$rootScope', '$timeout', 'diffArray',
  function ($q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout, diffArray) {
    return function (reactiveFunc, auto) {
      // Validate parameters
      if (!reactiveFunc) {
        throw new TypeError("The first argument of $meteorCollection is undefined.");
      }
      if (!(typeof reactiveFunc == "function" || reactiveFunc instanceof Mongo.Collection)) {
        throw new TypeError("The first argument of $meteorCollection must be a function or a Mongo.Collection.");
      }
      auto = auto !== false;

      if (reactiveFunc instanceof Mongo.Collection) {
        var collection = reactiveFunc;
        reactiveFunc = function() {
          return collection.find({});
        }
      }

      var ngCollection = new AngularMeteorCollection(reactiveFunc(), $q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout);

      function setAutoBind() {
        if (auto) { // Deep watches the model and performs autobind.
          ngCollection.unregisterAutoBind = $rootScope.$watch(function () {
            return _.without(ngCollection, 'UPDATING_FROM_SERVER');
          }, function (newItems, oldItems) {
            if (!ngCollection.UPDATING_FROM_SERVER && newItems !== oldItems) {

              diffArray(angular.copy(oldItems), angular.copy(newItems), {
                addedAt: function (id, item, index) {
                  var newValue = angular.copy(ngCollection[index]);
                  ngCollection.unregisterAutoBind();
                  ngCollection.splice( index, 1 );
                  setAutoBind();
                  ngCollection.save(newValue);
                },
                removedAt: function (id, item, index) {
                  ngCollection.remove(id);
                },
                changedAt: function (id, setDiff, unsetDiff, index) {
                  if (setDiff)
                    ngCollection.save(setDiff);

                  if (unsetDiff)
                    ngCollection.save(unsetDiff, true);
                },
                movedTo: function (id, item, fromIndex, toIndex) {
                  // XXX do we need this?
                }
              });
            }
          }, true);
        }
      }

      /**
       * Fetches the latest data from Meteor and update the data variable.
       */
      Tracker.autorun(function () {
        // When the reactive func gets recomputated we need to stop any previous
        // observeChanges
        Tracker.onInvalidate(function () {
          ngCollection.stop();
        });
        ngCollection.updateCursor(reactiveFunc());
        setAutoBind();
      });

      return ngCollection;
    }
  }]);
<commit_msg>safeApply() - better performance<commit_after>'use strict';

var angularMeteorCollections = angular.module('angular-meteor.meteor-collection',
  ['angular-meteor.subscribe', 'angular-meteor.utils', 'diffArray']);

var AngularMeteorCollection = function (cursor, $q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout) {

  var self = [];

  self.__proto__ = AngularMeteorCollection.prototype;
  self.__proto__.$q = $q;
  self.__proto__.$meteorSubscribe = $meteorSubscribe;
  self.__proto__.$rootScope = $rootScope;
  self.__proto__.$timeout = $timeout;

  self.$$collection = $meteorUtils.getCollectionByName(cursor.collection.name);

  return self;
};

AngularMeteorCollection.prototype = [];

AngularMeteorCollection.prototype.subscribe = function () {
  var self = this;
  self.$meteorSubscribe.subscribe.apply(this, arguments);
  return this;
};

AngularMeteorCollection.prototype.save = function save(docs, useUnsetModifier) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The upsertObject function will either update an object if the _id exists
   * or insert an object if the _id is not set in the collection.
   * Returns a promise.
   */
  function upsertObject(item, $q) {
    var deferred = $q.defer();

    item = angular.copy(item);

    if (item._id) { // Performs an update if the _id property is set.
      var item_id = item._id; // Store the _id in temporary variable
      delete item._id; // Remove the _id property so that it can be $set using update.
      var objectId = (item_id._str) ? new Meteor.Collection.ObjectID(item_id._str) : item_id;
      var modifier = (useUnsetModifier) ? {$unset: item} : {$set: item};

      collection.update(objectId, modifier, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "updated"});
        }
      });
    } else { // Performs an insert if the _id property isn't set.
      collection.insert(item, function (error, result) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: result, action: "inserted"});
        }
      });
    }

    return deferred.promise;
  }

  /*
   * How to update the collection depending on the 'docs' argument passed.
   */
  if (docs) { // Checks if a 'docs' argument was passed.
    if (angular.isArray(docs)) { // If an array of objects were passed.
      angular.forEach(docs, function (doc) {
        this.push(upsertObject(doc, $q));
      }, promises);
    } else { // If a single object was passed.
      promises.push(upsertObject(docs, $q));
    }
  } else { // If no 'docs' argument was passed, save the entire collection.
    angular.forEach(self, function (doc) {
      this.push(upsertObject(doc, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.remove = function remove(keys) {
  var self = this,
    collection = self.$$collection,
    $q = self.$q,
    promises = []; // To store all promises.

  /*
   * The removeObject function will delete an object with the _id property
   * equal to the specified key.
   * Returns a promise.
   */
  function removeObject(key, $q) {
    var deferred = $q.defer();

    if (key) { // Checks if 'key' argument is set.
      if (key._id) {
        key = key._id;
      }
      var objectId = (key._str) ? new Meteor.Collection.ObjectID(key._str) : key;

      collection.remove(objectId, function (error) {
        if (error) {
          deferred.reject(error);
        } else {
          deferred.resolve({_id: objectId, action: "removed"});
        }
      });
    } else {
      deferred.reject("key cannot be null");
    }

    return deferred.promise;
  }

  /*
   * What to remove from collection depending on the 'keys' argument passed.
   */
  if (keys) { // Checks if a 'keys' argument was passed.
    if (angular.isArray(keys)) { // If an array of keys were passed.
      angular.forEach(keys, function (key) {
        this.push(removeObject(key, $q));
      }, promises);
    } else { // If a single key was passed.
      promises.push(removeObject(keys, $q));
    }
  } else { // If no 'keys' argument was passed, save the entire collection.
    // When removing all, we do not use collection.remove({}) because Meteor doesn't give the client side that permissions
    // http://stackoverflow.com/a/15465286/1426570
    var originalSelf = angular.copy(self);
    angular.forEach(originalSelf, function (doc) {
      this.push(removeObject(doc._id, $q));
    }, promises);
  }

  return $q.all(promises); // Returns all promises when they're resolved.
};

AngularMeteorCollection.prototype.updateCursor = function (cursor) {
  var self = this,
    $rootScope = self.$rootScope,
    $timeout = self.$timeout;

  function safeApply() {
    // If already updating from the server, then digest is already timeouted
    // and will run in next turn
    if (self.UPDATING_FROM_SERVER) {
      return;
    }

    // Clearing the watch is needed so no updates are sent to server
    // while handling updates from the server
    self.UPDATING_FROM_SERVER = true;

    // Making sure we are running digest only once, after all changes
    // are handled (in next turn)
    $timeout(function() {
      if (!$rootScope.$$phase) $rootScope.$apply();
      // Making sure we are setting to false only after one digest cycle
      // and not before
      $timeout(function(){
        self.UPDATING_FROM_SERVER = false;
      },0,false);
    },0,false);
  }

  // XXX - consider adding an option for a non-orderd result
  // for faster performance
  if (self.observeHandle) {
    self.observeHandle.stop();
  }

  self.observeHandle = cursor.observeChanges({
    addedBefore: function (id, fields, before) {
      var newItem = angular.extend(fields, {_id: id});
      if (before == null) {
        self.push(newItem);
      }
      else {
        var beforeIndex = _.indexOf(self, _.findWhere(self, { _id: before}));
        self.splice(beforeIndex, 0, newItem);
      }
      safeApply();
    },
    changed: function (id, fields) {
      angular.extend(_.findWhere(self, {_id: id}), fields);
      safeApply();
    },
    movedBefore: function (id, before) {
      var index = self.indexOf(_.findWhere(self, {_id: id}));
      var removed = self.splice(index, 1)[0];
      if (before == null) {
        self.push(removed);
      }
      else {
        var beforeIndex = _.indexOf(self, _.findWhere(self, { _id: before}));
        self.splice(beforeIndex, 0, removed);
      }
      safeApply();
    },
    removed: function (id) {
      var removedObject = _.findWhere(self, {_id: id});
      if (removedObject){
        self.splice(self.indexOf(removedObject), 1);
        safeApply();
      }
    }
  });
};

AngularMeteorCollection.prototype.stop = function () {
  if (this.unregisterAutoBind)
    this.unregisterAutoBind();

  this.observeHandle.stop();
  while (this.length > 0) {
    this.pop();
  }
};

angularMeteorCollections.factory('$meteorCollection', ['$q', '$meteorSubscribe', '$meteorUtils', '$rootScope', '$timeout', 'diffArray',
  function ($q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout, diffArray) {
    return function (reactiveFunc, auto) {
      // Validate parameters
      if (!reactiveFunc) {
        throw new TypeError("The first argument of $meteorCollection is undefined.");
      }
      if (!(typeof reactiveFunc == "function" || reactiveFunc instanceof Mongo.Collection)) {
        throw new TypeError("The first argument of $meteorCollection must be a function or a Mongo.Collection.");
      }
      auto = auto !== false;

      if (reactiveFunc instanceof Mongo.Collection) {
        var collection = reactiveFunc;
        reactiveFunc = function() {
          return collection.find({});
        }
      }

      var ngCollection = new AngularMeteorCollection(reactiveFunc(), $q, $meteorSubscribe, $meteorUtils, $rootScope, $timeout);

      function setAutoBind() {
        if (auto) { // Deep watches the model and performs autobind.
          ngCollection.unregisterAutoBind = $rootScope.$watch(function () {
            return _.without(ngCollection, 'UPDATING_FROM_SERVER');
          }, function (newItems, oldItems) {
            if (!ngCollection.UPDATING_FROM_SERVER && newItems !== oldItems) {

              diffArray(angular.copy(oldItems), angular.copy(newItems), {
                addedAt: function (id, item, index) {
                  var newValue = angular.copy(ngCollection[index]);
                  ngCollection.unregisterAutoBind();
                  ngCollection.splice( index, 1 );
                  setAutoBind();
                  ngCollection.save(newValue);
                },
                removedAt: function (id, item, index) {
                  ngCollection.remove(id);
                },
                changedAt: function (id, setDiff, unsetDiff, index) {
                  if (setDiff)
                    ngCollection.save(setDiff);

                  if (unsetDiff)
                    ngCollection.save(unsetDiff, true);
                },
                movedTo: function (id, item, fromIndex, toIndex) {
                  // XXX do we need this?
                }
              });
            }
          }, true);
        }
      }

      /**
       * Fetches the latest data from Meteor and update the data variable.
       */
      Tracker.autorun(function () {
        // When the reactive func gets recomputated we need to stop any previous
        // observeChanges
        Tracker.onInvalidate(function () {
          ngCollection.stop();
        });
        ngCollection.updateCursor(reactiveFunc());
        setAutoBind();
      });

      return ngCollection;
    }
  }]);
<|endoftext|>


<commit_before>/* eslint-env jasmine */

'use strict';

describe('read |', function () {
  var fse = require('fs-extra');
  var helper = require('./support/spec_helper');
  var jetpack = require('..');

  beforeEach(helper.beforeEach);
  afterEach(helper.afterEach);

  it('reads file as a string', function (done) {
    var expectations = function (content) {
      expect(content).toBe('abc');
    };

    fse.outputFileSync('file.txt', 'abc');

    // SYNC
    expectations(jetpack.read('file.txt')); // defaults to 'utf8'
    expectations(jetpack.read('file.txt', 'utf8')); // explicitly specified

    // ASYNC
    jetpack.readAsync('file.txt') // defaults to 'utf8'
    .then(function (content) {
      expectations(content);
      return jetpack.readAsync('file.txt', 'utf8'); // explicitly said
    })
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as a Buffer', function (done) {
    var expectations = function (content) {
      expect(Buffer.isBuffer(content)).toBe(true);
      expect(content.length).toBe(2);
      expect(content[0]).toBe(11);
      expect(content[1]).toBe(22);
    };

    fse.outputFileSync('file.txt', new Buffer([11, 22]));

    // SYNC
    expectations(jetpack.read('file.txt', 'buffer'));

    // ASYNC
    jetpack.readAsync('file.txt', 'buffer')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as a Buffer (deprecated)', function (done) {
    var expectations = function (content) {
      expect(Buffer.isBuffer(content)).toBe(true);
      expect(content.length).toBe(2);
      expect(content[0]).toBe(11);
      expect(content[1]).toBe(22);
    };

    fse.outputFileSync('file.txt', new Buffer([11, 22]));

    // SYNC
    expectations(jetpack.read('file.txt', 'buf'));

    // ASYNC
    jetpack.readAsync('file.txt', 'buf')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as JSON', function (done) {
    var obj = {
      utf8: 'ąćłźż'
    };
    var expectations = function (content) {
      expect(content).toEqual(obj);
    };

    fse.outputFileSync('file.json', JSON.stringify(obj));

    // SYNC
    expectations(jetpack.read('file.json', 'json'));

    // ASYNC
    jetpack.readAsync('file.json', 'json')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('gives nice error message when JSON parsing failed', function (done) {
    var expectations = function (err) {
      expect(err.message).toContain('JSON parsing failed while reading');
    };

    fse.outputFileSync('file.json', '{ "abc: 123 }'); // Malformed JSON

    // SYNC
    try {
      jetpack.read('file.json', 'json');
    } catch (err) {
      expectations(err);
    }

    // ASYNC
    jetpack.readAsync('file.json', 'json')
    .catch(function (err) {
      expectations(err);
      done();
    });
  });

  it('reads file as JSON with Date parsing', function (done) {
    var obj = {
      utf8: 'ąćłźż',
      date: new Date()
    };
    var expectations = function (content) {
      expect(content).toEqual(obj);
    };

    fse.outputFileSync('file.json', JSON.stringify(obj));

    // SYNC
    expectations(jetpack.read('file.json', 'jsonWithDates'));

    // ASYNC
    jetpack.readAsync('file.json', 'jsonWithDates')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it("returns undefined if file doesn't exist", function (done) {
    var expectations = function (content) {
      expect(content).toBe(undefined);
    };

    // SYNC
    expectations(jetpack.read('nonexistent.txt'));

    // ASYNC
    jetpack.readAsync('nonexistent.txt')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('throws if given path is a directory', function (done) {
    var expectations = function (err) {
      expect(err.code).toBe('EISDIR');
    };

    fse.mkdirsSync('dir');

    // SYNC
    try {
      jetpack.read('dir');
      throw new Error('to make sure this code throws');
    } catch (err) {
      expectations(err);
    }

    // ASYNC
    jetpack.readAsync('dir')
    .catch(function (err) {
      expectations(err);
      done();
    });
  });

  it('respects internal CWD of jetpack instance', function (done) {
    var expectations = function (data) {
      expect(data).toBe('abc');
    };

    var jetContext = jetpack.cwd('a');
    fse.outputFileSync('a/file.txt', 'abc');

    // SYNC
    expectations(jetContext.read('file.txt'));

    // ASYNC
    jetContext.readAsync('file.txt')
    .then(function (data) {
      expectations(data);
      done();
    });
  });
});
<commit_msg>Test read() return value more precisely<commit_after>/* eslint-env jasmine */

'use strict';

var Q = require('q');

describe('read |', function () {
  var fse = require('fs-extra');
  var helper = require('./support/spec_helper');
  var jetpack = require('..');

  beforeEach(helper.beforeEach);
  afterEach(helper.afterEach);

  it('reads file as a string', function (done) {
    var expectations = function (content) {
      expect(content).toBe('abc');
    };

    fse.outputFileSync('file.txt', 'abc');

    // SYNC
    expectations(jetpack.read('file.txt')); // defaults to 'utf8'
    expectations(jetpack.read('file.txt', 'utf8')); // explicitly specified

    // ASYNC
    jetpack.readAsync('file.txt') // defaults to 'utf8'
    .then(function (content) {
      expectations(content);
      return jetpack.readAsync('file.txt', 'utf8'); // explicitly said
    })
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as a Buffer', function (done) {
    var expectations = function (content) {
      expect(Buffer.isBuffer(content)).toBe(true);
      expect(content.length).toBe(2);
      expect(content[0]).toBe(11);
      expect(content[1]).toBe(22);
    };

    fse.outputFileSync('file.txt', new Buffer([11, 22]));

    // SYNC
    expectations(jetpack.read('file.txt', 'buffer'));

    // ASYNC
    jetpack.readAsync('file.txt', 'buffer')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as a Buffer (deprecated)', function (done) {
    var expectations = function (content) {
      expect(Buffer.isBuffer(content)).toBe(true);
      expect(content.length).toBe(2);
      expect(content[0]).toBe(11);
      expect(content[1]).toBe(22);
    };

    fse.outputFileSync('file.txt', new Buffer([11, 22]));

    // SYNC
    expectations(jetpack.read('file.txt', 'buf'));

    // ASYNC
    jetpack.readAsync('file.txt', 'buf')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('reads file as JSON', function (done) {
    var obj = {
      utf8: 'ąćłźż'
    };
    var expectations = function (content) {
      expect(content).toEqual(obj);
    };

    fse.outputFileSync('file.json', JSON.stringify(obj));

    // SYNC
    expectations(jetpack.read('file.json', 'json'));

    // ASYNC
    jetpack.readAsync('file.json', 'json')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it('gives nice error message when JSON parsing failed', function (done) {
    var expectations = function (err) {
      expect(err.message).toContain('JSON parsing failed while reading');
    };

    fse.outputFileSync('file.json', '{ "abc: 123 }'); // Malformed JSON

    // SYNC
    try {
      jetpack.read('file.json', 'json');
    } catch (err) {
      expectations(err);
    }

    // ASYNC
    jetpack.readAsync('file.json', 'json')
    .catch(function (err) {
      expectations(err);
      done();
    });
  });

  it('reads file as JSON with Date parsing', function (done) {
    var obj = {
      utf8: 'ąćłźż',
      date: new Date()
    };
    var expectations = function (content) {
      expect(content).toEqual(obj);
    };

    fse.outputFileSync('file.json', JSON.stringify(obj));

    // SYNC
    expectations(jetpack.read('file.json', 'jsonWithDates'));

    // ASYNC
    jetpack.readAsync('file.json', 'jsonWithDates')
    .then(function (content) {
      expectations(content);
      done();
    });
  });

  it("returns undefined if file doesn't exist", function (done) {
    var expectations = function (content) {
      expect(content).toBe(undefined);
    };

    // SYNC
    expectations(jetpack.read('nonexistent.txt'));
    expectations(jetpack.read('nonexistent.txt'), 'json');
    expectations(jetpack.read('nonexistent.txt'), 'buffer');

    // ASYNC
    Q.spread([
      jetpack.readAsync('nonexistent.txt'),
      jetpack.readAsync('nonexistent.txt', 'json'),
      jetpack.readAsync('nonexistent.txt', 'buffer')
    ], function (content1, content2, content3) {
      expectations(content1);
      expectations(content2);
      expectations(content3);
      done();
    });
  });

  it('throws if given path is a directory', function (done) {
    var expectations = function (err) {
      expect(err.code).toBe('EISDIR');
    };

    fse.mkdirsSync('dir');

    // SYNC
    try {
      jetpack.read('dir');
      throw new Error('to make sure this code throws');
    } catch (err) {
      expectations(err);
    }

    // ASYNC
    jetpack.readAsync('dir')
    .catch(function (err) {
      expectations(err);
      done();
    });
  });

  it('respects internal CWD of jetpack instance', function (done) {
    var expectations = function (data) {
      expect(data).toBe('abc');
    };

    var jetContext = jetpack.cwd('a');
    fse.outputFileSync('a/file.txt', 'abc');

    // SYNC
    expectations(jetContext.read('file.txt'));

    // ASYNC
    jetContext.readAsync('file.txt')
    .then(function (data) {
      expectations(data);
      done();
    });
  });
});
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

// Constants
//-----------------------------------------------------------------------------

const Cc = Components.classes;
const Ci = Components.interfaces;

var gFindBar;

var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);                       /*@explore*/
var hiddenWindow = appShellService.hiddenDOMWindow;

// Implementation
//-----------------------------------------------------------------------------

var Console =
{
    modules: [],

    onLoad: function()
    {
        var args = window.arguments[0];
        /*
        hiddenWindow.dump("traceConsole window.arguments.length:"+window.arguments.length+"\n");
        hiddenWindow.dump("traceConsole window.arguments[0]"+window.arguments[0]+"\n");
        for (var i in args)
        {
            hiddenWindow.dump("args["+i+"]="+args[i]+"\n");
        }
        */
        FBL = args.FBL;
        Firebug = args.Firebug;
        this.prefDomain = args.prefDomain;
        this.activeModule = args.traceModule;

        window.title = "Firebug Trace Console: "+this.prefDomain;

        var consoleFrame = document.getElementById("consoleFrame");
        this.consoleNode = consoleFrame.contentDocument.getElementById("panelNode-traceConsole");

        // Associate Console with the module object.
        this.activeModule.onLoadConsole(window, this.consoleNode);
        this.registerModule(this.activeModule);

        // Find toolbar
        gFindBar = document.getElementById("FindToolbar");
    },

    onUnload: function()
    {
        for (var i=0; i<this.modules.length; ++i)
            this.modules[i].onUnloadConsole();
    },

    registerModule: function(module)
    {
        module.consoleRoot = this.activeModule.consoleRoot;
        this.modules.push(module);
    },

    unregisterModule: function(module)
    {
        for (var i=0; i<this.modules.length; ++i) {
            if (this.modules[i] == item) {
                this.modules.splice(i, 1);
                break;
            }
        }
    },

    // Commands
    onClearConsole: function()
    {
        var tbody = this.consoleNode.firstChild.firstChild;
        while (tbody.firstChild)
            tbody.removeChild(tbody.lastChild);
    },

    onSeparateConsole: function()
    {
        this.activeModule.dumpSeparator();
    },

    onSaveToFile: function()
    {
    },

    onRestartFirefox: function()
    {
        Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).
            quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
    },

    onExitFirefox: function()
    {
        goQuitApplication();
    }
}

//-----------------------------------------------------------------------------
<commit_msg>move appshell + hiddenWindow to comment block<commit_after>/* See license.txt for terms of usage */

// Constants
//-----------------------------------------------------------------------------

const Cc = Components.classes;
const Ci = Components.interfaces;

var gFindBar;

// Implementation
//-----------------------------------------------------------------------------

var Console =
{
    modules: [],

    onLoad: function()
    {
        var args = window.arguments[0];
        /*
        var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);
        var hiddenWindow = appShellService.hiddenDOMWindow;
        hiddenWindow.dump("traceConsole window.arguments.length:"+window.arguments.length+"\n");
        hiddenWindow.dump("traceConsole window.arguments[0]"+window.arguments[0]+"\n");
        for (var i in args)
        {
            hiddenWindow.dump("args["+i+"]="+args[i]+"\n");
        }
        */
        FBL = args.FBL;
        Firebug = args.Firebug;
        this.prefDomain = args.prefDomain;
        this.activeModule = args.traceModule;

        window.title = "Firebug Trace Console: "+this.prefDomain;

        var consoleFrame = document.getElementById("consoleFrame");
        this.consoleNode = consoleFrame.contentDocument.getElementById("panelNode-traceConsole");

        // Associate Console with the module object.
        this.activeModule.onLoadConsole(window, this.consoleNode);
        this.registerModule(this.activeModule);

        // Find toolbar
        gFindBar = document.getElementById("FindToolbar");
    },

    onUnload: function()
    {
        for (var i=0; i<this.modules.length; ++i)
            this.modules[i].onUnloadConsole();
    },

    registerModule: function(module)
    {
        module.consoleRoot = this.activeModule.consoleRoot;
        this.modules.push(module);
    },

    unregisterModule: function(module)
    {
        for (var i=0; i<this.modules.length; ++i) {
            if (this.modules[i] == item) {
                this.modules.splice(i, 1);
                break;
            }
        }
    },

    // Commands
    onClearConsole: function()
    {
        var tbody = this.consoleNode.firstChild.firstChild;
        while (tbody.firstChild)
            tbody.removeChild(tbody.lastChild);
    },

    onSeparateConsole: function()
    {
        this.activeModule.dumpSeparator();
    },

    onSaveToFile: function()
    {
    },

    onRestartFirefox: function()
    {
        Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).
            quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
    },

    onExitFirefox: function()
    {
        goQuitApplication();
    }
}

//-----------------------------------------------------------------------------
<|endoftext|>


<commit_before>define(["dojo/on", "dojo/has", "put-selector/put", "dojo/_base/sniff"], function(on, has, put){

return function(column, editor, editOn){
	// summary:
	//		Adds editing capability to a column's cells.

	var originalRenderCell = column.renderCell || function(object, value, td){
		if(value != null){
			td.appendChild(document.createTextNode(value));
		}
	};
	// accept arguments as parameters to Editor function, or from column def
	column.editor = editor = editor || column.editor;
	column.editOn = editOn = editOn || column.editOn;

	var grid;
	function onchange(event){
		var target = event.target;
		if("lastValue" in target && target.className.indexOf("dgrid-input") > -1){
			target.lastValue = setProperty(target.parentNode, target.lastValue,
				target[target.type == "checkbox" || target.type == "radio"  ? "checked" : "value"]);
		}
	}
	var renderWidget = typeof editor == "string" ?
		function(value, cell, object, onblur){
			var input;
			// when editor is a string, we use a common <input> as the editor
			input = cell.input || (cell.input = put(cell, "input[type=" + editor + "].dgrid-input", {
				name: column.field || this.id + "-selection",
				tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex
			}));
			input.value = value || "";
			input.checked = value;
			input.lastValue = value;
			if(!grid._hasInputListener){
				// register one listener at the top level that receives events delegated
				grid._hasInputListener = true;
				grid.on("change", onchange);
			}
			if(has("ie") < 9){
				// IE<9 doesn't fire change events for all the right things,
				// and it doesn't bubble, double fail.
				if(cell.input.type == "radio" || cell.input.type == "checkbox"){
					// listen for clicks because IE doesn't fire change events properly for checks/radios
					on(input, "click", onchange);
				}else{
					on(input, "change", onchange);
				}
			}
			if(onblur){
				input.focus();
				var signal = on(input, "blur", function(){
					// unhook and delete the input now
					signal.remove();
					put(input, "!");
					cell.input = null;
					onblur(input.lastValue);
				});
			}
		} :
		function(data, cell, object, onblur){
			// using a widget as the editor.
			var widget = new editor(column.widgetArgs || {},
				cell.appendChild(document.createElement("div")));
			widget.set("value", data);
			widget.watch("value", function(key, oldValue, value){
				data = setProperty(cell, data, value);
			});
			if(onblur){
				widget.focus();
				widget.connect(widget, "onBlur", function(){
					setTimeout(function(){
						// we have to wait on this for the widget will throw errors
						// about keydown events that happen right after blur
						widget.destroyRecursive();
					}, 0);
					onblur(data);
				});
			}
		};
	function setProperty(cellElement, oldValue, value){
		if(oldValue != value){
			var cell = grid.cell(cellElement);
			var row = cell.row;
			var column = cell.column;
			if(column.field && row){
				// first try to keep the type the same if possible
				if(typeof oldValue == 'number'){
					value = isNaN(value) ? value : parseFloat(value);
				}else if(typeof oldValue == 'boolean'){
					value = value == 'true' ? true : value == 'false' ? false : value;
				}else if(oldValue instanceof Date){
					var asDate = new Date(value);
					value = isNaN(asDate.getTime()) ? value : asDate;
				}
				if(on.emit(cellElement, "datachange", {oldValue: oldValue, value: value, bubbles: true, cancelable: true})){
					var object = row.data;
					var dirty = grid.dirty[row.id] || (grid.dirty[row.id] = {});
					dirty[column.field] = object[column.field] = value;
					if(column.autoSave){
						grid.save();
					}
				}else{
					// else keep the value the same
					return oldValue;
				}
			}
			if(column.selector){
				if(editor == "radio" || column.selector == "single"){
					grid.clearSelection();
				}
				if(row){
					suppressSelect = true;
					grid.select(row.id, null, value);
					suppressSelect = false;
				}else{
					// select all
					grid[value ? "selectAll" : "clearSelection"]();
				}
			}
		}
		return value;
	}
	var suppressSelect;
	column.renderCell = function(object, value, cell, options){
		if(!grid){
			grid = column.grid;
			if(column.selector){
				grid.on("select,deselect", function(event){
					if(!suppressSelect && event.currentTarget === event.target){
						var cell = grid.cell(event.row.id, column.id).element;
						renderWidget(event.type == "select", cell.contents || cell, object);
					}
				});
			}
		}
		if(column.selector){
			var row = object && grid.row(object);
			value = row && grid.selection[row.id];
		}
		if(column.editOn){ // TODO: Make this use event delegation, particularly now that we can do event delegation with focus events
			// if we are dealing with IE<8, the cell element is the padding cell, need to go to parent
			on(cell.tagName == "TD" ? cell : cell.parentNode,
					column.editOn, function(){
				if(!column.canEdit || column.canEdit(object, value)){
					cell.innerHTML = "";
					renderWidget(value, cell, object, function(newData){
						originalRenderCell(object, value = newData, cell);
					});
				}
			});
			originalRenderCell(object, value, cell, options);
		}else{
			renderWidget(value, cell, object);
		}
	}
	if(!column.label){
		column.renderHeaderCell = function(th){
			column.renderCell({}, null, th);
		}
	}
	return column;
};
});
<commit_msg>Fix Selectors' sync with grid selection (I broke it yesterday)<commit_after>define(["dojo/on", "dojo/has", "put-selector/put", "dojo/_base/sniff"], function(on, has, put){

return function(column, editor, editOn){
	// summary:
	//		Adds editing capability to a column's cells.

	var originalRenderCell = column.renderCell || function(object, value, td){
		if(value != null){
			td.appendChild(document.createTextNode(value));
		}
	};
	// accept arguments as parameters to Editor function, or from column def
	column.editor = editor = editor || column.editor;
	column.editOn = editOn = editOn || column.editOn;

	var grid;
	function onchange(event){
		var target = event.target;
		if("lastValue" in target && target.className.indexOf("dgrid-input") > -1){
			target.lastValue = setProperty(target.parentNode, target.lastValue,
				target[target.type == "checkbox" || target.type == "radio"  ? "checked" : "value"]);
		}
	}
	var renderWidget = typeof editor == "string" ?
		function(value, cell, object, onblur){
			var input;
			// when editor is a string, we use a common <input> as the editor
			input = cell.input || (cell.input = put(cell, "input[type=" + editor + "].dgrid-input", {
				name: column.field || this.id + "-selection",
				tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex
			}));
			input.value = value || "";
			input.checked = value;
			input.lastValue = value;
			if(!grid._hasInputListener){
				// register one listener at the top level that receives events delegated
				grid._hasInputListener = true;
				grid.on("change", onchange);
			}
			if(has("ie") < 9){
				// IE<9 doesn't fire change events for all the right things,
				// and it doesn't bubble, double fail.
				if(cell.input.type == "radio" || cell.input.type == "checkbox"){
					// listen for clicks because IE doesn't fire change events properly for checks/radios
					on(input, "click", onchange);
				}else{
					on(input, "change", onchange);
				}
			}
			if(onblur){
				input.focus();
				var signal = on(input, "blur", function(){
					// unhook and delete the input now
					signal.remove();
					put(input, "!");
					cell.input = null;
					onblur(input.lastValue);
				});
			}
		} :
		function(data, cell, object, onblur){
			// using a widget as the editor.
			var widget = new editor(column.widgetArgs || {},
				cell.appendChild(document.createElement("div")));
			widget.set("value", data);
			widget.watch("value", function(key, oldValue, value){
				data = setProperty(cell, data, value);
			});
			if(onblur){
				widget.focus();
				widget.connect(widget, "onBlur", function(){
					setTimeout(function(){
						// we have to wait on this for the widget will throw errors
						// about keydown events that happen right after blur
						widget.destroyRecursive();
					}, 0);
					onblur(data);
				});
			}
		};
	function setProperty(cellElement, oldValue, value){
		if(oldValue != value){
			var cell = grid.cell(cellElement);
			var row = cell.row;
			var column = cell.column;
			if(column.field && row){
				// first try to keep the type the same if possible
				if(typeof oldValue == 'number'){
					value = isNaN(value) ? value : parseFloat(value);
				}else if(typeof oldValue == 'boolean'){
					value = value == 'true' ? true : value == 'false' ? false : value;
				}else if(oldValue instanceof Date){
					var asDate = new Date(value);
					value = isNaN(asDate.getTime()) ? value : asDate;
				}
				if(on.emit(cellElement, "datachange", {oldValue: oldValue, value: value, bubbles: true, cancelable: true})){
					var object = row.data;
					var dirty = grid.dirty[row.id] || (grid.dirty[row.id] = {});
					dirty[column.field] = object[column.field] = value;
					if(column.autoSave){
						grid.save();
					}
				}else{
					// else keep the value the same
					return oldValue;
				}
			}
			if(column.selector){
				if(editor == "radio" || column.selector == "single"){
					grid.clearSelection();
				}
				if(row){
					suppressSelect = true;
					grid.select(row.id, null, value);
					suppressSelect = false;
				}else{
					// select all
					grid[value ? "selectAll" : "clearSelection"]();
				}
			}
		}
		return value;
	}
	var suppressSelect;
	column.renderCell = function(object, value, cell, options){
		if(!grid){
			grid = column.grid;
			if(column.selector){
				grid.on("select,deselect", function(event){
					// Selector only cares about selection events from rows.
					// We can't just use event delegation here, because we *don't* want
					// to capture events bubbled from deeper (e.g. text editors).
					if(!suppressSelect && /\bdgrid-row\b/.test(event.target.className)){
						var cell = grid.cell(event.row.id, column.id).element;
						renderWidget(event.type == "select", cell.contents || cell, object);
					}
				});
			}
		}
		if(column.selector){
			var row = object && grid.row(object);
			value = row && grid.selection[row.id];
		}
		if(column.editOn){ // TODO: Make this use event delegation, particularly now that we can do event delegation with focus events
			// if we are dealing with IE<8, the cell element is the padding cell, need to go to parent
			on(cell.tagName == "TD" ? cell : cell.parentNode,
					column.editOn, function(){
				if(!column.canEdit || column.canEdit(object, value)){
					cell.innerHTML = "";
					renderWidget(value, cell, object, function(newData){
						originalRenderCell(object, value = newData, cell);
					});
				}
			});
			originalRenderCell(object, value, cell, options);
		}else{
			renderWidget(value, cell, object);
		}
	}
	if(!column.label){
		column.renderHeaderCell = function(th){
			column.renderCell({}, null, th);
		}
	}
	return column;
};
});
<|endoftext|>


<commit_before>/*!
 * level-revision <https://github.com/doowb/level-revision>
 *
 * Copyright (c) 2015 Brian Woodward.
 * Licensed under the MIT license.
 */

'use strict';

var sublevel = require('level-sublevel/bytewise');
var defaults = require('levelup-defaults');
var bytewise = require('bytewise');

var timestamps = require('./lib/timestamps');

module.exports = LevelRevision;

function LevelRevision (db, options) {
  var self = this;
  if (!(this instanceof LevelRevision)) {
    return new LevelRevision(db, options);
  }
  if (!options) {
    options = {};
  }

  db = defaults(db, {
    keyEncoding: bytewise,
    valueEncoding: options.valueEncoding || 'json'
  });

  db = sublevel(db);

  this._db = db;
  this._revisions = this._db.sublevel('revisions');
};

/**
 * Validate the that given revision is one that can be updated.
 *
 * @param  {String|Array} `key` Key of the row to look for.
 * @param  {Object} `value` New value to compare with.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback function that takes `err`, `valid`, and `conflict`
 * @async
 */

LevelRevision.prototype.validateRevision = function(key, value, options, cb) {
  var self = this;
  this._db.get(key, function (err, current) {
    if (err && err.notFound) {
      return cb(null, true);
    }
    if (err) {
      return cb(err);
    }
    if (current.rev === value.rev) {
      return cb(null, true);
    }

    self.getLast(key, function (err, revision) {
      if (err) return cb(err);
      if (revision.rev > (value.rev || 0)) {
        cb(null, false, revision);
      }
    });
  });
};

/**
 * Get the last (most recent) revision for the given key.
 *
 * @param  {String|Array} `key` Key to search for.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback that takes `err` and `results`
 * @public
 * @async
 */

LevelRevision.prototype.getLast = function(key, options, cb) {
  var self = this;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  options = options || {};
  var results = null;
  self._revisions.createReadStream({reverse: true, limit: 1, gte: key.concat(null), lt: key.concat(undefined)})
    .on('data', function (revision) {
      results = revision;
    })
    .on('end', function () {
      if (!results) {
        var err = new Error('NotFoundError');
        err.notFound = true;
        return cb(err);
      }
      cb(null, results.value);
    });
};

/**
 * Add a value to the database. Timestamp values will automatically be applied.
 * When updating a value, the current `rev` property is required. If a `rev` property is supplied and
 * it's not the latest, a `RevisionConflict` error will be returned along with the latest version.
 * This allows handling the conflict.
 *
 * ```js
 * // put new value
 * db.put(['item', '1'], { id: '1', description: 'Item 1'}, function (err, results) {
 *   //=> `err` Error when something went wrong
 *   //=> `results` = { id: '1', description: 'Item 1', createdAt: 1234567, modifiedAt: 1234567, rev: 1 };
 * });
 *
 * // update existing value
 * db.put(['item', '1'], { id: '1', description: 'Item 1 details.', rev: 1 }, function (err, results) {
 *   //=> `err` Error when something went wrong
 *   //=> `results` = { id: '1', description: 'Item 1 details.', createdAt: 1234567, modifiedAt: 1234789, rev: 2 };
 * });
 *
 * // update with conflicting value
 * db.put(['item', '1'], { id: '1', description: 'Description of item 1.', rev: 1 }, function (err, results) {
 *   //=> `err` = RevisionConflict { message: "Conflict updating revision 1. Current revision is at 2" };
 *   //=> `results = { id: '1', description: 'Item 1 details.', createdAt: 1234567, modifiedAt: 1234789, rev: 2 };
 * });
 * ```
 *
 * @param  {String|Array} `key` String or Array key to represent the value.
 * @param  {Object} `value` JSON object to store.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback function containing an `err` and `results` property.
 * @public
 * @async
 */

LevelRevision.prototype.put = function(key, value, options, cb) {
  var self = this;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  options = options || {};

  this.validateRevision(key, value, options, function (err, valid, conflict) {
    if (err) return cb(err);
    if (!valid) {
      return cb(new Error([
          'RevisionConflict: Conflict updating revision',
          value.rev + '.',
          'Current revision is at',
          conflict.rev + '.'
        ].join(' ')),
      conflict);
    }

    timestamps(self._db, key, value, function (err, value) {
      if (err) return cb(err);
      value.rev = (value.rev || 0) + 1;
      var rows = [
        { type: 'put', key: key, value: value },
        { type: 'put', key: key.concat(value.rev), value: value, prefix: self._revisions }
      ];
      self._db.batch(rows, function (err) {
        if (err) return cb(err);
        self._db.get(key, cb);
      });
    });
  });
};

LevelRevision.prototype.get = function(key, options, cb) {
  return this._db.get.apply(this._db, arguments);
};

LevelRevision.prototype.del = function(key, options, cb) {
  return this._db.del.apply(this._db, arguments);
};

LevelRevision.prototype.batch = function(rows, options, cb) {
  return this._db.batch.apply(this._db, arguments);
};

LevelRevision.prototype.readStream =
LevelRevision.prototype.createReadStream = function(options) {
  return this._db.createReadStream.apply(this._db, arguments);
};

LevelRevision.prototype.keyStream =
LevelRevision.prototype.createKeyStream = function(options) {
  return this._db.createKeyStream.apply(this._db, arguments);
};

LevelRevision.prototype.valueStream =
LevelRevision.prototype.createValueStream = function(options) {
  return this._db.createValueStream.apply(this._db, arguments);
};

LevelRevision.prototype.writeStream =
LevelRevision.prototype.createWriteStream = function(options) {
  return this._db.createWriteStream.apply(this._db, arguments);
};

LevelRevision.prototype.close = function(cb) {
  this._db.close(cb);
};

LevelRevision.prototype.makeRevision = function(options) {
  return 1;
};
<commit_msg>checking that keys are arrays<commit_after>/*!
 * level-revision <https://github.com/doowb/level-revision>
 *
 * Copyright (c) 2015 Brian Woodward.
 * Licensed under the MIT license.
 */

'use strict';

var sublevel = require('level-sublevel/bytewise');
var defaults = require('levelup-defaults');
var bytewise = require('bytewise');

var timestamps = require('./lib/timestamps');

module.exports = LevelRevision;

function LevelRevision (db, options) {
  var self = this;
  if (!(this instanceof LevelRevision)) {
    return new LevelRevision(db, options);
  }
  if (!options) {
    options = {};
  }

  db = defaults(db, {
    keyEncoding: bytewise,
    valueEncoding: options.valueEncoding || 'json'
  });

  db = sublevel(db);

  this._db = db;
  this._revisions = this._db.sublevel('revisions');
};

/**
 * Validate the that given revision is one that can be updated.
 *
 * @param  {String|Array} `key` Key of the row to look for.
 * @param  {Object} `value` New value to compare with.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback function that takes `err`, `valid`, and `conflict`
 * @async
 */

LevelRevision.prototype.validateRevision = function(key, value, options, cb) {
  var self = this;
  key = Array.isArray(key) ? key : [key];

  this._db.get(key, function (err, current) {
    if (err && err.notFound) {
      return cb(null, true);
    }
    if (err) {
      return cb(err);
    }
    if (current.rev === value.rev) {
      return cb(null, true);
    }

    self.getLast(key, function (err, revision) {
      if (err) return cb(err);
      if (revision.rev > (value.rev || 0)) {
        cb(null, false, revision);
      }
    });
  });
};

/**
 * Get the last (most recent) revision for the given key.
 *
 * @param  {String|Array} `key` Key to search for.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback that takes `err` and `results`
 * @public
 * @async
 */

LevelRevision.prototype.getLast = function(key, options, cb) {
  var self = this;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  options = options || {};
  key = Array.isArray(key) ? key : [key];

  var results = null;
  self._revisions.createReadStream({reverse: true, limit: 1, gte: key.concat(null), lt: key.concat(undefined)})
    .on('data', function (revision) {
      results = revision;
    })
    .on('end', function () {
      if (!results) {
        var err = new Error('NotFoundError');
        err.notFound = true;
        return cb(err);
      }
      cb(null, results.value);
    });
};

/**
 * Add a value to the database. Timestamp values will automatically be applied.
 * When updating a value, the current `rev` property is required. If a `rev` property is supplied and
 * it's not the latest, a `RevisionConflict` error will be returned along with the latest version.
 * This allows handling the conflict.
 *
 * ```js
 * // put new value
 * db.put(['item', '1'], { id: '1', description: 'Item 1'}, function (err, results) {
 *   //=> `err` Error when something went wrong
 *   //=> `results` = { id: '1', description: 'Item 1', createdAt: 1234567, modifiedAt: 1234567, rev: 1 };
 * });
 *
 * // update existing value
 * db.put(['item', '1'], { id: '1', description: 'Item 1 details.', rev: 1 }, function (err, results) {
 *   //=> `err` Error when something went wrong
 *   //=> `results` = { id: '1', description: 'Item 1 details.', createdAt: 1234567, modifiedAt: 1234789, rev: 2 };
 * });
 *
 * // update with conflicting value
 * db.put(['item', '1'], { id: '1', description: 'Description of item 1.', rev: 1 }, function (err, results) {
 *   //=> `err` = RevisionConflict { message: "Conflict updating revision 1. Current revision is at 2" };
 *   //=> `results = { id: '1', description: 'Item 1 details.', createdAt: 1234567, modifiedAt: 1234789, rev: 2 };
 * });
 * ```
 *
 * @param  {String|Array} `key` String or Array key to represent the value.
 * @param  {Object} `value` JSON object to store.
 * @param  {Object} `options` Additional options
 * @param  {Function} `cb` Callback function containing an `err` and `results` property.
 * @public
 * @async
 */

LevelRevision.prototype.put = function(key, value, options, cb) {
  var self = this;
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }
  options = options || {};
  key = Array.isArray(key) ? key : [key];

  this.validateRevision(key, value, options, function (err, valid, conflict) {
    if (err) return cb(err);
    if (!valid) {
      return cb(new Error([
          'RevisionConflict: Conflict updating revision',
          value.rev + '.',
          'Current revision is at',
          conflict.rev + '.'
        ].join(' ')),
      conflict);
    }

    timestamps(self._db, key, value, function (err, value) {
      if (err) return cb(err);
      value.rev = (value.rev || 0) + 1;
      var rows = [
        { type: 'put', key: key, value: value },
        { type: 'put', key: key.concat(value.rev), value: value, prefix: self._revisions }
      ];
      self._db.batch(rows, function (err) {
        if (err) return cb(err);
        self._db.get(key, cb);
      });
    });
  });
};

LevelRevision.prototype.get = function(key, options, cb) {
  key = Array.isArray(key) ? key : [key];
  return this._db.get.apply(this._db, arguments);
};

LevelRevision.prototype.del = function(key, options, cb) {
  key = Array.isArray(key) ? key : [key];
  return this._db.del.apply(this._db, arguments);
};

LevelRevision.prototype.batch = function(rows, options, cb) {
  return this._db.batch.apply(this._db, arguments);
};

LevelRevision.prototype.readStream =
LevelRevision.prototype.createReadStream = function(options) {
  return this._db.createReadStream.apply(this._db, arguments);
};

LevelRevision.prototype.keyStream =
LevelRevision.prototype.createKeyStream = function(options) {
  return this._db.createKeyStream.apply(this._db, arguments);
};

LevelRevision.prototype.valueStream =
LevelRevision.prototype.createValueStream = function(options) {
  return this._db.createValueStream.apply(this._db, arguments);
};

LevelRevision.prototype.writeStream =
LevelRevision.prototype.createWriteStream = function(options) {
  return this._db.createWriteStream.apply(this._db, arguments);
};

LevelRevision.prototype.close = function(cb) {
  this._db.close(cb);
};

LevelRevision.prototype.makeRevision = function(options) {
  return 1;
};
<|endoftext|>