<commit_before>(function(jQuery) {
"use strict";

var $ = jQuery;

var canvas = Echo.Control.manifest("Echo.Canvas");

if (Echo.Control.isDefined(canvas)) return;

/**
 * @class Echo.Canvas
 * Class which implements Canvas mechanics on the client side.
 * The instance of this class is created for each Canvas found on the page by
 * the Echo.Loader. The instance of the class can also be created manually in
 * case the Canvas data already exists on the page.
 *
 * @package environment.pack.js
 *
 * @extends Echo.Control
 *
 * @constructor
 * Canvas object constructor to initialize the Echo.Canvas instance
 *
 * @param {Object} config
 * Configuration options
 */

/** @hide @method getRelativeTime */
/** @hide @echo_label today */
/** @hide @echo_label yesterday */
/** @hide @echo_label lastWeek */
/** @hide @echo_label lastMonth */
/** @hide @echo_label secondAgo */
/** @hide @echo_label secondsAgo */
/** @hide @echo_label minuteAgo */
/** @hide @echo_label minutesAgo */
/** @hide @echo_label hourAgo */
/** @hide @echo_label hoursAgo */
/** @hide @echo_label dayAgo */
/** @hide @echo_label daysAgo */
/** @hide @echo_label weekAgo */
/** @hide @echo_label weeksAgo */
/** @hide @echo_label monthAgo */
/** @hide @echo_label monthsAgo */
/** @hide @echo_label loading */
/** @hide @echo_label retrying */
/** @hide @echo_label error_busy */
/** @hide @echo_label error_timeout */
/** @hide @echo_label error_waiting */
/** @hide @echo_label error_view_limit */
/** @hide @echo_label error_view_update_capacity_exceeded */
/** @hide @echo_label error_result_too_large */
/** @hide @echo_label error_wrong_query */
/** @hide @echo_label error_incorrect_appkey */
/** @hide @echo_label error_internal_error */
/** @hide @echo_label error_quota_exceeded */
/** @hide @echo_label error_incorrect_user_id */
/** @hide @echo_label error_unknown */

canvas.init = function() {
	var ids, cssClass;
	var self = this, target = this.config.get("target");
	// parent init function takes care about init finalization (rendering
	// and the "onReady" event firing)
	var parent = $.proxy(this.parent, this);

	// check if the canvas was already initialized
	if (target.data("echo-canvas-initialized")) {
		this._error({
			"args": {"target": target},
			"code": "canvas_already_initialized"
		});
		return;
	}

	// define initialized state for the canvas
	// to prevent multiple initialization of the same canvas
	target.data("echo-canvas-initialized", true);

	// extending Canvas config with the parameters defined in the target
	var overrides = this._getOverrides(target, ["id", "useSecureAPI", "mode"]);
	if (!$.isEmptyObject(overrides)) {
		this.config.extend(overrides);
	}

	// exit if no "id" is defined for the canvas,
	// skip this validation in case the "data" is defined explicitly in the config
	if (!this._isManuallyConfigured() && !this.config.get("id")) {
		this._error({
			"args": {"target": target},
			"code": "invalid_canvas_config"
		});
		return;
	}

	// apply our canvas id as a CSS class if we aren't manually configured
	if (this.config.get("id")) {
		ids = this._getIds().normalized;
		// adding a primary canvas ID and unique page identifier
		// as a CSS class if provided
		cssClass = Echo.Utils.foldl("", ["main", "unique"], function(type, acc) {
			return (acc += ids[type] ? self.get("cssPrefix") + ids[type] + " " : "");
		});
		target.addClass(cssClass);
	}

	// fetch canvas config from remote storage
	this._fetchConfig(function() {
		if (self.get("data.backplane")) Backplane.init(this.get("data.backplane"));
		self._loadAppResources(parent);
	});
};

canvas.config = {
	/**
	 * @cfg {String} [id]
	 * Unique ID of the Canvas, used by the Echo.Canvas instance
	 * to retrieve the data from the Canvases data storage.
	 */
	"id": "",

	/**
	 * @cfg {Object} [data]
	 * Object which contains the Canvas data in the format
	 * used to store the Canvas config in the Canvas storage.
	 */
	"data": {},

	/**
	 * @cfg {String} target(required)
	 * Specifies the DOM element where the control will be displayed.
	 *
	 * Note: if only the "target" config parameter is defined, the target DOM element
	 * should contain the following HTML attribute:
	 *
	 * + "data-canvas-id" with the unique Canvas ID which should be initialized
	 *
	 * The values of the HTML parameters override the "id" parameter value
	 * (respectively) passed via the Canvas config.
	 */

	/**
	 * @cfg {Object} [overrides]
	 * Object which contains the overrides applied for this Canvas on the page
	 * via Echo.Loader.override function call.
	 */
	"overrides": {},

	/**
	 * @cfg {String} [mode]
	 * This parameter specifies the mode in which Canvas works.
	 * There are two possible values for this parameter:
	 *
	 * + "dev" - in this case the Canvas works with the development configuration storage
	 * + "prod" - in this case the Canvas works with the production configuration storage
	 *
	 * More information about defference between production and development configuration
	 * storages can be found in the ["How to deploy an App using a Canvas guide"](#!/guide/how_to_deploy_an_app_using_a_canvas)
	 *
	 * The value of this parameter can be overridden by specifying the "data-canvas-mode"
	 * target DOM element attribute.
	 * More information about HTML attributes of the target DOM element can be found [here](#!/guide/how_to_deploy_an_app_using_a_canvas)
	 */
	"mode": "dev"
};

canvas.vars = {
	"apps": []
};

canvas.labels = {
	/**
	 * @echo_label
	 */
	"error_no_apps": "No applications defined for this canvas",
	/**
	 * @echo_label
	 */
	"error_no_config": "Unable to retrieve Canvas config",
	/**
	 * @echo_label
	 */
	"error_no_suitable_app_class": "Unable to init an app, no suitable JS class found",
	/**
	 * @echo_label
	 */
	"error_unable_to_retrieve_app_config": "Unable to retrieve Canvas config from the storage",
	/**
	 * @echo_label
	 */
	"error_incomplete_app_config": "Unable to init an app, config is incomplete",
	/**
	 * @echo_label
	 */
	"error_canvas_already_initialized": "Canvas has been initialized already",
	/**
	 * @echo_label
	 */
	"error_invalid_canvas_config": "Canvas with invalid configuration found"
};

/**
 * @echo_template
 */
canvas.templates.main =
	'

';

/**
 * @echo_template
 */
canvas.templates.app =
	'
' +
		'
{data:caption}
' +
		'

' +
	'
';

canvas.destroy = function() {
	$.map(this.get("apps"), $.proxy(this._destroyApp, this));
	this.config.get("target").data("echo-canvas-initialized", false);
};

/**
 * @echo_renderer
 */
canvas.renderers.container = function(element) {
	var self = this;
	$.map(this.get("data.apps"), function(app, id) {
		self._initApp(app, element, id);
	});
	return element;
};

canvas.methods._initApp = function(app, element, id) {
	var self = this;
	var Application = Echo.Utils.getComponent(app.component);
	if (!Application) {
		this._error({
			"args": {"app": app},
			"code": "no_suitable_app_class"
		});
		return;
	}

	app.id = app.id || id;  // define app position in array as id if not specified
	app.config = app.config || {};
	app.config.canvasId = this.config.get('id');

	var view = this.view.fork({
		"renderer": null,
		"renderers": {
			"appHeader": function(element) {
				// show|hide app header depending on the caption existance
				return element[app.caption ? "show" : "hide"]();
			},
			"appBody": function(element) {
				var className = self.get("cssPrefix") + "appId-" + app.id;
				return element.addClass(className);
			}
		}
	});
	element.append(view.render({
		"data": app,
		"template": this.templates.app
	}));

	app.config.target = view.get("appBody");

	var overrides = this.config.get("overrides")[app.id];
	var config = overrides
		? $.extend(true, app.config, overrides)
		: app.config;
	this.apps.push(new Application(config));
};

canvas.methods._destroyApp = function(app) {
	if (app && $.isFunction(app.destroy)) app.destroy();
};

canvas.methods._isManuallyConfigured = function() {
	return !$.isEmptyObject(this.get("data"));
};

canvas.methods._getAppScriptURL = function(config) {
	if (!config.scripts) return config.script;
	var isSecure, script = {
		"dev": config.scripts.dev || config.scripts.prod,
		"prod": config.scripts.prod || config.scripts.dev
	}[Echo.Loader.isDebug() ? "dev" : "prod"];
	if (typeof script === "string") return script;
	isSecure = /^https/.test(window.location.protocol);
	return script[isSecure ? "secure" : "regular"];
};

canvas.methods._loadAppResources = function(callback) {
	var self = this, resources = [], isManual = this._isManuallyConfigured();
	$.map(this.get("data.apps"), function(app) {
		var script = self._getAppScriptURL(app);
		if (!app.component || !script || !(isManual || app.id)) {
			self._error({
				"args": {"app": app},
				"code": "incomplete_app_config"
			});
			return;
		}
		resources.push({
			"url": script,
			"loaded": function() {
				return Echo.Control.isDefined(app.component);
			}
		});
	});
	Echo.Loader.download(resources, callback);
};

canvas.methods._getOverrides = function(target, spec) {
	return Echo.Utils.foldl({}, spec || [], function(item, acc) {
		// We should convert spec item to lower case because of jQuery
		// HTML5 data attributes implementation http://api.jquery.com/data/#data-html5
		// Since we have config keys in camel case representation like "useSecureAPI",
		// we should follow to these rules.
		var key = "canvas-" + item.toLowerCase();
		var value = target.data(key);
		if (typeof value !== "undefined") {
			acc[item] = value;
		}
	});
};

canvas.methods._error = function(args) {
	args.message = args.message || this.labels.get("error_" + args.code);

	/**
	 * @echo_event Echo.Canvas.onError
	 * Event which is triggered in case of errors such as invalid configuration,
	 * problems fetching the data from the server side, etc.
	 *
	 * @param {String} topic
	 * Name of the event produced.
	 *
	 * @param {Object} data
	 * Object which contains debug information regarding the error.
	 */
	Echo.Events.publish({
		"topic": "Echo.Canvas.onError",
		"data": args
	});

	Echo.Utils.log($.extend(args, {"type": "error", "component": "Echo.Canvas"}));
	if (args.renderError) {
		this.showMessage({
			"type": "error",
			"message": args.message
		});
	}
};

canvas.methods._getIds = function() {
	var id = this.config.get("id");
	var parts = id.split("#");
	var normalize = function(s) { return s.replace(/[^a-z\d]/ig, "-"); };
	return {
		"unique": id,
		"main": parts[0],
		"normalized": {
			"unique": normalize(id),
			"main": normalize(parts[0])
		}
	};
};

canvas.methods._fetchConfig = function(callback) {
	var self = this, target = this.config.get("target");
	var isManual = this._isManuallyConfigured();

	// no need to perform server side request in case
	// we already have all the data on the client side
	if (isManual) {
		callback.call(this);
		return;
	}
	(new Echo.API.Request({
		"apiBaseURL": Echo.Loader.config.storageURL[this.config.get("mode")],
		"secure": this.config.get("useSecureAPI"),
		// taking care of the Canvas unique identifier on the page,
		// specified as "#XXX" in the Canvas ID. We don't need to send this
		// unique page identifier, we send only the primary Canvas ID.
		"endpoint": this._getIds().main,
		"data": this.config.get("mode") === "dev" ? {"_": Math.random()} : {},
		"onData": function(config) {
			if (!config || !config.apps || !config.apps.length) {
				var message = self.labels.get("error_no_" + (config ? "apps" : "config"));
				self._error({
					"args": {"config": config, "target": target},
					"code": "invalid_canvas_config",
					"renderError": true,
					"message": message
				});
				return;
			}
			self.set("data", config); // store Canvas data into the instance
			callback.call(self);
		},
		"onError": function(response) {
			self._error({
				"args": response,
				"code": "unable_to_retrieve_app_config",
				"renderError": true
			});
		}
	})).request();
};

Echo.Control.create(canvas);

})(Echo.jQuery);
<commit_msg>CR fix<commit_after>(function(jQuery) {
"use strict";

var $ = jQuery;

var canvas = Echo.Control.manifest("Echo.Canvas");

if (Echo.Control.isDefined(canvas)) return;

/**
 * @class Echo.Canvas
 * Class which implements Canvas mechanics on the client side.
 * The instance of this class is created for each Canvas found on the page by
 * the Echo.Loader. The instance of the class can also be created manually in
 * case the Canvas data already exists on the page.
 *
 * @package environment.pack.js
 *
 * @extends Echo.Control
 *
 * @constructor
 * Canvas object constructor to initialize the Echo.Canvas instance
 *
 * @param {Object} config
 * Configuration options
 */

/** @hide @method getRelativeTime */
/** @hide @echo_label today */
/** @hide @echo_label yesterday */
/** @hide @echo_label lastWeek */
/** @hide @echo_label lastMonth */
/** @hide @echo_label secondAgo */
/** @hide @echo_label secondsAgo */
/** @hide @echo_label minuteAgo */
/** @hide @echo_label minutesAgo */
/** @hide @echo_label hourAgo */
/** @hide @echo_label hoursAgo */
/** @hide @echo_label dayAgo */
/** @hide @echo_label daysAgo */
/** @hide @echo_label weekAgo */
/** @hide @echo_label weeksAgo */
/** @hide @echo_label monthAgo */
/** @hide @echo_label monthsAgo */
/** @hide @echo_label loading */
/** @hide @echo_label retrying */
/** @hide @echo_label error_busy */
/** @hide @echo_label error_timeout */
/** @hide @echo_label error_waiting */
/** @hide @echo_label error_view_limit */
/** @hide @echo_label error_view_update_capacity_exceeded */
/** @hide @echo_label error_result_too_large */
/** @hide @echo_label error_wrong_query */
/** @hide @echo_label error_incorrect_appkey */
/** @hide @echo_label error_internal_error */
/** @hide @echo_label error_quota_exceeded */
/** @hide @echo_label error_incorrect_user_id */
/** @hide @echo_label error_unknown */

canvas.init = function() {
	var ids, cssClass;
	var self = this, target = this.config.get("target");
	// parent init function takes care about init finalization (rendering
	// and the "onReady" event firing)
	var parent = $.proxy(this.parent, this);

	// check if the canvas was already initialized
	if (target.data("echo-canvas-initialized")) {
		this._error({
			"args": {"target": target},
			"code": "canvas_already_initialized"
		});
		return;
	}

	// define initialized state for the canvas
	// to prevent multiple initialization of the same canvas
	target.data("echo-canvas-initialized", true);

	// extending Canvas config with the parameters defined in the target
	var overrides = this._getOverrides(target, ["id", "useSecureAPI", "mode"]);
	if (!$.isEmptyObject(overrides)) {
		this.config.extend(overrides);
	}

	// exit if no "id" is defined for the canvas,
	// skip this validation in case the "data" is defined explicitly in the config
	if (!this._isManuallyConfigured() && !this.config.get("id")) {
		this._error({
			"args": {"target": target},
			"code": "invalid_canvas_config"
		});
		return;
	}

	// apply our canvas id as a CSS class if we aren't manually configured
	if (this.config.get("id")) {
		ids = this._getIds().normalized;
		// adding a primary canvas ID and unique page identifier
		// as a CSS class if provided
		cssClass = Echo.Utils.foldl("", ["main", "unique"], function(type, acc) {
			return (acc += ids[type] ? self.get("cssPrefix") + ids[type] + " " : "");
		});
		target.addClass(cssClass);
	}

	// fetch canvas config from remote storage
	this._fetchConfig(function() {
		var backplane = self.get("data.backplane");
		if (backplane) {
			Backplane.init(backplane);
		}
		self._loadAppResources(parent);
	});
};

canvas.config = {
	/**
	 * @cfg {String} [id]
	 * Unique ID of the Canvas, used by the Echo.Canvas instance
	 * to retrieve the data from the Canvases data storage.
	 */
	"id": "",

	/**
	 * @cfg {Object} [data]
	 * Object which contains the Canvas data in the format
	 * used to store the Canvas config in the Canvas storage.
	 */
	"data": {},

	/**
	 * @cfg {String} target(required)
	 * Specifies the DOM element where the control will be displayed.
	 *
	 * Note: if only the "target" config parameter is defined, the target DOM element
	 * should contain the following HTML attribute:
	 *
	 * + "data-canvas-id" with the unique Canvas ID which should be initialized
	 *
	 * The values of the HTML parameters override the "id" parameter value
	 * (respectively) passed via the Canvas config.
	 */

	/**
	 * @cfg {Object} [overrides]
	 * Object which contains the overrides applied for this Canvas on the page
	 * via Echo.Loader.override function call.
	 */
	"overrides": {},

	/**
	 * @cfg {String} [mode]
	 * This parameter specifies the mode in which Canvas works.
	 * There are two possible values for this parameter:
	 *
	 * + "dev" - in this case the Canvas works with the development configuration storage
	 * + "prod" - in this case the Canvas works with the production configuration storage
	 *
	 * More information about defference between production and development configuration
	 * storages can be found in the ["How to deploy an App using a Canvas guide"](#!/guide/how_to_deploy_an_app_using_a_canvas)
	 *
	 * The value of this parameter can be overridden by specifying the "data-canvas-mode"
	 * target DOM element attribute.
	 * More information about HTML attributes of the target DOM element can be found [here](#!/guide/how_to_deploy_an_app_using_a_canvas)
	 */
	"mode": "dev"
};

canvas.vars = {
	"apps": []
};

canvas.labels = {
	/**
	 * @echo_label
	 */
	"error_no_apps": "No applications defined for this canvas",
	/**
	 * @echo_label
	 */
	"error_no_config": "Unable to retrieve Canvas config",
	/**
	 * @echo_label
	 */
	"error_no_suitable_app_class": "Unable to init an app, no suitable JS class found",
	/**
	 * @echo_label
	 */
	"error_unable_to_retrieve_app_config": "Unable to retrieve Canvas config from the storage",
	/**
	 * @echo_label
	 */
	"error_incomplete_app_config": "Unable to init an app, config is incomplete",
	/**
	 * @echo_label
	 */
	"error_canvas_already_initialized": "Canvas has been initialized already",
	/**
	 * @echo_label
	 */
	"error_invalid_canvas_config": "Canvas with invalid configuration found"
};

/**
 * @echo_template
 */
canvas.templates.main =
	'

';

/**
 * @echo_template
 */
canvas.templates.app =
	'
' +
		'
{data:caption}
' +
		'

' +
	'
';

canvas.destroy = function() {
	$.map(this.get("apps"), $.proxy(this._destroyApp, this));
	this.config.get("target").data("echo-canvas-initialized", false);
};

/**
 * @echo_renderer
 */
canvas.renderers.container = function(element) {
	var self = this;
	$.map(this.get("data.apps"), function(app, id) {
		self._initApp(app, element, id);
	});
	return element;
};

canvas.methods._initApp = function(app, element, id) {
	var self = this;
	var Application = Echo.Utils.getComponent(app.component);
	if (!Application) {
		this._error({
			"args": {"app": app},
			"code": "no_suitable_app_class"
		});
		return;
	}

	app.id = app.id || id;  // define app position in array as id if not specified
	app.config = app.config || {};
	app.config.canvasId = this.config.get('id');

	var view = this.view.fork({
		"renderer": null,
		"renderers": {
			"appHeader": function(element) {
				// show|hide app header depending on the caption existance
				return element[app.caption ? "show" : "hide"]();
			},
			"appBody": function(element) {
				var className = self.get("cssPrefix") + "appId-" + app.id;
				return element.addClass(className);
			}
		}
	});
	element.append(view.render({
		"data": app,
		"template": this.templates.app
	}));

	app.config.target = view.get("appBody");

	var overrides = this.config.get("overrides")[app.id];
	var config = overrides
		? $.extend(true, app.config, overrides)
		: app.config;
	this.apps.push(new Application(config));
};

canvas.methods._destroyApp = function(app) {
	if (app && $.isFunction(app.destroy)) app.destroy();
};

canvas.methods._isManuallyConfigured = function() {
	return !$.isEmptyObject(this.get("data"));
};

canvas.methods._getAppScriptURL = function(config) {
	if (!config.scripts) return config.script;
	var isSecure, script = {
		"dev": config.scripts.dev || config.scripts.prod,
		"prod": config.scripts.prod || config.scripts.dev
	}[Echo.Loader.isDebug() ? "dev" : "prod"];
	if (typeof script === "string") return script;
	isSecure = /^https/.test(window.location.protocol);
	return script[isSecure ? "secure" : "regular"];
};

canvas.methods._loadAppResources = function(callback) {
	var self = this, resources = [], isManual = this._isManuallyConfigured();
	$.map(this.get("data.apps"), function(app) {
		var script = self._getAppScriptURL(app);
		if (!app.component || !script || !(isManual || app.id)) {
			self._error({
				"args": {"app": app},
				"code": "incomplete_app_config"
			});
			return;
		}
		resources.push({
			"url": script,
			"loaded": function() {
				return Echo.Control.isDefined(app.component);
			}
		});
	});
	Echo.Loader.download(resources, callback);
};

canvas.methods._getOverrides = function(target, spec) {
	return Echo.Utils.foldl({}, spec || [], function(item, acc) {
		// We should convert spec item to lower case because of jQuery
		// HTML5 data attributes implementation http://api.jquery.com/data/#data-html5
		// Since we have config keys in camel case representation like "useSecureAPI",
		// we should follow to these rules.
		var key = "canvas-" + item.toLowerCase();
		var value = target.data(key);
		if (typeof value !== "undefined") {
			acc[item] = value;
		}
	});
};

canvas.methods._error = function(args) {
	args.message = args.message || this.labels.get("error_" + args.code);

	/**
	 * @echo_event Echo.Canvas.onError
	 * Event which is triggered in case of errors such as invalid configuration,
	 * problems fetching the data from the server side, etc.
	 *
	 * @param {String} topic
	 * Name of the event produced.
	 *
	 * @param {Object} data
	 * Object which contains debug information regarding the error.
	 */
	Echo.Events.publish({
		"topic": "Echo.Canvas.onError",
		"data": args
	});

	Echo.Utils.log($.extend(args, {"type": "error", "component": "Echo.Canvas"}));
	if (args.renderError) {
		this.showMessage({
			"type": "error",
			"message": args.message
		});
	}
};

canvas.methods._getIds = function() {
	var id = this.config.get("id");
	var parts = id.split("#");
	var normalize = function(s) { return s.replace(/[^a-z\d]/ig, "-"); };
	return {
		"unique": id,
		"main": parts[0],
		"normalized": {
			"unique": normalize(id),
			"main": normalize(parts[0])
		}
	};
};

canvas.methods._fetchConfig = function(callback) {
	var self = this, target = this.config.get("target");
	var isManual = this._isManuallyConfigured();

	// no need to perform server side request in case
	// we already have all the data on the client side
	if (isManual) {
		callback.call(this);
		return;
	}
	(new Echo.API.Request({
		"apiBaseURL": Echo.Loader.config.storageURL[this.config.get("mode")],
		"secure": this.config.get("useSecureAPI"),
		// taking care of the Canvas unique identifier on the page,
		// specified as "#XXX" in the Canvas ID. We don't need to send this
		// unique page identifier, we send only the primary Canvas ID.
		"endpoint": this._getIds().main,
		"data": this.config.get("mode") === "dev" ? {"_": Math.random()} : {},
		"onData": function(config) {
			if (!config || !config.apps || !config.apps.length) {
				var message = self.labels.get("error_no_" + (config ? "apps" : "config"));
				self._error({
					"args": {"config": config, "target": target},
					"code": "invalid_canvas_config",
					"renderError": true,
					"message": message
				});
				return;
			}
			self.set("data", config); // store Canvas data into the instance
			callback.call(self);
		},
		"onError": function(response) {
			self._error({
				"args": response,
				"code": "unable_to_retrieve_app_config",
				"renderError": true
			});
		}
	})).request();
};

Echo.Control.create(canvas);

})(Echo.jQuery);
<|endoftext|>


<commit_before>import SignaturePad from 'signature_pad/dist/signature_pad.js';
import Input from '../_classes/input/Input';
import _ from 'lodash';

export default class SignatureComponent extends Input {
  static schema(...extend) {
    return Input.schema({
      type: 'signature',
      label: 'Signature',
      key: 'signature',
      footer: 'Sign above',
      width: '100%',
      height: '150px',
      penColor: 'black',
      backgroundColor: 'rgb(245,245,235)',
      minWidth: '0.5',
      maxWidth: '2.5'
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Signature',
      group: 'common',
      icon: 'pencil',
      weight: 120,
      documentation: 'http://help.form.io/userguide/#signature',
      schema: SignatureComponent.schema()
    };
  }

  init() {
    super.init();
    this.currentWidth = 0;
    this.scale = 1;
    if (!this.component.width) {
      this.component.width = '100%';
    }
    if (!this.component.height) {
      this.component.height = '200px';
    }
  }

  get emptyValue() {
    return '';
  }

  get defaultSchema() {
    return SignatureComponent.schema();
  }

  get inputInfo() {
    const info = super.inputInfo;
    info.type = 'input';
    info.attr.type = 'hidden';
    return info;
  }

  get className() {
    return `${super.className} signature-pad`;
  }

  setValue(value, flags) {
    flags = this.getFlags.apply(this, arguments);
    super.setValue(value, flags);
    if (this.options.readOnly && this.refs.signatureImage) {
      this.refs.signatureImage.setAttribute('src', value);
    }
    else if (this.signaturePad) {
      if (value && !flags.noSign) {
        this.refs.signatureImage.setAttribute('src', value);
        this.showCanvas(false);
      }
      if (!value) {
        this.signaturePad.clear();
      }
    }
  }

  showCanvas(show) {
    if (show) {
      if (this.refs.canvas) {
        this.refs.canvas.style.display = 'inherit';
      }
      this.refs.signatureImage.style.display = 'none';
    }
    else {
      if (this.refs.canvas) {
        this.refs.canvas.style.display = 'none';
      }
      this.refs.signatureImage.style.display = 'inherit';
    }
  }

  set disabled(disabled) {
    super.disabled = disabled;
    this.showCanvas(!disabled);
    if (this.signaturePad) {
      if (disabled) {
        this.signaturePad.off();
        this.refresh.classList.add('disabled');
      }
      else {
        this.signaturePad.on();
        this.refresh.classList.remove('disabled');
      }
    }
  }

  checkSize(force, scale) {
    if (force || (this.refs.padBody.offsetWidth !== this.currentWidth)) {
      this.scale = force ? scale : this.scale;
      this.currentWidth = this.refs.padBody.offsetWidth;
      this.refs.canvas.width = this.currentWidth * this.scale;
      this.refs.canvas.height = this.refs.padBody.offsetHeight * this.scale;
      const ctx = this.refs.canvas.getContext('2d');
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.scale((1 / this.scale), (1 / this.scale));
      ctx.fillStyle = this.signaturePad.backgroundColor;
      ctx.fillRect(0, 0, this.refs.canvas.width, this.refs.canvas.height);
      this.signaturePad.clear();
    }
  }

  renderElement(value, index) {
    return this.renderTemplate('signature', {
      element: super.renderElement(value, index),
      required: _.get(this.component, 'validate.required', false),
    });
  }

  attach(element) {
    this.loadRefs(element, { canvas: 'single', refresh: 'single', padBody: 'single', signatureImage: 'single' });
    super.attach(element);

    // Create the signature pad.
    if (this.refs.canvas) {
      this.signaturePad = new SignaturePad(this.refs.canvas, {
        minWidth: this.component.minWidth,
        maxWidth: this.component.maxWidth,
        penColor: this.component.penColor,
        backgroundColor: this.component.backgroundColor
      });

      this.signaturePad.onEnd = () => this.setValue(this.signaturePad.toDataURL(), {
        noSign: true
      });
      this.refs.signatureImage.setAttribute('src', this.signaturePad.toDataURL());

      // Ensure the signature is always the size of its container.
      if (this.refs.padBody) {
        this.addEventListener(window, 'resize', _.debounce(() => this.checkSize(), 100));
        setTimeout(function checkWidth() {
          if (this.refs.padBody.offsetWidth) {
            this.checkSize();
          }
          else {
            setTimeout(checkWidth.bind(this), 200);
          }
        }.bind(this), 200);
      }
    }
    this.addEventListener(this.refs.refresh, 'click', (event) => {
      event.preventDefault();
      this.showCanvas(true);
      this.signaturePad.clear();
      this.setValue(this.defaultValue);
    });
    this.setValue(this.dataValue);
  }
  /* eslint-enable max-statements */

  detach() {
    if (this.signaturePad) {
      this.signaturePad.off();
    }
    this.signaturePad = null;
    super.detach();
  }

  getView(value) {
    return value ? 'Yes' : 'No';
  }

  focus() {
    this.refs.padBody.focus();
  }
}
<commit_msg>Fix signature crash.<commit_after>import SignaturePad from 'signature_pad/dist/signature_pad.js';
import Input from '../_classes/input/Input';
import _ from 'lodash';

export default class SignatureComponent extends Input {
  static schema(...extend) {
    return Input.schema({
      type: 'signature',
      label: 'Signature',
      key: 'signature',
      footer: 'Sign above',
      width: '100%',
      height: '150px',
      penColor: 'black',
      backgroundColor: 'rgb(245,245,235)',
      minWidth: '0.5',
      maxWidth: '2.5'
    }, ...extend);
  }

  static get builderInfo() {
    return {
      title: 'Signature',
      group: 'common',
      icon: 'pencil',
      weight: 120,
      documentation: 'http://help.form.io/userguide/#signature',
      schema: SignatureComponent.schema()
    };
  }

  init() {
    super.init();
    this.currentWidth = 0;
    this.scale = 1;
    if (!this.component.width) {
      this.component.width = '100%';
    }
    if (!this.component.height) {
      this.component.height = '200px';
    }
  }

  get emptyValue() {
    return '';
  }

  get defaultSchema() {
    return SignatureComponent.schema();
  }

  get inputInfo() {
    const info = super.inputInfo;
    info.type = 'input';
    info.attr.type = 'hidden';
    return info;
  }

  get className() {
    return `${super.className} signature-pad`;
  }

  setValue(value, flags) {
    flags = this.getFlags.apply(this, arguments);
    super.setValue(value, flags);
    if (this.options.readOnly && this.refs.signatureImage) {
      this.refs.signatureImage.setAttribute('src', value);
    }
    else if (this.signaturePad) {
      if (value && !flags.noSign) {
        this.refs.signatureImage.setAttribute('src', value);
        this.showCanvas(false);
      }
      if (!value) {
        this.signaturePad.clear();
      }
    }
  }

  showCanvas(show) {
    if (show) {
      if (this.refs.canvas) {
        this.refs.canvas.style.display = 'inherit';
      }
      this.refs.signatureImage.style.display = 'none';
    }
    else {
      if (this.refs.canvas) {
        this.refs.canvas.style.display = 'none';
      }
      this.refs.signatureImage.style.display = 'inherit';
    }
  }

  set disabled(disabled) {
    super.disabled = disabled;
    this.showCanvas(!disabled);
    if (this.signaturePad) {
      if (disabled) {
        this.signaturePad.off();
        if (this.refs.refresh) {
          this.refs.refresh.classList.add('disabled');
        }
      }
      else {
        this.signaturePad.on();
        if (this.refs.refresh) {
          this.refs.refresh.classList.remove('disabled');
        }
      }
    }
  }

  checkSize(force, scale) {
    if (force || (this.refs.padBody.offsetWidth !== this.currentWidth)) {
      this.scale = force ? scale : this.scale;
      this.currentWidth = this.refs.padBody.offsetWidth;
      this.refs.canvas.width = this.currentWidth * this.scale;
      this.refs.canvas.height = this.refs.padBody.offsetHeight * this.scale;
      const ctx = this.refs.canvas.getContext('2d');
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.scale((1 / this.scale), (1 / this.scale));
      ctx.fillStyle = this.signaturePad.backgroundColor;
      ctx.fillRect(0, 0, this.refs.canvas.width, this.refs.canvas.height);
      this.signaturePad.clear();
    }
  }

  renderElement(value, index) {
    return this.renderTemplate('signature', {
      element: super.renderElement(value, index),
      required: _.get(this.component, 'validate.required', false),
    });
  }

  attach(element) {
    this.loadRefs(element, { canvas: 'single', refresh: 'single', padBody: 'single', signatureImage: 'single' });
    super.attach(element);

    // Create the signature pad.
    if (this.refs.canvas) {
      this.signaturePad = new SignaturePad(this.refs.canvas, {
        minWidth: this.component.minWidth,
        maxWidth: this.component.maxWidth,
        penColor: this.component.penColor,
        backgroundColor: this.component.backgroundColor
      });

      this.signaturePad.onEnd = () => this.setValue(this.signaturePad.toDataURL(), {
        noSign: true
      });
      this.refs.signatureImage.setAttribute('src', this.signaturePad.toDataURL());

      // Ensure the signature is always the size of its container.
      if (this.refs.padBody) {
        this.addEventListener(window, 'resize', _.debounce(() => this.checkSize(), 100));
        setTimeout(function checkWidth() {
          if (this.refs.padBody.offsetWidth) {
            this.checkSize();
          }
          else {
            setTimeout(checkWidth.bind(this), 200);
          }
        }.bind(this), 200);
      }
    }
    this.addEventListener(this.refs.refresh, 'click', (event) => {
      event.preventDefault();
      this.showCanvas(true);
      this.signaturePad.clear();
      this.setValue(this.defaultValue);
    });
    this.setValue(this.dataValue);
  }
  /* eslint-enable max-statements */

  detach() {
    if (this.signaturePad) {
      this.signaturePad.off();
    }
    this.signaturePad = null;
    super.detach();
  }

  getView(value) {
    return value ? 'Yes' : 'No';
  }

  focus() {
    this.refs.padBody.focus();
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { SPIN_STYLES, X_STYLES } from './styles';
import List from './List';

const KEY_CODES = {
	ENTER: 13,
	ESCAPE: 27,
	DOWN: 40,
	UP: 38
};

const EMPTY_STR = '';

export class Suggestor extends Component {
	constructor(props){
		super(props);
		this._bind('handleClick', 'handleChange', 'removeItem', 'handleKeyDown', 'handleItemClick', 'handleItemMouseEnter', 'handleDocClick');

		this.state = {
			value: EMPTY_STR,
			filter: props.list,
			open: false,
			index: 0
		};
	}
	_bind(...methods) {
		methods.forEach(method => this[method] = this[method].bind(this));
	}
	componentDidMount() {
		document.addEventListener('click', this.handleDocClick);
	}
	componentWillUnmount() {
		document.removeEventListener('click', this.handleDocClick);
	}
	handleDocClick(e) {
		let node = ReactDOM.findDOMNode(this);
		if (!node.contains(e.target)) {
			this.handleClose();
		}
	}
	handleClose() {
		this.setState({ open: false, filter:this.props.list, index:0 });
	}
	toggleList() {
		if(this.state.open) {
			this.handleClose();
		}
		else {
			this.setState({
				open: true
			});
		}
	}
	handleClick() {
		this.toggleList();
	}
	handleKeyDown(e) {
		let { open, index, value, filter:list } = this.state;

		switch (e.keyCode) {
			case KEY_CODES.ENTER:
				this.toggleList();
				if (open && !!list.length) {
					this.changeValue(list[index]);
				}
				break;
			case KEY_CODES.ESCAPE:
				this.handleClose();
				if (!open) {
					this.changeValue(EMPTY_STR);
				}
				break;
			case KEY_CODES.DOWN: {
				let next = (index + open) % list.length;
				this.setState({ open:true, index: next });
				break;
			}
			case KEY_CODES.UP: {
				let prev = (index || list.length) - 1;
				this.setState({ open: true, index: prev });
				break;
			}
			default:
				return;
		}

		e.preventDefault();
	}
	handleItemClick(value) {
		this.changeValue(value);
	}
	handleItemMouseEnter(index) {
		this.setState({ index });
	}
	handleChange(e) {
		e.stopPropagation();
		let value = e.target.value;
		let filter = this.filter(value);
		this.setState({ open: true, filter });
		if(!filter.length) {
			this.handleClose();
		}
		this.changeValue(value);
	}
	removeItem() {
		this.changeValue(EMPTY_STR);
	}
	changeValue(value) {
		this.setState({ value });
		this.props.onChange(value);
	}
	filter(value) {
		return this.props.list.filter(item => item.includes(value));
	}
	render() {
		let { open, value, index, filter:list } = this.state;
		let { style, placeholder } = this.props;

		return (

				<input type="text" className="form-control" onChange={this.handleChange} value={value} placeholder={placeholder} />
				{  }
				{ value &&  }
				<List {...{ list, open, index }} onItemClick={this.handleItemClick} onItemMouseEnter={this.handleItemMouseEnter} />

		);
	}
}

Suggestor.propTypes = {
	list: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
	style: React.PropTypes.object,
	placeholder: React.PropTypes.string,
	onChange: React.PropTypes.func
};
Suggestor.defaultProps = {
	onChange: f=>f
};

export default Suggestor;<commit_msg>Suggestor (+ nox prop) * no `x` mark<commit_after>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { SPIN_STYLES, X_STYLES } from './styles';
import List from './List';

const KEY_CODES = {
	ENTER: 13,
	ESCAPE: 27,
	DOWN: 40,
	UP: 38
};

const EMPTY_STR = '';

export class Suggestor extends Component {
	constructor(props){
		super(props);
		this._bind('handleClick', 'handleChange', 'removeItem', 'handleKeyDown', 'handleItemClick', 'handleItemMouseEnter', 'handleDocClick');

		this.state = {
			value: EMPTY_STR,
			filter: props.list,
			open: false,
			index: 0
		};
	}
	_bind(...methods) {
		methods.forEach(method => this[method] = this[method].bind(this));
	}
	componentDidMount() {
		document.addEventListener('click', this.handleDocClick);
	}
	componentWillUnmount() {
		document.removeEventListener('click', this.handleDocClick);
	}
	handleDocClick(e) {
		let node = ReactDOM.findDOMNode(this);
		if (!node.contains(e.target)) {
			this.handleClose();
		}
	}
	handleClose() {
		this.setState({ open: false, filter:this.props.list, index:0 });
	}
	toggleList() {
		if(this.state.open) {
			this.handleClose();
		}
		else {
			this.setState({
				open: true
			});
		}
	}
	handleClick() {
		this.toggleList();
	}
	handleKeyDown(e) {
		let { open, index, value, filter:list } = this.state;

		switch (e.keyCode) {
			case KEY_CODES.ENTER:
				this.toggleList();
				if (open && !!list.length) {
					this.changeValue(list[index]);
				}
				break;
			case KEY_CODES.ESCAPE:
				this.handleClose();
				if (!open) {
					this.changeValue(EMPTY_STR);
				}
				break;
			case KEY_CODES.DOWN: {
				let next = (index + open) % list.length;
				this.setState({ open:true, index: next });
				break;
			}
			case KEY_CODES.UP: {
				let prev = (index || list.length) - 1;
				this.setState({ open: true, index: prev });
				break;
			}
			default:
				return;
		}

		e.preventDefault();
	}
	handleItemClick(value) {
		this.changeValue(value);
	}
	handleItemMouseEnter(index) {
		this.setState({ index });
	}
	handleChange(e) {
		e.stopPropagation();
		let value = e.target.value;
		let filter = this.filter(value);
		this.setState({ open: true, filter });
		if(!filter.length) {
			this.handleClose();
		}
		this.changeValue(value);
	}
	removeItem() {
		this.changeValue(EMPTY_STR);
	}
	changeValue(value) {
		this.setState({ value });
		this.props.onChange(value);
	}
	filter(value) {
		return this.props.list.filter(item => item.includes(value));
	}
	render() {
		let { open, value, index, filter:list } = this.state;
		let { style, placeholder, nox } = this.props;

		return (

				<input type="text" className="form-control" onChange={this.handleChange} value={value} placeholder={placeholder} />
				{  }
				{ !nox && value &&  }
				<List {...{ list, open, index }} onItemClick={this.handleItemClick} onItemMouseEnter={this.handleItemMouseEnter} />

		);
	}
}

Suggestor.propTypes = {
	list: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
	style: React.PropTypes.object,
	placeholder: React.PropTypes.string,
	nox: React.PropTypes.bool,
	onChange: React.PropTypes.func
};
Suggestor.defaultProps = {
	onChange: f=>f,
	nox: false
};

export default Suggestor;<|endoftext|>


<commit_before>import Url                from 'url'
import Superagent         from 'superagent'
import { merge }          from 'lodash'

const methods = ['get', 'post', 'put', 'patch', 'del']

export default class ApiClient {

  static defaults = {
    server: {
      protocol: 'http',
      host: 'localhost',
      pathname: '/'
    },
    client: {
      pathname: '/'
    }
  }

  constructor(req, options) {
    this.req = req
    this.options = merge({}, ApiClient.defaults, options)
    methods.forEach(method => {
      this[method] = this.genericMethod.bind(this, method)
    })
  }

  genericMethod(method, path, options) {
    if (!options) { options = {} }

    let req = this.req
    let aborted = false
    let dfd = deferred()
    let request = Superagent[method](this.formatUrl(path))

    if (options.query) {
      request.query(options.query)
    }

    if (SERVER && req.get('cookie')) {
      request.set('cookie', req.get('cookie'))
    }

    if (options.data) {
      request.send(options.data)
    }

    else if (options.attach) {
      let formData = new FormData()
      let files = options.attach

      for (let key in files) {
        if (files.hasOwnProperty(key) && files[key] instanceof File) {
          formData.append(`files`, files[key])
        }
      }

      request.send(formData)
    }

    dfd.promise.abort = function() {
      aborted = true
      request.abort()
    }

    request.end((err, res) => {
      if (!aborted) {
        if (err) {
          return dfd.reject(err)
        }
        dfd.resolve(res.body)
      }
    })

    return dfd.promise
  }

  formatUrl(path) {
    const config = { ...(SERVER ? this.options.server : this.options.client) }
    const adjustedPath = path[0] === '/' ? path : `/${path}`

    if (config.base) {
      config.pathname = config.base
    }

    if (config.host && config.port) {
      config.host = `${config.host}:${config.port}`
    }

    const baseUrl = Url.format(config)
    const url = baseUrl + adjustedPath
    return url
  }
}

function deferred() {
  let resolve, reject,
  promise = new Promise((_resolve, _reject)=> {
    resolve = _resolve
    reject = _reject
  })
  return {
    promise,
    resolve,
    reject
  }
}<commit_msg>Update file attachments for built-in api client<commit_after>import Url                from 'url'
import Superagent         from 'superagent'
import { merge, map }          from 'lodash'

const methods = ['get', 'post', 'put', 'patch', 'del']

export default class ApiClient {

  static defaults = {
    server: {
      protocol: 'http',
      host: 'localhost',
      pathname: '/'
    },
    client: {
      pathname: '/'
    }
  }

  constructor(req, options) {
    this.req = req
    this.options = merge({}, ApiClient.defaults, options)
    methods.forEach(method => {
      this[method] = this.genericMethod.bind(this, method)
    })
  }

  genericMethod(method, path, options) {
    if (!options) { options = {} }

    let req = this.req
    let aborted = false
    let dfd = deferred()
    let request = Superagent[method](this.formatUrl(path))

    if (options.query) {
      request.query(options.query)
    }

    if (SERVER && req.get('cookie')) {
      request.set('cookie', req.get('cookie'))
    }

    if (options.data) {
      request.send(options.data)
    }

    else if (options.attach) {
      let form = new FormData()
      let files = options.attach

      map(files, (val, key)=> {
        if (val instanceof FileList) {
          for (let f in val) {
            if (val.hasOwnProperty(f) && val[f] instanceof File) {
              form.append(key, val[f])
            }
          }
          return
        }
        if (val instanceof File) {
          form.append(key, val)
          return
        }
        console.log('Unrecognized object type for `'+key+'`, must be a File or FileList.')
      })

      request.send(form)
    }

    dfd.promise.abort = function() {
      aborted = true
      request.abort()
    }

    request.end((err, res) => {
      if (!aborted) {
        if (err) {
          return dfd.reject(err)
        }
        dfd.resolve(res.body)
      }
    })

    return dfd.promise
  }

  formatUrl(path) {
    const config = { ...(SERVER ? this.options.server : this.options.client) }
    const adjustedPath = path[0] === '/' ? path : `/${path}`

    if (config.base) {
      config.pathname = config.base
    }

    if (config.host && config.port) {
      config.host = `${config.host}:${config.port}`
    }

    const baseUrl = Url.format(config)
    const url = baseUrl + adjustedPath
    return url
  }
}

function deferred() {
  let resolve, reject,
  promise = new Promise((_resolve, _reject)=> {
    resolve = _resolve
    reject = _reject
  })
  return {
    promise,
    resolve,
    reject
  }
}<|endoftext|>


<commit_before>function BaseDataSource(view, idProperty, dataProvider) {

    this.eventStore = new EventStore();

    //public
    this.onPageNumberChanged = function (handler) {
        this.eventStore.addEvent('onPageNumberChanged', handler);
    };
    this.onPageSizeChanged = function (handler) {
        this.eventStore.addEvent('onPageSizeChanged', handler);
    };
    this.onItemDeleted = function (handler) {
        this.eventStore.addEvent('onItemDeleted', handler);
    };
    this.onBeforeItemSaved = function (handler) {
        this.eventStore.addEvent('onBeforeItemSaved', handler);
    };
    this.onItemSaved = function (handler) {
        this.eventStore.addEvent('onItemSaved', handler);
    };
    this.onTextFilterChanged = function (handler) {
        this.eventStore.addEvent('onTextFilterChanged', handler);
    };
    this.onPropertyFiltersChanged = function (handler) {
        this.eventStore.addEvent('onPropertyFiltersChanged', handler);
    };

    this.onBeforeItemCreated = function (handler) {
        this.eventStore.addEvent('onBeforeItemCreated', handler);
    };
    this.onItemCreated = function (handler) {
        this.eventStore.addEvent('onItemCreated', handler);
    };
    this.onItemsUpdated = function (handler) {
        return this.eventStore.addEvent('onItemsUpdated', handler);
    };
    this.onItemsDeleted = function (handler) {
        this.eventStore.addEvent('onItemsDeleted', handler);
    };

    //private
    var _criteriaConstructor;
    var fillCreatedItem = true;
    var idFilter = null;
    var name = null;
    var dataItems = [];
    var currentStrategy = null;
    var listStrategy = new ListDataSourceStrategy(this);
    var editStrategy = new EditDataSourceStrategy(this);

    var propertyFilters = [];
    var queryFilter = null;
    var textFilter = null;
    var selectedItem = null;

    var pageSize = null;
    var pageNumber = null;

    var sorting = null;

    var dataBindings = [];

    var suspended = false;

    this.isSuspended = function () {
        return suspended;
    };

    var _loadingProcess = $.Deferred();

    this.loading = _loadingProcess.promise();

    this.loadingProcessDone = function () {
        _loadingProcess.resolve();
    };

    //strategy by default
    currentStrategy = listStrategy;

    this.setCriteriaConstructor = function (criteriaConstructor) {
        _criteriaConstructor = criteriaConstructor;
    };

    this.getDataItems = function () {
        return dataItems;
    };

    this.setDataItems = function (value) {
        dataItems = value;
        currentStrategy.setDataItems(value);
        if (suspended) {
            suspended = false;
        }
        currentStrategy.onItemsUpdated(dataItems);
    };

    this.setSorting = function (value) {
        sorting = (typeof value === 'undefined') ? null : value;
    };

    this.getSorting = function () {
        return sorting;
    };

    var setListStrategy = function () {
        currentStrategy = listStrategy;
    };

    var setEditStrategy = function () {
        currentStrategy = editStrategy;
    };

    this.setUserStrategy = function (strategy) {
        currentStrategy = strategy;
    };

    var addOrUpdateItem = function (item) {
        if (dataItems.length == 1) {
            dataItems[0] = item;
        }
        else {
            //may be slow method to add
            dataItems.push(item);
        }
    };

    var removeById = function (item) {
        for (var i = 0; i < dataItems.length; i++) {
            if (dataItems[i].Id === item.Id) {
                var indexToSplice = i;
                break;
            }
        }
        if (indexToSplice !== undefined) {
            dataItems.splice(indexToSplice, 1);
        }
    };

    var resetModified = function (item) {
        if (item != null) {
            var index = modifiedItems.indexOf(item);
            if (index > -1) {
                modifiedItems.splice(index, 1);
            }
        }
    };

    this.suspendUpdate = function () {
        suspended = true;
    };

    this.resumeUpdate = function (callback) {
        if (suspended) {
            suspended = false;
        }
        this.updateItems(callback);
    };

    this.getPageNumber = function () {
        return pageNumber;
    };

    this.setPageNumber = function (value) {
        if (value < 0) {
            value = 0;
        }

        if (value !== pageNumber) {
            pageNumber = value;
            if (currentStrategy.onPageNumberChanged !== null) {
                currentStrategy.onPageNumberChanged(value);
            }
            this.updateItems();
        }
    };

    this.getPageSize = function () {
        return pageSize;
    };

    this.setPageSize = function (value) {
        if (value < 0) {
            value = 0;
        }
        else if (value > 1000) {
            value = 1000;
        }

        var isPageSizeChanging = pageSize !== value;

        pageNumber = 0;
        pageSize = value;

        if (isPageSizeChanging && !suspended) {
            currentStrategy.onPageNumberChanged(pageNumber);
            currentStrategy.onPageSizeChanged(value);

            this.updateItems();
        }
    };

    this.getName = function () {
        return name;
    };

    this.setName = function (value) {
        name = value;
    };

    this.getIdProperty = function () {
        return idProperty || 'Id';
    };

    this.setIdProperty = function (value) {
        idProperty = value;
    };

    this.getFillCreatedItem = function () {
        return fillCreatedItem;
    };

    this.setFillCreatedItem = function (value) {
        fillCreatedItem = value || false;
    };

    this.getIdFilter = function () {
        return idFilter;
    };

    this.setIdFilter = function (value) {
        if (idFilter !== value) {
            idFilter = value;
            this.updateItems();
        }
    };

    this.setEditMode = function () {
        setEditStrategy();
    };

    this.setListMode = function () {
        setListStrategy();
    };

    this.getView = function () {
        return view;
    };

    this.createItem = function () {
        dataProvider.createItem(
            function (data) {
                addOrUpdateItem(data);
                currentStrategy.onItemCreated(data);
                setModified(data);
            });
    };
    var warnings = false;

    this.saveItem = function (item, onSuccess/*, onError*/) {

        var showErrors = function (errors) {
            if (_.isEmpty(errors) || errors.IsValid) {
                return;
            }
            if (errors.Message instanceof Array) {
                for (var i = 0; i < errors.Message.length; i++) {
                    toastr.error(errors.Message[i].Message, "Ошибка!");
                }
            } else if(typeof errors.Message == 'string' && errors.Message.indexOf('{') >= 0){
                var result = JSON.parse(errors.Message);
                result = result.ValidationMessage &&
                result.ValidationMessage.ValidationErrors &&
                result.ValidationMessage.ValidationErrors.Message ? result.ValidationMessage.ValidationErrors.Message : 'Обратитесь к системному администратору';
                toastr.error(result, "Ошибка!");
            }else{
                toastr.error('Обратитесь к системному администратору', "Ошибка!");
            }
        };

        var showWarnings = function (warnings) {
            if (_.isEmpty(warnings) || warnings.IsValid) {
                return;
            }

            var resultText = warnings.Message[0].Message;
            for (var j = 1; j < warnings.Message.length; j++) {
                resultText += '
' + warnings.Message[j].Message;
            }
            resultText += '
Продолжить добавление?';

            new MessageBox({
                text: resultText,
                buttons: [
                    {
                        name: 'Да',
                        type: 'action',
                        onClick: attemptSave.bind(undefined, true)
                    },
                    {
                        name: 'Нет'
                    }
                ]
            });
        };

        var invokeCallback = function (callback) {
            if (typeof callback === 'function') {
                var args = Array.prototype.slice.call(arguments, 1);
                callback.apply(undefined, args);
            }
        };

        var idProperty = that.getIdProperty() || "Id";

        var attemptSave = function (warnings) {
            dataProvider.replaceItem(item, warnings, function (data) {
                //TODO: убрать 'data.IsValid == undefined' когда заполнятся метаданные
                if ((data.IsValid || data.IsValid == undefined) ) {
                    if(!(data instanceof Array) && item != null) {
                        item[idProperty] = data.InstanceId;
                        addOrUpdateItem(item);
                        currentStrategy.onItemSaved(item, data);
                        resetModified(item);
                    }
                    invokeCallback(onSuccess, data);
                } else {
                    var validation = data.ValidationMessage;
                    showErrors(validation.ValidationErrors);
                    showWarnings(validation.ValidationWarnings);
                }
            });

        };

        attemptSave(warnings);
    };

    this.deleteItem = function (item) {
        dataProvider.deleteItem(item, function (data) {
            removeById(item);
            currentStrategy.onItemDeleted(item)
        });
    };

    var loadItems = function (resultCallback) {
        currentStrategy.getItems(dataProvider, resultCallback);
    };

    this.getItems = function (resultCallback) {
        loadItems(resultCallback);
    };

    this.updateItems = function (callback) {
        if (!suspended) {
            loadItems(function (data) {

                dataItems = data;
                currentStrategy.onItemsUpdated(data);

                if (callback) {
                    callback(data);
                }
            });
        }
    };

    this.setQueryFilter = function (value, callback) {

        if (queryFilter !== value) {
            queryFilter = _criteriaConstructor(value);
            this.updateItems(callback);
        }
    };

    this.getQueryFilter = function () {
        return queryFilter;
    };

    this.setPropertyFilters = function (value) {
        if (propertyFilters !== value) {
            pageNumber = 0;
            propertyFilters = value;

            currentStrategy.onPageNumberChanged(0);
            currentStrategy.onPropertyFiltersChanged(value);
            this.updateItems();
        }
    };

    this.getPropertyFilters = function () {
        return propertyFilters;
    };

    this.getTextFilter = function () {
        return textFilter;
    };

    this.setTextFilter = function (value) {
        if (textFilter !== value) {
            pageNumber = 0;
            textFilter = value;
            var self = this;

            setTimeout(function () {
                currentStrategy.onPageNumberChanged(0);
                currentStrategy.onTextFilterChanged(value);
                self.updateItems();
            }, 30)
        }
    };

    this.addDataBinding = function (value) {
        if (value !== null) {
            dataBindings.push(value);

            value.onSetPropertyValue(onSetPropertyValueHandler);

            if (!suspended) {

                //устанавливаем значение элемента
                currentStrategy.bindItems(value, dataItems, this);

            }

        }
    };

    this.removeDataBinding = function (value) {
        var itemIndex = dataBindings.indexOf(value);
        if (itemIndex > -1) {
            dataBindings.splice(itemIndex, 1);

            //remove data binding problem
            value.removeOnSetPropertyValue(onSetPropertyValueHandler);
        }
    };

    this.getSelectedItem = function () {
        if (selectedItem === undefined || selectedItem === null) {
            return null;
        }

        return JSON.parse(JSON.stringify(selectedItem));
    };

    this.setSelectedItem = function (value) {
        var isEmpty = function (value) {
            return typeof value === 'undefined' || value === null;
        };

        if (isEmpty(value) || isEmpty(selectedItem)) {
            if (isEmpty(value) && isEmpty(selectedItem)) {
                return;
            }
        } else {
            if (JSON.stringify(selectedItem) === JSON.stringify(value)) {
                return;
            }
        }
        //TODO: Проверить работу selectedItem в ListBox
        //if (JSON.stringify(selectedItem) === JSON.stringify(value)) {
        //    return;
        //}

        selectedItem = currentStrategy.syncSelectedItem(value);
        currentStrategy.onSelectedItemChanged(selectedItem);
    };

    this.getDataBindings = function () {
        return dataBindings;
    };

    var that = this;

    var onSetPropertyValueHandler = function (context, args) {
        var propertyName = args.property;
        var propertyValue = args.value;

        if (propertyName !== undefined && propertyName !== null) {
            var selectedItem = that.getSelectedItem();

            if (selectedItem === null) {
                selectedItem = {};
            }
            if (selectedItem !== null) {
                if (propertyName.length > 2 && propertyName.substring(0, 2) === '$.') {
                    InfinniUI.ObjectUtils.setPropertyValue(selectedItem, propertyName.substr(2), propertyValue);
                }
                else if (propertyName !== '$') {
                    InfinniUI.ObjectUtils.setPropertyValue(selectedItem, propertyName, propertyValue);
                }
                else {
                    for (var property in selectedItem) {
                        delete(selectedItem[property]);
                    }

                    for (var property in propertyValue) {
                        selectedItem[property] = propertyValue[property];
                    }
                }
                that.setSelectedItem(selectedItem);
                setModified(selectedItem);
            }

            var bindings = that.getDataBindings();
            for (var i = 0; i < bindings.length; i++) {
                if (bindings[i].getProperty() === propertyName) {
                    bindings[i].propertyValueChanged(propertyValue);
                }
            }

            that.eventStore.executeEvent('onSelectedItemModified');
        }
    };

    this.onSelectedItemChanged = function (action) {
        this.eventStore.addEvent('onSelectedItemChanged', action);
    };

    this.onSelectedItemModified = function (action) {
        this.eventStore.addEvent('onSelectedItemModified', action);
    };

    //Добавляем событие по умолчанию (уведомление всех датабиндингов)
    this.onSelectedItemChanged(function (context, args) {
        var selectedItem = args.value;

        for (var i = 0; i < dataBindings.length; i++) {
            var propertyName = dataBindings[i].getProperty();

            if (/^\d+/.test(propertyName)) {
                /**
                 * @TODO Костыль! Чтобы при изменении выбранного элемента, в привязки для ItemTemplate не вкидывались
                 * неверные данные из selectedItem
                 */
            } else if (propertyName) {
                if (propertyName.length > 2 && propertyName.substring(0, 2) === '$.') {
                    var propertyValue = InfinniUI.ObjectUtils.getPropertyValue(selectedItem, propertyName.substring(2, propertyName.length));
                }

                else if (propertyName !== '$') {
                    var propertyValue = InfinniUI.ObjectUtils.getPropertyValue(selectedItem, propertyName); //selectedItem[propertyName];
                }

                else {
                    var propertyValue = selectedItem;
                }

                dataBindings[i].propertyValueChanged(propertyValue);
            }
        }
    });

    this.onItemsUpdated(function (context, args) {
        var items = args.value;
        _.each(dataBindings, function (binding) {
            if (binding.property === '$') {
                var value = this.getSelectedItem();
            } else if (/^\$\..+$/.test(binding.property)) {
                var value = InfinniUI.ObjectUtils.getPropertyValue(this.getSelectedItem(), binding.property.substr(2));
            } else {
                var value = InfinniUI.ObjectUtils.getPropertyValue(items, binding.property);
            }
            binding.bind(value);
        }, that);
    });

    var modifiedItems = [];

    this.isModifiedItems = function () {
        return modifiedItems.length > 0;
    };

    this.isModified = function (value) {
        var isModified = value != null && value !== undefined;
        if (!isModified) {
            return false;
        }
        else {
            var index = modifiedItems.indexOf(value);
            return index > -1;
        }
    };

    var setModified = function (value) {
        if (value != null) {
            modifiedItems.push(value);
        }
    };

}

function isMainDataSource(ds) {
    return ds.getName() == 'MainDataSource';
}<commit_msg>add message (error, warning) to deleteItem<commit_after>function BaseDataSource(view, idProperty, dataProvider) {

    this.eventStore = new EventStore();

    //public
    this.onPageNumberChanged = function (handler) {
        this.eventStore.addEvent('onPageNumberChanged', handler);
    };
    this.onPageSizeChanged = function (handler) {
        this.eventStore.addEvent('onPageSizeChanged', handler);
    };
    this.onItemDeleted = function (handler) {
        this.eventStore.addEvent('onItemDeleted', handler);
    };
    this.onBeforeItemSaved = function (handler) {
        this.eventStore.addEvent('onBeforeItemSaved', handler);
    };
    this.onItemSaved = function (handler) {
        this.eventStore.addEvent('onItemSaved', handler);
    };
    this.onTextFilterChanged = function (handler) {
        this.eventStore.addEvent('onTextFilterChanged', handler);
    };
    this.onPropertyFiltersChanged = function (handler) {
        this.eventStore.addEvent('onPropertyFiltersChanged', handler);
    };

    this.onBeforeItemCreated = function (handler) {
        this.eventStore.addEvent('onBeforeItemCreated', handler);
    };
    this.onItemCreated = function (handler) {
        this.eventStore.addEvent('onItemCreated', handler);
    };
    this.onItemsUpdated = function (handler) {
        return this.eventStore.addEvent('onItemsUpdated', handler);
    };
    this.onItemsDeleted = function (handler) {
        this.eventStore.addEvent('onItemsDeleted', handler);
    };

    //private
    var _criteriaConstructor;
    var fillCreatedItem = true;
    var idFilter = null;
    var name = null;
    var dataItems = [];
    var currentStrategy = null;
    var listStrategy = new ListDataSourceStrategy(this);
    var editStrategy = new EditDataSourceStrategy(this);

    var propertyFilters = [];
    var queryFilter = null;
    var textFilter = null;
    var selectedItem = null;

    var pageSize = null;
    var pageNumber = null;

    var sorting = null;

    var dataBindings = [];

    var suspended = false;

    this.isSuspended = function () {
        return suspended;
    };

    var _loadingProcess = $.Deferred();

    this.loading = _loadingProcess.promise();

    this.loadingProcessDone = function () {
        _loadingProcess.resolve();
    };

    //strategy by default
    currentStrategy = listStrategy;

    this.setCriteriaConstructor = function (criteriaConstructor) {
        _criteriaConstructor = criteriaConstructor;
    };

    this.getDataItems = function () {
        return dataItems;
    };

    this.setDataItems = function (value) {
        dataItems = value;
        currentStrategy.setDataItems(value);
        if (suspended) {
            suspended = false;
        }
        currentStrategy.onItemsUpdated(dataItems);
    };

    this.setSorting = function (value) {
        sorting = (typeof value === 'undefined') ? null : value;
    };

    this.getSorting = function () {
        return sorting;
    };

    var setListStrategy = function () {
        currentStrategy = listStrategy;
    };

    var setEditStrategy = function () {
        currentStrategy = editStrategy;
    };

    this.setUserStrategy = function (strategy) {
        currentStrategy = strategy;
    };

    var addOrUpdateItem = function (item) {
        if (dataItems.length == 1) {
            dataItems[0] = item;
        }
        else {
            //may be slow method to add
            dataItems.push(item);
        }
    };

    var removeById = function (item) {
        for (var i = 0; i < dataItems.length; i++) {
            if (dataItems[i].Id === item.Id) {
                var indexToSplice = i;
                break;
            }
        }
        if (indexToSplice !== undefined) {
            dataItems.splice(indexToSplice, 1);
        }
    };

    var resetModified = function (item) {
        if (item != null) {
            var index = modifiedItems.indexOf(item);
            if (index > -1) {
                modifiedItems.splice(index, 1);
            }
        }
    };

    this.suspendUpdate = function () {
        suspended = true;
    };

    this.resumeUpdate = function (callback) {
        if (suspended) {
            suspended = false;
        }
        this.updateItems(callback);
    };

    this.getPageNumber = function () {
        return pageNumber;
    };

    this.setPageNumber = function (value) {
        if (value < 0) {
            value = 0;
        }

        if (value !== pageNumber) {
            pageNumber = value;
            if (currentStrategy.onPageNumberChanged !== null) {
                currentStrategy.onPageNumberChanged(value);
            }
            this.updateItems();
        }
    };

    this.getPageSize = function () {
        return pageSize;
    };

    this.setPageSize = function (value) {
        if (value < 0) {
            value = 0;
        }
        else if (value > 1000) {
            value = 1000;
        }

        var isPageSizeChanging = pageSize !== value;

        pageNumber = 0;
        pageSize = value;

        if (isPageSizeChanging && !suspended) {
            currentStrategy.onPageNumberChanged(pageNumber);
            currentStrategy.onPageSizeChanged(value);

            this.updateItems();
        }
    };

    this.getName = function () {
        return name;
    };

    this.setName = function (value) {
        name = value;
    };

    this.getIdProperty = function () {
        return idProperty || 'Id';
    };

    this.setIdProperty = function (value) {
        idProperty = value;
    };

    this.getFillCreatedItem = function () {
        return fillCreatedItem;
    };

    this.setFillCreatedItem = function (value) {
        fillCreatedItem = value || false;
    };

    this.getIdFilter = function () {
        return idFilter;
    };

    this.setIdFilter = function (value) {
        if (idFilter !== value) {
            idFilter = value;
            this.updateItems();
        }
    };

    this.setEditMode = function () {
        setEditStrategy();
    };

    this.setListMode = function () {
        setListStrategy();
    };

    this.getView = function () {
        return view;
    };

    this.createItem = function () {
        dataProvider.createItem(
            function (data) {
                addOrUpdateItem(data);
                currentStrategy.onItemCreated(data);
                setModified(data);
            });
    };
    var warnings = false;

    this.showErrors = function(errors){
        if (_.isEmpty(errors) || errors.IsValid) {
            return;
        }
        if (errors.Message instanceof Array) {
            for (var i = 0; i < errors.Message.length; i++) {
                toastr.error(errors.Message[i].Message, "Ошибка!");
            }
        } else if(typeof errors.Message == 'string' && errors.Message.indexOf('{') >= 0){
            var result = JSON.parse(errors.Message);
            result = result.ValidationMessage &&
            result.ValidationMessage.ValidationErrors &&
            result.ValidationMessage.ValidationErrors.Message ? result.ValidationMessage.ValidationErrors.Message : 'Обратитесь к системному администратору';
            toastr.error(result, "Ошибка!");
        }else{
            toastr.error('Обратитесь к системному администратору', "Ошибка!");
        }
    };

    this.showWarnings = function(warnings){
        if (_.isEmpty(warnings) || warnings.IsValid) {
            return;
        }

        var resultText = warnings.Message[0].Message;
        for (var j = 1; j < warnings.Message.length; j++) {
            resultText += '
' + warnings.Message[j].Message;
        }
        resultText += '
Продолжить добавление?';

        new MessageBox({
            text: resultText,
            buttons: [
                {
                    name: 'Да',
                    type: 'action',
                    onClick: attemptSave.bind(undefined, true)
                },
                {
                    name: 'Нет'
                }
            ]
        });
    };

    this.saveItem = function (item, onSuccess/*, onError*/) {
        var invokeCallback = function (callback) {
            if (typeof callback === 'function') {
                var args = Array.prototype.slice.call(arguments, 1);
                callback.apply(undefined, args);
            }
        };

        var idProperty = that.getIdProperty() || "Id";

        var self = this;

        var attemptSave = function (warnings) {
            dataProvider.replaceItem(item, warnings, function (data) {
                //TODO: убрать 'data.IsValid == undefined' когда заполнятся метаданные
                if ((data.IsValid || data.IsValid == undefined) ) {
                    if(!(data instanceof Array) && item != null) {
                        item[idProperty] = data.InstanceId;
                        addOrUpdateItem(item);
                        currentStrategy.onItemSaved(item, data);
                        resetModified(item);
                    }
                    invokeCallback(onSuccess, data);
                } else {
                    var validation = data.ValidationMessage;
                    self.showErrors(validation.ValidationErrors);
                    self.showWarnings(validation.ValidationWarnings);
                }
            });

        };

        attemptSave(warnings);
    };

    this.deleteItem = function (item) {
        var self = this;
        dataProvider.deleteItem(item, function (data) {
            if ((data.IsValid || data.IsValid == undefined) ) {
                removeById(item);
                currentStrategy.onItemDeleted(item);
            }else{
                var validation = data.ValidationMessage;
                self.showErrors(validation.ValidationErrors);
                self.showWarnings(validation.ValidationWarnings);
            }
        });
    };

    var loadItems = function (resultCallback) {
        currentStrategy.getItems(dataProvider, resultCallback);
    };

    this.getItems = function (resultCallback) {
        loadItems(resultCallback);
    };

    this.updateItems = function (callback) {
        if (!suspended) {
            loadItems(function (data) {

                dataItems = data;
                currentStrategy.onItemsUpdated(data);

                if (callback) {
                    callback(data);
                }
            });
        }
    };

    this.setQueryFilter = function (value, callback) {

        if (queryFilter !== value) {
            queryFilter = _criteriaConstructor(value);
            this.updateItems(callback);
        }
    };

    this.getQueryFilter = function () {
        return queryFilter;
    };

    this.setPropertyFilters = function (value) {
        if (propertyFilters !== value) {
            pageNumber = 0;
            propertyFilters = value;

            currentStrategy.onPageNumberChanged(0);
            currentStrategy.onPropertyFiltersChanged(value);
            this.updateItems();
        }
    };

    this.getPropertyFilters = function () {
        return propertyFilters;
    };

    this.getTextFilter = function () {
        return textFilter;
    };

    this.setTextFilter = function (value) {
        if (textFilter !== value) {
            pageNumber = 0;
            textFilter = value;
            var self = this;

            setTimeout(function () {
                currentStrategy.onPageNumberChanged(0);
                currentStrategy.onTextFilterChanged(value);
                self.updateItems();
            }, 30)
        }
    };

    this.addDataBinding = function (value) {
        if (value !== null) {
            dataBindings.push(value);

            value.onSetPropertyValue(onSetPropertyValueHandler);

            if (!suspended) {

                //устанавливаем значение элемента
                currentStrategy.bindItems(value, dataItems, this);

            }

        }
    };

    this.removeDataBinding = function (value) {
        var itemIndex = dataBindings.indexOf(value);
        if (itemIndex > -1) {
            dataBindings.splice(itemIndex, 1);

            //remove data binding problem
            value.removeOnSetPropertyValue(onSetPropertyValueHandler);
        }
    };

    this.getSelectedItem = function () {
        if (selectedItem === undefined || selectedItem === null) {
            return null;
        }

        return JSON.parse(JSON.stringify(selectedItem));
    };

    this.setSelectedItem = function (value) {
        var isEmpty = function (value) {
            return typeof value === 'undefined' || value === null;
        };

        if (isEmpty(value) || isEmpty(selectedItem)) {
            if (isEmpty(value) && isEmpty(selectedItem)) {
                return;
            }
        } else {
            if (JSON.stringify(selectedItem) === JSON.stringify(value)) {
                return;
            }
        }
        //TODO: Проверить работу selectedItem в ListBox
        //if (JSON.stringify(selectedItem) === JSON.stringify(value)) {
        //    return;
        //}

        selectedItem = currentStrategy.syncSelectedItem(value);
        currentStrategy.onSelectedItemChanged(selectedItem);
    };

    this.getDataBindings = function () {
        return dataBindings;
    };

    var that = this;

    var onSetPropertyValueHandler = function (context, args) {
        var propertyName = args.property;
        var propertyValue = args.value;

        if (propertyName !== undefined && propertyName !== null) {
            var selectedItem = that.getSelectedItem();

            if (selectedItem === null) {
                selectedItem = {};
            }
            if (selectedItem !== null) {
                if (propertyName.length > 2 && propertyName.substring(0, 2) === '$.') {
                    InfinniUI.ObjectUtils.setPropertyValue(selectedItem, propertyName.substr(2), propertyValue);
                }
                else if (propertyName !== '$') {
                    InfinniUI.ObjectUtils.setPropertyValue(selectedItem, propertyName, propertyValue);
                }
                else {
                    for (var property in selectedItem) {
                        delete(selectedItem[property]);
                    }

                    for (var property in propertyValue) {
                        selectedItem[property] = propertyValue[property];
                    }
                }
                that.setSelectedItem(selectedItem);
                setModified(selectedItem);
            }

            var bindings = that.getDataBindings();
            for (var i = 0; i < bindings.length; i++) {
                if (bindings[i].getProperty() === propertyName) {
                    bindings[i].propertyValueChanged(propertyValue);
                }
            }

            that.eventStore.executeEvent('onSelectedItemModified');
        }
    };

    this.onSelectedItemChanged = function (action) {
        this.eventStore.addEvent('onSelectedItemChanged', action);
    };

    this.onSelectedItemModified = function (action) {
        this.eventStore.addEvent('onSelectedItemModified', action);
    };

    //Добавляем событие по умолчанию (уведомление всех датабиндингов)
    this.onSelectedItemChanged(function (context, args) {
        var selectedItem = args.value;

        for (var i = 0; i < dataBindings.length; i++) {
            var propertyName = dataBindings[i].getProperty();

            if (/^\d+/.test(propertyName)) {
                /**
                 * @TODO Костыль! Чтобы при изменении выбранного элемента, в привязки для ItemTemplate не вкидывались
                 * неверные данные из selectedItem
                 */
            } else if (propertyName) {
                if (propertyName.length > 2 && propertyName.substring(0, 2) === '$.') {
                    var propertyValue = InfinniUI.ObjectUtils.getPropertyValue(selectedItem, propertyName.substring(2, propertyName.length));
                }

                else if (propertyName !== '$') {
                    var propertyValue = InfinniUI.ObjectUtils.getPropertyValue(selectedItem, propertyName); //selectedItem[propertyName];
                }

                else {
                    var propertyValue = selectedItem;
                }

                dataBindings[i].propertyValueChanged(propertyValue);
            }
        }
    });

    this.onItemsUpdated(function (context, args) {
        var items = args.value;
        _.each(dataBindings, function (binding) {
            if (binding.property === '$') {
                var value = this.getSelectedItem();
            } else if (/^\$\..+$/.test(binding.property)) {
                var value = InfinniUI.ObjectUtils.getPropertyValue(this.getSelectedItem(), binding.property.substr(2));
            } else {
                var value = InfinniUI.ObjectUtils.getPropertyValue(items, binding.property);
            }
            binding.bind(value);
        }, that);
    });

    var modifiedItems = [];

    this.isModifiedItems = function () {
        return modifiedItems.length > 0;
    };

    this.isModified = function (value) {
        var isModified = value != null && value !== undefined;
        if (!isModified) {
            return false;
        }
        else {
            var index = modifiedItems.indexOf(value);
            return index > -1;
        }
    };

    var setModified = function (value) {
        if (value != null) {
            modifiedItems.push(value);
        }
    };

}

function isMainDataSource(ds) {
    return ds.getName() == 'MainDataSource';
}<|endoftext|>


<commit_before>//
// Client side boot loader
//
// This file initializes the ES6 System Loader,
// the ServiceWorker - if possible - and starts the initialize client code.
//
import * as path from 'path'
import kernel_conf from 'kernel_conf'

import { Loader } from './loader'

const { filter, shift } = Array.prototype

export default async function() {
  // Lookup self script tag as the service worker will be started using
  // the same kernel script.
  const script = do {
    document.querySelectorAll('script')
      ::filter(el => typeof el.dataset.livelyKernel !== 'undefined')
      ::shift()
  }

  if (typeof script === 'undefined') {
    throw new Error('Cannot find lively kernel script tag. You must add the `data-lively-kernel` attribute!')
  }

  let src = new URL(script.src)
  let base = new URL(kernel_conf.base, src)

  //
  // Service worker
  //

  if (!('serviceWorker' in navigator) || true) {
    console.error('[KERNEL] ServiceWorker API not available')
    console.error('[KERNEL] Your browser is total wrong for this. I refuse to continue any further...')
    return undefined
  }

  try {
    let scope = new URL('./', src)
    let registration = await navigator.serviceWorker.register(src, {scope: scope})

    let serviceWorker = do {
      registration.installing || registration.waiting || registration.active
    }

    if (serviceWorker.state !== 'active') {
      let swState = new Promise((resolve, reject) => {
        const fn = (event) => {
          serviceWorker.removeEventListener('statechange', fn)

          if (event.target.state === 'redundant') {
            reject(new Error('State changed to redundant'))
          } else {
            resolve(event.target)
          }
        }

        serviceWorker.addEventListener('statechange', fn)
      })

      await swState
    }

    // Set loader base to redirect all file requests to service worker
    // file systems
    base = new URL('https://lively/')

    console.log('[KERNEL] ServiceWorker registered and ready')

  } catch(e) {
    console.error('[KERNEL] ServiceWorker install failed:', e)
    console.log('[KERNEL] Continue with client-only boot...')
  }

  //
  // ES module loader
  //

  let loader = new Loader({
    base: base
  })

  loader.set('kernel', {
    resolve: (name) => loader.resolve(name).toString(),
    realpath: (name) => path.normalize(name),
  })

  //
  // Initialize system (like running init process)
  //

  let init = do {
    if ('livelyKernelInit' in script.dataset) {
      script.dataset.livelyKernelInit
    } else {
      path.normalize(kernel_conf.init)
    }
  }

  if (init) {
    return loader.import(init)
  } else {
    return true
  }
}
<commit_msg>Fix things<commit_after>//
// Client side boot loader
//
// This file initializes the ES6 System Loader,
// the ServiceWorker - if possible - and starts the initialize client code.
//
import * as path from 'path'
import kernel_conf from 'kernel_conf'

import { Loader } from './loader'

const { filter, shift } = Array.prototype

export default async function() {
  // Lookup self script tag as the service worker will be started using
  // the same kernel script.
  const script = do {
    document.querySelectorAll('script')
      ::filter(el => typeof el.dataset.livelyKernel !== 'undefined')
      ::shift()
  }

  if (typeof script === 'undefined') {
    throw new Error('Cannot find lively kernel script tag. You must add the `data-lively-kernel` attribute!')
  }

  let src = new URL(script.src)
  let base = new URL(kernel_conf.base, src)

  //
  // Service worker
  //

  if (!('serviceWorker' in navigator)) {
    console.error('[KERNEL] ServiceWorker API not available')
    console.error('[KERNEL] Your browser is total wrong for this. I refuse to continue any further...')
    return undefined
  }

  try {
    let scope = new URL('./', src)
    let registration = await navigator.serviceWorker.register(src, {scope: scope})

    let serviceWorker = do {
      registration.installing || registration.waiting || registration.active
    }

    if (serviceWorker.state !== 'active') {
      let swState = new Promise((resolve, reject) => {
        const fn = (event) => {
          serviceWorker.removeEventListener('statechange', fn)

          if (event.target.state === 'redundant') {
            reject(new Error('State changed to redundant'))
          } else {
            resolve(event.target)
          }
        }

        serviceWorker.addEventListener('statechange', fn)
      })

      await swState
    }

    // Set loader base to redirect all file requests to service worker
    // file systems
    base = new URL('https://lively/')

    console.log('[KERNEL] ServiceWorker registered and ready')

  } catch(e) {
    console.error('[KERNEL] ServiceWorker install failed:', e)
    console.log('[KERNEL] Continue with client-only boot...')
  }

  //
  // ES module loader
  //

  let loader = new Loader({
    base: base
  })

  loader.set('kernel', {
    resolve: (name) => loader.resolve(name).toString(),
    realpath: (name) => path.normalize(name),
  })

  //
  // Initialize system (like running init process)
  //

  let init = do {
    if ('livelyKernelInit' in script.dataset) {
      script.dataset.livelyKernelInit
    } else {
      path.normalize(kernel_conf.init)
    }
  }

  if (init) {
    return loader.import(init)
  } else {
    return true
  }
}
<|endoftext|>


<commit_before>var superagent = require('superagent')
var crypto = require('crypto')
var debug = require('debug')('mcash')
var format = require('util').format
var CONTENT_TYPE = 'application/vnd.mcash.api.merchant.v1+json'
var lodash = require('lodash')
var parseUrl = require('url').parse

superagent.parse[CONTENT_TYPE] = superagent.parse['application/json']

var Mcash = module.exports = exports = function(opts) {
    this.opts = opts
}

exports.ENDPOINTS = {
    production: 'https://api.mca.sh/merchant/v1/',
    test: 'https://mcashtestbed.appspot.com/merchant/v1/'
}

function mcashTimestamp() {
    return new Date().toISOString().replace(/T/, ' ').substr(0, 19)
}

Mcash.prototype.request = function(type, url, opts) {
    var fn = superagent[type.toLowerCase()]

    opts = lodash.extend({
        payload: null
    }, opts)

   var request = fn(exports.ENDPOINTS[this.opts.environment] + url)
    .buffer(true)
    .parse(superagent.parse.json)
    .set('Accept', 'application/vnd.mcash.api.merchant.v1+json')
    .set('X-Mcash-Merchant', this.opts.merchantId)
    .set('X-Mcash-User', this.opts.user)

    if (this.opts.environment == 'test') {
        request = request.set('X-Testbed-Token', this.opts.testbedToken)
    }

    if (this.opts.secret) {
        request = request
        .set('Authorization', 'SECRET ' + this.opts.secret)
    } else if (this.opts.privateKey) {
        var payload = opts.payload ? JSON.stringify(opts.payload) : ''
        debug('payload (post data) is:\n%s', payload)
        var digest = crypto.createHash('sha256').update(payload).digest('base64')

        request = request
        .set('X-Mcash-Timestamp', mcashTimestamp())
        .set('X-Mcash-Content-Digest', 'SHA256=' + digest)

        var parsedUrl = parseUrl(request.url)

        var concat = format('%s|%s|%s',
            request.req.method,
            request.url,
            Object.keys(request.req._headers).filter(function(key) {
                return !!key.match(/^x-mcash-/)
            }).sort().reduce(function(p, key) {
                return format('%s%s%s=%s',
                    p,
                    p.length ? '&' : '',
                    key.toUpperCase(),
                    request.req._headers[key])
            }, ''))

        var sign = crypto.createSign('RSA-SHA256')
        .update(concat)
        .sign(this.opts.privateKey, 'base64')

        if (!sign) {
            throw new Error('Unable to sign. Bad private key?')
        }

        debug('will sign with private key:\n%s', concat)

        debug('signature:\n%s', sign)

        request = request
        .set('Authorization', 'RSA-SHA256 ' + sign)
    }

    if (opts.payload) {
        request = request.send(opts.payload)
    }

    debug('all headers, for debugging:\n%s', Object.keys(request.req._headers).map(function(key) {
        return '\t' + key + ': ' + request.req._headers[key]
    }).join('\n'))

    return request
}

exports.handler = require('./handler')
<commit_msg>Formatting<commit_after>var superagent = require('superagent')
var crypto = require('crypto')
var debug = require('debug')('mcash')
var format = require('util').format
var CONTENT_TYPE = 'application/vnd.mcash.api.merchant.v1+json'
var lodash = require('lodash')
var parseUrl = require('url').parse

superagent.parse[CONTENT_TYPE] = superagent.parse['application/json']

var Mcash = module.exports = exports = function(opts) {
    this.opts = opts
}

exports.ENDPOINTS = {
    production: 'https://api.mca.sh/merchant/v1/',
    test: 'https://mcashtestbed.appspot.com/merchant/v1/'
}

function mcashTimestamp() {
    return new Date().toISOString().replace(/T/, ' ').substr(0, 19)
}

Mcash.prototype.request = function(type, url, opts) {
    var fn = superagent[type.toLowerCase()]

    opts = lodash.extend({
        payload: null
    }, opts)

   var request = fn(exports.ENDPOINTS[this.opts.environment] + url)
    .buffer(true)
    .parse(superagent.parse.json)
    .set('Accept', 'application/vnd.mcash.api.merchant.v1+json')
    .set('X-Mcash-Merchant', this.opts.merchantId)
    .set('X-Mcash-User', this.opts.user)

    if (this.opts.environment == 'test') {
        request = request.set('X-Testbed-Token', this.opts.testbedToken)
    }

    if (this.opts.secret) {
        request = request
        .set('Authorization', 'SECRET ' + this.opts.secret)
    } else if (this.opts.privateKey) {
        var payload = opts.payload ? JSON.stringify(opts.payload) : ''
        debug('payload (post data) is: %s', payload || '<none>')
        var digest = crypto.createHash('sha256').update(payload).digest('base64')

        request = request
        .set('X-Mcash-Timestamp', mcashTimestamp())
        .set('X-Mcash-Content-Digest', 'SHA256=' + digest)

        var parsedUrl = parseUrl(request.url)

        var concat = format('%s|%s|%s',
            request.req.method,
            request.url,
            Object.keys(request.req._headers).filter(function(key) {
                return !!key.match(/^x-mcash-/)
            }).sort().reduce(function(p, key) {
                return format('%s%s%s=%s',
                    p,
                    p.length ? '&' : '',
                    key.toUpperCase(),
                    request.req._headers[key])
            }, ''))

        var sign = crypto.createSign('RSA-SHA256')
        .update(concat)
        .sign(this.opts.privateKey, 'base64')

        if (!sign) {
            throw new Error('Unable to sign. Bad private key?')
        }

        debug('will sign with private key:\n%s', concat)

        debug('signature:\n%s', sign)

        request = request
        .set('Authorization', 'RSA-SHA256 ' + sign)
    }

    if (opts.payload) {
        request = request.send(opts.payload)
    }

    debug('all headers, for debugging:\n%s', Object.keys(request.req._headers).map(function(key) {
        return '\t' + key + ': ' + request.req._headers[key]
    }).join('\n'))

    return request
}

exports.handler = require('./handler')
<|endoftext|>