<commit_before>/**
 * @author Chetan Sachdev <mail@chetansachdev.com>
 * @listens atpreview:show
 */
EkstepEditor.basePlugin.extend({
    /**
     *   @member type {String} plugin title
     *   @memberof ecmlpreview
     *
     */
    type: 'preview',
    canvasOffset: undefined,
    /**
     *   @member previewURL {String} reverse proxy URL
     *   @memberof ecmlpreview
     *
     */
    previewURL: 'https://dev.ekstep.in/assets/public/preview/preview.html?webview=true',
    /**
     *   @member contentBody {Object} content body for preview
     *   @memberof Preview
     *
     */
    contentBody: undefined,
    /**
     *   registers events
     *   @memberof preview
     *
     */
    initialize: function() {
        var instance = this;
        EkstepEditorAPI.addEventListener("atpreview:show", instance.initPreview, instance);
        setTimeout(function() {
            Mousetrap.bind('ctrl+enter', function() {
                alert("Command + Enter");
                instance.initPreview(undefined, instance);
            });
            Mousetrap.bind('ctrl+shift+enter', function() {
                alert("Command + Shift + Enter");
                // instance.initPreview(undefined, instance);
            });
            alert("Events registered!!");
        }, 1000);

        this.canvasOffset = EkstepEditorAPI.jQuery('#canvas').offset();

        var templatePath = EkstepEditorAPI.resolvePluginResource(this.manifest.id, this.manifest.ver, "editor/ecmlpreview.html");
        EkstepEditorAPI.getService('popup').loadNgModules(templatePath);
    },
    /**
     *
     *   @param event {Object} event object from event bus.
     *   @param data {Object} ecml
     *   @memberof preview
     */
    initPreview: function(event, data) {
        var instance = this;
        instance.contentBody = data.contentBody;
        if (data.currentStage) {
            this.contentBody.theme.startStage = EkstepEditorAPI.getCurrentStage().id;
        }
        this.showPreview();
    },
    /**
     *   @memberof preview
     */
    showPreview: function() {
        console.log(this.previewURL);
        var instance = this;
        var contentService = EkstepEditorAPI.getService('content');
        var meta = EkstepEditorAPI.getService('content').getContentMeta(EkstepEditorAPI.getContext('contentId'));
        var modalController = function($scope) {
            $scope.$on('ngDialog.opened', function() {

                var p = 50;
                var q = 139;
                var changePosition = function(x,y) {
                  p = p+x;
                  q = q+y;
                  jQuery('.child').offset({
                    left: p,
                    top: q
                  });
                }

                var previewContentIframe = EkstepEditorAPI.jQuery('#previewContentIframe')[0];
                previewContentIframe.src = instance.previewURL;
                meta.contentMeta = _.isUndefined(meta.contentMeta) ? null : meta.contentMeta;
                changePosition(instance.canvasOffset.left, instance.canvasOffset.top);
                previewContentIframe.onload = function() {
                    previewContentIframe.contentWindow.setContentData(meta.contentMeta, instance.contentBody, { "showStartPage": true, "showEndPage": true });
                };
            });
        };

        EkstepEditorAPI.getService('popup').open({
            template: 'partials.ecmlpreview.html',
            controller: ['$scope', modalController],
            showClose: false,
            width: 900,
            className: 'ngdialog-theme-plain'
        });

    }
});

//# sourceURL=previewplugin.js
<commit_msg>Added option to handle both command + enter and ctrl + enter<commit_after>/**
 * @author Chetan Sachdev <mail@chetansachdev.com>
 * @listens atpreview:show
 */
EkstepEditor.basePlugin.extend({
    /**
     *   @member type {String} plugin title
     *   @memberof ecmlpreview
     *
     */
    type: 'preview',
    canvasOffset: undefined,
    /**
     *   @member previewURL {String} reverse proxy URL
     *   @memberof ecmlpreview
     *
     */
    previewURL: 'https://dev.ekstep.in/assets/public/preview/preview.html?webview=true',
    /**
     *   @member contentBody {Object} content body for preview
     *   @memberof Preview
     *
     */
    contentBody: undefined,
    /**
     *   registers events
     *   @memberof preview
     *
     */
    initialize: function() {
        var instance = this;
        EkstepEditorAPI.addEventListener("atpreview:show", instance.initPreview, instance);
        setTimeout(function() {
            Mousetrap.bind(['ctrl+enter', 'control+enter'], function() {
                // alert("Command + Enter");
                instance.initPreview(undefined, instance);
            });
            Mousetrap.bind(['ctrl+shift+enter', 'control+shift+enter'], function() {
                // instance.initPreview(undefined, instance);
            });
            alert("Events registered!!");
        }, 1000);

        this.canvasOffset = EkstepEditorAPI.jQuery('#canvas').offset();

        var templatePath = EkstepEditorAPI.resolvePluginResource(this.manifest.id, this.manifest.ver, "editor/ecmlpreview.html");
        EkstepEditorAPI.getService('popup').loadNgModules(templatePath);
    },
    /**
     *
     *   @param event {Object} event object from event bus.
     *   @param data {Object} ecml
     *   @memberof preview
     */
    initPreview: function(event, data) {
        var instance = this;
        instance.contentBody = data.contentBody;
        if (data.currentStage) {
            this.contentBody.theme.startStage = EkstepEditorAPI.getCurrentStage().id;
        }
        this.showPreview();
    },
    /**
     *   @memberof preview
     */
    showPreview: function() {
        console.log(this.previewURL);
        var instance = this;
        var contentService = EkstepEditorAPI.getService('content');
        var meta = EkstepEditorAPI.getService('content').getContentMeta(EkstepEditorAPI.getContext('contentId'));
        var modalController = function($scope) {
            $scope.$on('ngDialog.opened', function() {

                var p = 50;
                var q = 139;
                var changePosition = function(x,y) {
                  p = p+x;
                  q = q+y;
                  jQuery('.child').offset({
                    left: p,
                    top: q
                  });
                }

                var previewContentIframe = EkstepEditorAPI.jQuery('#previewContentIframe')[0];
                previewContentIframe.src = instance.previewURL;
                meta.contentMeta = _.isUndefined(meta.contentMeta) ? null : meta.contentMeta;
                changePosition(instance.canvasOffset.left, instance.canvasOffset.top);
                previewContentIframe.onload = function() {
                    previewContentIframe.contentWindow.setContentData(meta.contentMeta, instance.contentBody, { "showStartPage": true, "showEndPage": true });
                };
            });
        };

        EkstepEditorAPI.getService('popup').open({
            template: 'partials.ecmlpreview.html',
            controller: ['$scope', modalController],
            showClose: false,
            width: 900,
            className: 'ngdialog-theme-plain'
        });

    }
});

//# sourceURL=previewplugin.js
<|endoftext|>


<commit_before>
var createRequest = function(iq) {
	iq = typeof iq.tree == "function" ? iq.tree() : iq;
	var req = new Strophe.Request(iq, function() {});
	req.getResponse = function() {
		var env = new Strophe.Builder('env', {type: 'mock'}).tree();
		env.appendChild(iq);
		return env;
	};
	return req;
};

function clear(stanza) {
	if (stanza.tree) {
		stanza = stanza.tree();
	}
	if (stanza.removeAttribute) {
		stanza.removeAttribute('id');
		return Strophe.serialize(stanza);
	}
	return stanza;
}
var stanzas = {
	info: {
		request: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>",
		request_with_node: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'/></iq>",
		response: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info'><identity name='strophe'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='http://jabber.org/protocol/disco#items'/></query></iq>",
		response_with_node: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'><identity>aNode</identity><feature var='a'/><feature var='b'/></query></iq>",
		response_not_found: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'><error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></query></iq>"
	},
	items: {
		request: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>",
		request_with_node: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items' node='aNode'/></iq>",
		response: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>",
		response_with_node: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items' node='aNode'><item name='aNother' node='aNotherNode'/></query></iq>"
	},
	commands: {
		response_not_found: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='http://jabber.org/protocol/commands'><error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></query></iq>",
		execute: "<iq to='n@d/r' type='set' xmlns='jabber:client'><command xmlns='http://jabber.org/protocol/commands' node='aCmd' action='execute'/></iq>"
	}
};
<commit_msg>helpers<commit_after>function connect() {
	var c = new Strophe.Connection('http://localhost/xmpp-httpbind');
	c.connect('asdf@psi/strophe','asdf');
	return c;
}
function epic() {
	var html = "

Epic

";
	jQuery(html).epic({conn: connect()}).appendTo('body');
}

var createRequest = function(iq) {
	iq = typeof iq.tree == "function" ? iq.tree() : iq;
	var req = new Strophe.Request(iq, function() {});
	req.getResponse = function() {
		var env = new Strophe.Builder('env', {type: 'mock'}).tree();
		env.appendChild(iq);
		return env;
	};
	return req;
};

function clear(stanza) {
	if (stanza.tree) {
		stanza = stanza.tree();
	}
	if (stanza.removeAttribute) {
		stanza.removeAttribute('id');
		return Strophe.serialize(stanza);
	}
	return stanza;
}
var stanzas = {
	info: {
		request: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>",
		request_with_node: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'/></iq>",
		response: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info'><identity name='strophe'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='http://jabber.org/protocol/disco#items'/></query></iq>",
		response_with_node: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'><identity>aNode</identity><feature var='a'/><feature var='b'/></query></iq>",
		response_not_found: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='aNode'><error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></query></iq>"
	},
	items: {
		request: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>",
		request_with_node: "<iq to='n@d/r' type='get' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items' node='aNode'/></iq>",
		response: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items'/></iq>",
		response_with_node: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#items' node='aNode'><item name='aNother' node='aNotherNode'/></query></iq>"
	},
	commands: {
		response_not_found: "<iq to='null' type='result' xmlns='jabber:client'><query xmlns='http://jabber.org/protocol/disco#info' node='http://jabber.org/protocol/commands'><error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></query></iq>",
		execute: "<iq to='n@d/r' type='set' xmlns='jabber:client'><command xmlns='http://jabber.org/protocol/commands' node='aCmd' action='execute'/></iq>"
	}
};
<|endoftext|>


<commit_before>
var settings = require('./settings');
var createClass = require('./functions/createClass');
var arrayIndexOf = require('./functions/arrayIndexOf');
var callModelAsFunction = require('./functions/callModelAsFunction');
var defaultInsertBefore = require('./functions/nodeMan').insertBefore;
var defaultRemoveChild = require('./functions/nodeMan').removeChild;

var CollectionBinder = createClass(
/** @lends Binder.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function(options)
	{
		this.collection = null;
		this.keyPath = options.keyPath;
		this.collectionArgs = options.collectionArgs;
		this.view = options.view;
		this.elementTemplate = null;
		this.boundViews = null;
		this.anchor = null;
		this.viewTemplate = null;
		this.animate = options.animate;
	},

	/**
	 * Renders "bound" views.
	 * This method generates DOM elements corresponding to each item in the bound collection and
	 * creates the bindingView for each element. If the collection changes, it reflects those changes
	 * automatically in real time.
	 *
	 * @private
	 */
	renderBoundViews: function()
	{
		this.anchor = this.view.env.document.createTextNode('');
		var el = this.view.element;

		if(el.parentNode)
		{
			el.parentNode.insertBefore(this.anchor, el.nextSibling);
			el.parentNode.removeChild(el);
		}

		this.boundViews = [];

		// Boundview options:
		this.boundViewName = this.view.element.getAttribute(settings.DATA_VIEW_ATTR);
		var opt = this.view.element.getAttribute(settings.DATA_OPTIONS_ATTR);

		this.boundViewOptions = opt ? JSON.parse(opt) : {};
		this.boundViewOptions.parentView = this.view;
		this.boundViewOptions.serviceContainer = this.view.serviceContainer;
		this.boundViewOptions.env = this.view.env;
		this.boundViewOptions.isBoundView = true;

		this.refreshBoundViews();
	},

	/**
	 * Destroys previously bound views.
	 *
	 * @private
	 */
	destroyBoundViews: function()
	{
		var boundView, i, l;

		// Destroy boundviews
		if(this.boundViews !== null)
		{
			for(i = 0, l = this.boundViews.length; i < l; i++)
			{
				boundView = this.boundViews[i];
				boundView.destroyAll();
				if(boundView.element && boundView.element.parentNode) boundView.element.parentNode.removeChild(boundView.element);
			}
			this.boundViews = null;
		}

		if(this.anchor)
		{
			if(this.anchor.parentNode)
			{
				this.anchor.parentNode.insertBefore(this.view.element, this.anchor.nextSibling);
				this.anchor.parentNode.removeChild(this.anchor);
			}
			this.anchor = null;
		}
		if(this.elementTemplate)
		{
			if(this.elementTemplate.parentNode)
			{
				this.elementTemplate.parentNode.removeChild(this.elementTemplate);
			}
			this.elementTemplate = null;
		}
		this.viewTemplate = null;
		if(this.collection) this.collection = null;
	},

	refreshAll: function()
	{
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshAll();
		}
	},

	/**
	 * Updates bound views when collection changes.
	 *
	 * @private
	 * @param {Object} event An event triggered by collection change
	 */
	refreshBoundViews: function(event)
	{
		this.refreshBoundViewsAll();
	},

	rebindCollection: function()
	{
		this.cursor = this.view.getCursor(this.keyPath);
		this.collection = this.cursor.get();

		if(typeof this.collection === 'function')
		{
			this.collection = callModelAsFunction.call(this.view, this.collection, this.collectionArgs);
		}
		if(!(this.collection instanceof Array)) this.collection = [];
	},

	/**
	 * Updates bound views when collection changes on other events.
	 *
	 * @private
	 */
	refreshBoundViewsAll: function()
	{
		var boundView, i, l, el, a;
		var lastView, lastChild, parentNode, item;
		var insertBefore = defaultInsertBefore;
		var removeChild = defaultRemoveChild;
		var oldCollection = this.collection;

		if(this.animate)
		{
			insertBefore = this.view.scope[this.animate]['insert'];
			removeChild = this.view.scope[this.animate]['remove'];
		}

		this.rebindCollection();

		// This will speed up rendering but requires strictly immutable collections
		// if(oldCollection === this.collection) return;

		if(this.boundViews === null) this.boundViews = [];

		if(this.boundViews.length === 0)
		{
			// Fast initial rendering:
			l = this.collection.length;
			if(l > 0)
			{
				a = this.collection;
				lastChild = this.anchor;
				if(this.anchor && this.anchor.parentNode)
				{
					parentNode = this.anchor.parentNode;
					for(i = 0; i < l; i++)
					{
						boundView = this.createBoundView(a[i]);
						el = boundView.element;
						insertBefore(parentNode, lastChild.nextSibling, el);
						boundView.setBindingIndex(i);
						lastChild = el;
					}

					for(i = 0; i < l; i++)
					{
						this.boundViews[i].runAll();
						this.boundViews[i].afterRunAll();
					}
				}
			}
		}
		else
		{
			// Diff based rendering:
			var newBoundViews = [];
			var recycledViews = [];

			// Merge old and new bound view arrays:
			var tempBoundViews = [];
			var newCollection = this.collection;
			for(i = 0, l = Math.max(oldCollection.length, newCollection.length); i < l; i++)
			{
				if(oldCollection[i] && arrayIndexOf(newCollection, oldCollection[i]) === -1)
				{
					// Item is in the old collection but not in the new one
					boundView = this.boundViews[i];
					if(this.animate)
					{
						tempBoundViews.push(boundView);
						lastView = boundView;
					}
					else lastView = null;
					recycledViews.push(boundView);
				}
				if(newCollection[i])
				{
					// Item is in the new collection
					var oldIndex = arrayIndexOf(oldCollection, newCollection[i]);
					if(oldIndex !== -1)
					{
						// Item is already rendered, reuse its view
						boundView = this.boundViews[oldIndex];
						tempBoundViews.push(boundView);
						boundView.scope['*'] = this.cursor.refine([i]);
						if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
						boundView.setBindingIndex(i);
						newBoundViews.push(boundView);
						lastView = boundView;
					}
					else
					{
						// Item is new, create new binding view
						if(this.animate || recycledViews.length === 0)
						{
							boundView = this.createBoundView(newCollection[i]);
							boundView.scope['*'] = this.cursor.refine([i]);
							if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
							boundView.setBindingIndex(i);
							boundView.runAll();
							boundView.afterRunAll();
						}
						else
						{
							boundView = recycledViews.shift();
							boundView.scope['*'] = this.cursor.refine([i]);
							if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
							boundView.setBindingIndex(i);
						}
						tempBoundViews.push(boundView);
						newBoundViews.push(boundView);
						lastView = null;
					}
				}
			}

			if(lastView)
			{
				// Reordering elements from the last one:
				lastChild = lastView.element;
				i = tempBoundViews.length - 1;

				el = tempBoundViews[i].element;
				if(el !== lastChild && lastChild.parentNode && lastChild.parentNode.nodeType === 1 && el !== lastChild.nextSibling)
				{
					insertBefore(lastChild.parentNode, lastChild.nextSibling, el);
					lastChild = el;
				}

				for(; i >= 0; i--)
				{
					el = tempBoundViews[i].element;
					var nextSibling = el.nextSibling;
					if(el !== lastChild && nextSibling !== lastChild && lastChild.parentNode && lastChild.parentNode.nodeType === 1)
					{
						insertBefore(lastChild.parentNode, lastChild, el);
					}
					lastChild = el;
					tempBoundViews[i].refreshIndexedBinders(true);
				}
			}
			else
			{
				// Add elements after anchor text node:
				lastChild = this.anchor;
				if(this.anchor.parentNode)
				{
					parentNode = this.anchor.parentNode;
				}
				for(i = 0, l = tempBoundViews.length; i < l; i++)
				{
					el = tempBoundViews[i].element;

					if(el !== lastChild.nextSibling)
					{
						insertBefore(parentNode, lastChild.nextSibling, el);
					}
					tempBoundViews[i].refreshIndexedBinders(true);
					lastChild = el;
				}
			}
			this.boundViews = newBoundViews;

			// Remove old views:
			for(i = 0, l = recycledViews.length; i < l; i++)
			{
				var viewToRemove = recycledViews[i];
				if(viewToRemove.element && viewToRemove.element.parentNode)
				{
					removeNodeAsync(viewToRemove, removeChild);
				}
			}
		}
	},

	/**
	 * Refreshes view indices when the collection changes
	 *
	 * @private
	 * @param  {nubmer} from Render index at which reindexing starts
	 * @param  {number} to   Render index at which reindexing ends
	 */
	reindexBoundviews: function(from, to)
	{
		if(!from) from = 0;
		if(!to || to > this.boundViews.length) to = this.boundViews.length;

		// Reindex subsequent boundviews:
		for(var i = from; i < to; i++)
		{
			this.boundViews[i].setBindingIndex(i);
			this.boundViews[i].refreshBinders(true);
		}
	},

	/**
	 * Creates a new bound view for item in collection
	 *
	 * @private
	 * @param  {Object} item Item for data-binding
	 * @param  {number} i 		Binding index
	 * @return {View} 		created view
	 */
	createBoundView: function(item)
	{
		var boundView, element, i;

		if(!this.viewTemplate)
		{
			element = this.view.element.cloneNode(true);

			this.boundViewOptions.element = element;

			boundView = new this.view.constructor(this.boundViewOptions);

			boundView._collectionBinder = null;
			boundView._modelBindersMap = this.view._modelBindersMap.clone();

			this.boundViews.push(boundView);
			i = this.boundViews.length - 1;

			boundView.scope['*'] = this.cursor.refine([i]);
			if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];

			boundView.setBindingIndex(i);

			boundView.renderAll();

			this.viewTemplate = boundView.clone();
			this.elementTemplate = element.cloneNode(true);
		}
		else
		{
			element = this.elementTemplate.cloneNode(true);
			boundView = this.viewTemplate.clone();
			boundView.setParentView(this.view);

			this.boundViews.push(boundView);
			i = this.boundViews.length - 1;

			boundView.scope['*'] = this.cursor.refine([i]);
			if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];

			boundView.setBindingIndex(i);
			boundView.rebindElement(element);
		}

		element.setAttribute(settings.DATA_RENDERED_ATTR, true);

		boundView._itemAlias = this.view._itemAlias;
		boundView._modelBindersMap.setView(boundView);

		return boundView;
	},

	refreshBinders: function(force)
	{
		this.refreshBoundViews();
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshBinders(force);
		}
	},

	refreshIndexedBinders: function()
	{
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshIndexedBinders();
		}
	},

	getCollectionIndex: function(item)
	{
		if(this.collection instanceof Array)
		{
			return arrayIndexOf(this.collection, item);
		}
		else return -1;
	}
});

function removeNodeAsync(view, removeFn)
{
	view.suspendAll();
	removeFn(view.element.parentNode, view.element, function()
	{
		view.destroyAll();
	});
}

module.exports = CollectionBinder;
<commit_msg>fix(CollectionBinder): run view before calling the insert function on initial rendering<commit_after>
var settings = require('./settings');
var createClass = require('./functions/createClass');
var arrayIndexOf = require('./functions/arrayIndexOf');
var callModelAsFunction = require('./functions/callModelAsFunction');
var defaultInsertBefore = require('./functions/nodeMan').insertBefore;
var defaultRemoveChild = require('./functions/nodeMan').removeChild;

var CollectionBinder = createClass(
/** @lends Binder.prototype */
{
	/**
	 * @constructs
	 */
	constructor: function(options)
	{
		this.collection = null;
		this.keyPath = options.keyPath;
		this.collectionArgs = options.collectionArgs;
		this.view = options.view;
		this.elementTemplate = null;
		this.boundViews = null;
		this.anchor = null;
		this.viewTemplate = null;
		this.animate = options.animate;
	},

	/**
	 * Renders "bound" views.
	 * This method generates DOM elements corresponding to each item in the bound collection and
	 * creates the bindingView for each element. If the collection changes, it reflects those changes
	 * automatically in real time.
	 *
	 * @private
	 */
	renderBoundViews: function()
	{
		this.anchor = this.view.env.document.createTextNode('');
		var el = this.view.element;

		if(el.parentNode)
		{
			el.parentNode.insertBefore(this.anchor, el.nextSibling);
			el.parentNode.removeChild(el);
		}

		this.boundViews = [];

		// Boundview options:
		this.boundViewName = this.view.element.getAttribute(settings.DATA_VIEW_ATTR);
		var opt = this.view.element.getAttribute(settings.DATA_OPTIONS_ATTR);

		this.boundViewOptions = opt ? JSON.parse(opt) : {};
		this.boundViewOptions.parentView = this.view;
		this.boundViewOptions.serviceContainer = this.view.serviceContainer;
		this.boundViewOptions.env = this.view.env;
		this.boundViewOptions.isBoundView = true;

		this.refreshBoundViews();
	},

	/**
	 * Destroys previously bound views.
	 *
	 * @private
	 */
	destroyBoundViews: function()
	{
		var boundView, i, l;

		// Destroy boundviews
		if(this.boundViews !== null)
		{
			for(i = 0, l = this.boundViews.length; i < l; i++)
			{
				boundView = this.boundViews[i];
				boundView.destroyAll();
				if(boundView.element && boundView.element.parentNode) boundView.element.parentNode.removeChild(boundView.element);
			}
			this.boundViews = null;
		}

		if(this.anchor)
		{
			if(this.anchor.parentNode)
			{
				this.anchor.parentNode.insertBefore(this.view.element, this.anchor.nextSibling);
				this.anchor.parentNode.removeChild(this.anchor);
			}
			this.anchor = null;
		}
		if(this.elementTemplate)
		{
			if(this.elementTemplate.parentNode)
			{
				this.elementTemplate.parentNode.removeChild(this.elementTemplate);
			}
			this.elementTemplate = null;
		}
		this.viewTemplate = null;
		if(this.collection) this.collection = null;
	},

	refreshAll: function()
	{
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshAll();
		}
	},

	/**
	 * Updates bound views when collection changes.
	 *
	 * @private
	 * @param {Object} event An event triggered by collection change
	 */
	refreshBoundViews: function(event)
	{
		this.refreshBoundViewsAll();
	},

	rebindCollection: function()
	{
		this.cursor = this.view.getCursor(this.keyPath);
		this.collection = this.cursor.get();

		if(typeof this.collection === 'function')
		{
			this.collection = callModelAsFunction.call(this.view, this.collection, this.collectionArgs);
		}
		if(!(this.collection instanceof Array)) this.collection = [];
	},

	/**
	 * Updates bound views when collection changes on other events.
	 *
	 * @private
	 */
	refreshBoundViewsAll: function()
	{
		var boundView, i, l, el, a;
		var lastView, lastChild, parentNode, item;
		var insertBefore = defaultInsertBefore;
		var removeChild = defaultRemoveChild;
		var oldCollection = this.collection;

		if(this.animate)
		{
			insertBefore = this.view.scope[this.animate]['insert'];
			removeChild = this.view.scope[this.animate]['remove'];
		}

		this.rebindCollection();

		// This will speed up rendering but requires strictly immutable collections
		// if(oldCollection === this.collection) return;

		if(this.boundViews === null) this.boundViews = [];

		if(this.boundViews.length === 0)
		{
			// Fast initial rendering:
			l = this.collection.length;
			if(l > 0)
			{
				a = this.collection;
				lastChild = this.anchor;
				if(this.anchor && this.anchor.parentNode)
				{
					parentNode = this.anchor.parentNode;
					for(i = 0; i < l; i++)
					{
						boundView = this.createBoundView(a[i]);
						this.boundViews[i].runAll();
						this.boundViews[i].afterRunAll();
						el = boundView.element;
						insertBefore(parentNode, lastChild.nextSibling, el);
						lastChild = el;
					}
				}
			}
		}
		else
		{
			// Diff based rendering:
			var newBoundViews = [];
			var recycledViews = [];

			// Merge old and new bound view arrays:
			var tempBoundViews = [];
			var newCollection = this.collection;
			for(i = 0, l = Math.max(oldCollection.length, newCollection.length); i < l; i++)
			{
				if(oldCollection[i] && arrayIndexOf(newCollection, oldCollection[i]) === -1)
				{
					// Item is in the old collection but not in the new one
					boundView = this.boundViews[i];
					if(this.animate)
					{
						tempBoundViews.push(boundView);
						lastView = boundView;
					}
					else lastView = null;
					recycledViews.push(boundView);
				}
				if(newCollection[i])
				{
					// Item is in the new collection
					var oldIndex = arrayIndexOf(oldCollection, newCollection[i]);
					if(oldIndex !== -1)
					{
						// Item is already rendered, reuse its view
						boundView = this.boundViews[oldIndex];
						tempBoundViews.push(boundView);
						boundView.scope['*'] = this.cursor.refine([i]);
						if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
						boundView.setBindingIndex(i);
						newBoundViews.push(boundView);
						lastView = boundView;
					}
					else
					{
						// Item is new, create new binding view
						if(this.animate || recycledViews.length === 0)
						{
							boundView = this.createBoundView(newCollection[i]);
							boundView.scope['*'] = this.cursor.refine([i]);
							if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
							boundView.setBindingIndex(i);
							boundView.runAll();
							boundView.afterRunAll();
						}
						else
						{
							boundView = recycledViews.shift();
							boundView.scope['*'] = this.cursor.refine([i]);
							if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];
							boundView.setBindingIndex(i);
						}
						tempBoundViews.push(boundView);
						newBoundViews.push(boundView);
						lastView = null;
					}
				}
			}

			if(lastView)
			{
				// Reordering elements from the last one:
				lastChild = lastView.element;
				i = tempBoundViews.length - 1;

				el = tempBoundViews[i].element;
				if(el !== lastChild && lastChild.parentNode && lastChild.parentNode.nodeType === 1 && el !== lastChild.nextSibling)
				{
					insertBefore(lastChild.parentNode, lastChild.nextSibling, el);
					lastChild = el;
				}

				for(; i >= 0; i--)
				{
					el = tempBoundViews[i].element;
					var nextSibling = el.nextSibling;
					if(el !== lastChild && nextSibling !== lastChild && lastChild.parentNode && lastChild.parentNode.nodeType === 1)
					{
						insertBefore(lastChild.parentNode, lastChild, el);
					}
					lastChild = el;
					tempBoundViews[i].refreshIndexedBinders(true);
				}
			}
			else
			{
				// Add elements after anchor text node:
				lastChild = this.anchor;
				if(this.anchor.parentNode)
				{
					parentNode = this.anchor.parentNode;
				}
				for(i = 0, l = tempBoundViews.length; i < l; i++)
				{
					el = tempBoundViews[i].element;

					if(el !== lastChild.nextSibling)
					{
						insertBefore(parentNode, lastChild.nextSibling, el);
					}
					tempBoundViews[i].refreshIndexedBinders(true);
					lastChild = el;
				}
			}
			this.boundViews = newBoundViews;

			// Remove old views:
			for(i = 0, l = recycledViews.length; i < l; i++)
			{
				var viewToRemove = recycledViews[i];
				if(viewToRemove.element && viewToRemove.element.parentNode)
				{
					removeNodeAsync(viewToRemove, removeChild);
				}
			}
		}
	},

	/**
	 * Refreshes view indices when the collection changes
	 *
	 * @private
	 * @param  {nubmer} from Render index at which reindexing starts
	 * @param  {number} to   Render index at which reindexing ends
	 */
	reindexBoundviews: function(from, to)
	{
		if(!from) from = 0;
		if(!to || to > this.boundViews.length) to = this.boundViews.length;

		// Reindex subsequent boundviews:
		for(var i = from; i < to; i++)
		{
			this.boundViews[i].setBindingIndex(i);
			this.boundViews[i].refreshBinders(true);
		}
	},

	/**
	 * Creates a new bound view for item in collection
	 *
	 * @private
	 * @param  {Object} item Item for data-binding
	 * @param  {number} i 		Binding index
	 * @return {View} 		created view
	 */
	createBoundView: function(item)
	{
		var boundView, element, i;

		if(!this.viewTemplate)
		{
			element = this.view.element.cloneNode(true);

			this.boundViewOptions.element = element;

			boundView = new this.view.constructor(this.boundViewOptions);

			boundView._collectionBinder = null;
			boundView._modelBindersMap = this.view._modelBindersMap.clone();

			this.boundViews.push(boundView);
			i = this.boundViews.length - 1;

			boundView.scope['*'] = this.cursor.refine([i]);
			if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];

			boundView.setBindingIndex(i);

			boundView.renderAll();

			this.viewTemplate = boundView.clone();
			this.elementTemplate = element.cloneNode(true);
		}
		else
		{
			element = this.elementTemplate.cloneNode(true);
			boundView = this.viewTemplate.clone();
			boundView.setParentView(this.view);

			this.boundViews.push(boundView);
			i = this.boundViews.length - 1;

			boundView.scope['*'] = this.cursor.refine([i]);
			if(this.view._itemAlias) boundView.scope[this.view._itemAlias] = boundView.scope['*'];

			boundView.setBindingIndex(i);
			boundView.rebindElement(element);
		}

		element.setAttribute(settings.DATA_RENDERED_ATTR, true);

		boundView._itemAlias = this.view._itemAlias;
		boundView._modelBindersMap.setView(boundView);

		return boundView;
	},

	refreshBinders: function(force)
	{
		this.refreshBoundViews();
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshBinders(force);
		}
	},

	refreshIndexedBinders: function()
	{
		if(this.boundViews !== null)
		{
			for(var i = 0, l = this.boundViews.length; i < l; i++) this.boundViews[i].refreshIndexedBinders();
		}
	},

	getCollectionIndex: function(item)
	{
		if(this.collection instanceof Array)
		{
			return arrayIndexOf(this.collection, item);
		}
		else return -1;
	}
});

function removeNodeAsync(view, removeFn)
{
	view.suspendAll();
	removeFn(view.element.parentNode, view.element, function()
	{
		view.destroyAll();
	});
}

module.exports = CollectionBinder;
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

sap.ui.define(['jquery.sap.global'],
	function(jQuery) {
	"use strict";

	/**
	 *

	 * sap.m.Support shows the technical information for SAPUI5 Mobile Applications.
	 * This technical information includes
	 * 	* SAPUI5 Version
	 * 	* User Agent
	 * 	* Configurations (Bootstrap and Computed)
	 * 	* URI parameters
	 * 	* All loaded module names
	 *
	 * In order to show the device information, the user must follow the following gestures.
	 * 	1 - Hold two finger for 3 seconds minimum.
	 * 	2 - Tab with a third finger while holding the first two fingers.
	 *
	 * NOTE: This class is internal and all its functions must not be used by an application
	 *
	 * As sap.m.Support is a static class, a jQuery.sap.require("sap.m.Support");
	 * statement must be implicitly executed before the class is used.
	 *
	 *
	 * Enable Support:
	 * --------------------------------------------------
	 * //import library
	 * jQuery.sap.require("sap.m.Support");
	 *
	 * //By default after require, support is enabled but implicitly we can call
	 * sap.m.Support.on();
	 *
	 * Disable Support:
	 * --------------------------------------------------
	 * sap.m.Support.off();
	 *

	 *
	 * @author SAP SE
	 * @since 1.11.0
	 *
	 * @static
	 * @protected
	 * @name sap.m.Support
	 */
	var Support = (function($, document) {

		var dialog, startTime, isEventRegistered, lastTouchUID;
		var timeDiff = 0;
		var minHoldTime = 3000; // 3s(3000ms) two-finger hold time
		var holdFingersNumber = 2; // two-fingers hold
		var maxFingersAllowed = 3; // two-fingers hold + 1-finger tab
		var releasedFingersNumber = 1,
			oData = {},
			e2eTraceConst = {
				btnStart : "startE2ETrace",
				selLevel : "logLevelE2ETrace",
				taContent : "outputE2ETrace",
				infoText : "Ent-to-End trace is running in the background." +
							" Navigate to the URL that you would like to trace." +
							" The result of the trace will be shown in dialog after the trace is terminated.",
				infoDuration : 5000 // 5 sec.
			},
			controlIDs = {
				dvLoadedLibs 	: "LoadedLibs",
				dvLoadedModules : "LoadedModules"
		};

		// copied from core
		function line(buffer, right, border, label, content) {
			buffer.push("
", jQuery.sap.escapeHTML(label), "
");
			var ctnt = content;
			if ($.isFunction(content)) {
				ctnt = content(buffer) || "";
			}
			buffer.push($.sap.escapeHTML(ctnt));
			buffer.push("
");
		}

		// copied from core
		function multiline(buffer, right, border, label, content) {
			line(buffer, right, border, label, function(buffer) {
				buffer.push("

");
				$.each(content, function(i, v) {
					var val = "";
					if (v) {
						if (typeof (v) == "string" || typeof (v) == "boolean" || ($.isArray(v) && v.length == 1)) {
							val = v;
						} else if (($.isArray(v) || $.isPlainObject(v)) && window.JSON) {
							val = window.JSON.stringify(v);
						}
					}
					line(buffer, false, false, i, "" + val);
				});
				buffer.push("

");
			});
		}

		// copied from core
		function getTechnicalContent() {
			var html,
				oConfig = sap.ui.getCore().getConfiguration();
			var oLoadedLibs = {};
			jQuery.each(sap.ui.getCore().getLoadedLibraries(), function(sName, oLibInfo) {
				oLoadedLibs[sName] = oLibInfo.version;
			});
			oData = {
					version: sap.ui.version,
					build: sap.ui.buildinfo.buildtime,
					change: sap.ui.buildinfo.lastchange,
					useragent: navigator.userAgent,
					docmode: document.documentMode ||  "",
					debug: $.sap.debug(),
					bootconfig: window["sap-ui-config"] || {},
					modules: $.sap.getAllDeclaredModules(),
					loadedlibs: oLoadedLibs,
					uriparams: $.sap.getUriParameters().mParams,
					appurl: window.location.href,
					config: {
						theme: oConfig.getTheme(),
						language: oConfig.getLanguage(),
						formatLocale: oConfig.getFormatLocale(),
						accessibility: "" + oConfig.getAccessibility(),
						animation: "" + oConfig.getAnimation(),
						rtl: "" + oConfig.getRTL(),
						debug: "" + oConfig.getDebug(),
						inspect: "" + oConfig.getInspect(),
						originInfo: "" + oConfig.getOriginInfo(),
						noDuplicateIds: "" + oConfig.getNoDuplicateIds()
					}
			};

			html = ["

"];
			line(html, true, true, "SAPUI5 Version", function(buffer) {
				buffer.push(oData.version, " (built at ", oData.build, ", last change ", oData.change, ")");
			});
			line(html, true, true, "User Agent", function(buffer) {
				buffer.push(oData.useragent, (oData.docmode ? ", Document Mode '" + oData.docmode + "'" : ""));
			});
			line(html, true, true, "Debug Sources", function(buffer) {
				buffer.push((oData.debug ? "ON" : "OFF") );
			});
			line(html, true, true, "Application", oData.appurl);
			multiline(html, true, true, "Configuration (bootstrap)", oData.bootconfig);
			multiline(html, true, true, "Configuration (computed)", oData.config);
			multiline(html, true, true, "URI Parameters", oData.uriparams);
			// e2e trace section
			line(html, true, true, "End-to-End Trace", function(buffer) {
				buffer.push("Trace Level:",
					"<select id='" + buildControlId(e2eTraceConst.selLevel) + "' class='sapUiSupportTxtFld' >",
						"<option value='low'>LOW</option>",
						"<option value='medium' selected>MEDIUM</option>",
						"<option value='high'>HIGH</option>",
					"</select>"
				);
				buffer.push("<button id='" + buildControlId(e2eTraceConst.btnStart) + "' class='sapUiSupportBtn'>Start</button>");
				buffer.push("
");
				buffer.push("XML Output:");
				buffer.push("<textarea id='" + buildControlId(e2eTraceConst.taContent) +  "' class='sapUiSupportTxtArea sapUiSelectable' readonly ></textarea>");
				buffer.push("
");
			});

			line(html, true, true, "Loaded Libraries", function(buffer) {
				buffer.push("
");
				$.each(oData.loadedlibs, function(i, v) {
		            if (v && (typeof (v) === "string" || typeof (v) === "boolean")) {
	                     buffer.push("
", i + " " + v, "
");
		            }
				});
				buffer.push("
");
			});

			line(html, true, true, "Loaded Modules", function(buffer) {
				buffer.push("
");
			});

			html.push("

");

			return new sap.ui.core.HTML({
				content : html.join("").replace(/\{/g, "{").replace(/\}/g, "}")
			});
		}

		function buildControlId(controlId) {
			return dialog.getId() + "-" + controlId;
		}

		function fillPanelContent(panelId, arContent) {

			var panelHeader = "Modules";
			var libsCount = 0, arDivContent = [];

			libsCount = arContent.length;
			$.each(arContent.sort(), function(i, module) {
				arDivContent.push(new sap.m.Label({ text : " - " + module }).addStyleClass("sapUiSupportPnlLbl"));
			});

			// insert content into div placeholders
			var objPanel = new sap.m.Panel({
				expandable : true,
				expanded : false,
				headerToolbar : new sap.m.Toolbar({
					design : sap.m.ToolbarDesign.Transparent,
					content : [new sap.m.Label({
						text : panelHeader + " (" + libsCount + ")",
						design : sap.m.LabelDesign.Bold
					})]
				}),
				content : arDivContent
			});

			objPanel.placeAt( buildControlId(panelId), "only");
		}

		// setup dialog elements and bind some events
		function setupDialog() {
			// setup e2e values as log level and content
			if (dialog.traceXml) {
				dialog.$(e2eTraceConst.taContent).text(dialog.traceXml);
			}
			if (dialog.e2eLogLevel) {
				dialog.$(e2eTraceConst.selLevel).val(dialog.e2eLogLevel);
			}

			fillPanelContent(controlIDs.dvLoadedModules, oData.modules);

			// bind button Start event
			dialog.$(e2eTraceConst.btnStart).one("tap", function() {

				dialog.e2eLogLevel = dialog.$(e2eTraceConst.selLevel).val();
				dialog.$(e2eTraceConst.btnStart).addClass("sapUiSupportRunningTrace").text("Running...");
				dialog.traceXml = "";
				dialog.$(e2eTraceConst.taContent).text("");

				sap.ui.core.support.trace.E2eTraceLib.start(dialog.e2eLogLevel, function(traceXml) {
					dialog.traceXml = traceXml;
				});

				// show info message about the E2E trace activation
				sap.m.MessageToast.show(e2eTraceConst.infoText, {duration: e2eTraceConst.infoDuration});

				//close the dialog, but keep it for later use
				dialog.close();
			});
		}

		// get or create dialog instance and return
		function getDialog() {
			if (dialog) {
				return dialog;
			}

			$.sap.require("sap.m.Dialog");
			$.sap.require("sap.m.Button");
			$.sap.require("sap.ui.core.HTML");
			$.sap.require("sap.m.MessageToast");
			$.sap.require("sap.ui.core.support.trace.E2eTraceLib");

			dialog = new sap.m.Dialog({
				title : "Technical Information",
				horizontalScrolling: true,
				verticalScrolling: true,
				stretch: jQuery.device.is.phone,
				leftButton : new sap.m.Button({
					text : "Close",
					press : function() {
						dialog.close();
					}
				}),
				afterOpen : function() {
					Support.off();
				},
				afterClose : function() {
					Support.on();
				}
			}).addStyleClass("sapMSupport");

			return dialog;
		}

		//function is triggered when a touch is detected
		function onTouchStart(oEvent) {
			if (oEvent.touches) {
				var currentTouches = oEvent.touches.length;

				if (currentTouches > maxFingersAllowed) {
					document.removeEventListener('touchend', onTouchEnd);
					return;
				}

				switch (currentTouches) {

					case holdFingersNumber:
						startTime = Date.now();
						document.addEventListener('touchend', onTouchEnd);
						break;

					case maxFingersAllowed:
						if (startTime) {
							timeDiff = Date.now() - startTime;
							lastTouchUID = oEvent.touches[currentTouches - 1].identifier;
						}
						break;
				}
			}
		}

		//function is triggered when a touch is removed e.g. the user’s finger is removed from the touchscreen.
		function onTouchEnd(oEvent) {
			document.removeEventListener('touchend', onTouchEnd);

			// Check if two fingers are holded for 3 seconds or more and after that it`s tapped with a third finger
			if (timeDiff > minHoldTime
					&& oEvent.touches.length === holdFingersNumber
					&& oEvent.changedTouches.length === releasedFingersNumber
					&& oEvent.changedTouches[0].identifier === lastTouchUID) {

						timeDiff = 0;
						startTime = 0;
						show();
			}
		}

		function show() {
			var container = getDialog();
			container.removeAllAggregation("content");
			container.addAggregation("content", getTechnicalContent());
			dialog.open();
			setupDialog();
		}

		return ({
			/**
			 * Enables support.
			 *
			 * @returns {sap.m.Support} this to allow method chaining
			 * @protected
			 * @name sap.m.Support.on
			 * @function
			 */
			on : function() {
				if (!isEventRegistered && "ontouchstart" in document) {
					isEventRegistered = true;
					document.addEventListener("touchstart", onTouchStart);
				}
				return this;
			},

			/**
			 * Disables support.
			 *
			 * @returns {sap.m.Support} this to allow method chaining
			 * @protected
			 * @name sap.m.Support.off
			 * @function
			 */
			off : function() {
				if (isEventRegistered) {
					isEventRegistered = false;
					document.removeEventListener("touchstart", onTouchStart);
				}
				return this;
			},

			open : function() {
				show();
			},

			/**
			 * Returns if event is registered or not.
			 *
			 * @returns {boolean}
			 * @protected
			 * @name sap.m.Support.isEventRegistered
			 * @function
			 */
			isEventRegistered : function() {
				return isEventRegistered;
			}
		}).on();

	}(jQuery, document));

	return Support;

}, /* bExport= */ true);
<commit_msg>[INTERNAL] sap.m.Support: Use ToolsAPI - common base code for support tools<commit_after>/*!
 * ${copyright}
 */

sap.ui.define(['jquery.sap.global', 'sap/ui/core/support/ToolsAPI'],
	function(jQuery, ToolsAPI) {
	"use strict";

	/**
	 *

	 * sap.m.Support shows the technical information for SAPUI5 Mobile Applications.
	 * This technical information includes
	 * 	* SAPUI5 Version
	 * 	* User Agent
	 * 	* Configurations (Bootstrap and Computed)
	 * 	* URI parameters
	 * 	* All loaded module names
	 *
	 * In order to show the device information, the user must follow the following gestures.
	 * 	1 - Hold two finger for 3 seconds minimum.
	 * 	2 - Tab with a third finger while holding the first two fingers.
	 *
	 * NOTE: This class is internal and all its functions must not be used by an application
	 *
	 * As sap.m.Support is a static class, a jQuery.sap.require("sap.m.Support");
	 * statement must be implicitly executed before the class is used.
	 *
	 *
	 * Enable Support:
	 * --------------------------------------------------
	 * //import library
	 * jQuery.sap.require("sap.m.Support");
	 *
	 * //By default after require, support is enabled but implicitly we can call
	 * sap.m.Support.on();
	 *
	 * Disable Support:
	 * --------------------------------------------------
	 * sap.m.Support.off();
	 *

	 *
	 * @author SAP SE
	 * @since 1.11.0
	 *
	 * @static
	 * @protected
	 * @name sap.m.Support
	 */
	var Support = (function($, document) {

		var dialog, startTime, isEventRegistered, lastTouchUID;
		var timeDiff = 0;
		var minHoldTime = 3000; // 3s(3000ms) two-finger hold time
		var holdFingersNumber = 2; // two-fingers hold
		var maxFingersAllowed = 3; // two-fingers hold + 1-finger tab
		var releasedFingersNumber = 1,
			oData = {},
			e2eTraceConst = {
				btnStart : "startE2ETrace",
				selLevel : "logLevelE2ETrace",
				taContent : "outputE2ETrace",
				infoText : "Ent-to-End trace is running in the background." +
							" Navigate to the URL that you would like to trace." +
							" The result of the trace will be shown in dialog after the trace is terminated.",
				infoDuration : 5000 // 5 sec.
			},
			controlIDs = {
				dvLoadedLibs 	: "LoadedLibs",
				dvLoadedModules : "LoadedModules"
		};

		// copied from core
		function line(buffer, right, border, label, content) {
			buffer.push("
", jQuery.sap.escapeHTML(label), "
");
			var ctnt = content;
			if ($.isFunction(content)) {
				ctnt = content(buffer) || "";
			}
			buffer.push($.sap.escapeHTML(ctnt));
			buffer.push("
");
		}

		// copied from core
		function multiline(buffer, right, border, label, content) {
			line(buffer, right, border, label, function(buffer) {
				buffer.push("

");
				$.each(content, function(i, v) {
					var val = "";
					if (v !== undefined && v !== null) {
						if (typeof (v) == "string" || typeof (v) == "boolean" || ($.isArray(v) && v.length == 1)) {
							val = v;
						} else if (($.isArray(v) || $.isPlainObject(v)) && window.JSON) {
							val = window.JSON.stringify(v);
						}
					}
					line(buffer, false, false, i, "" + val);
				});
				buffer.push("

");
			});
		}

		// copied from core
		function getTechnicalContent() {
			var oCfg = ToolsAPI.getFrameworkInformation();
			oData = {
				version: oCfg.commonInformation.version,
				build: oCfg.commonInformation.buildTime,
				change: oCfg.commonInformation.lastChange,
				useragent: oCfg.commonInformation.userAgent,
				docmode: oCfg.commonInformation.documentMode,
				debug: oCfg.commonInformation.debugMode,
				bootconfig: oCfg.configurationBootstrap,
				config:  oCfg.configurationComputed,
				loadedlibs: oCfg.loadedLibraries,
				modules: oCfg.loadedModules,
				uriparams: oCfg.URLParameters,
				appurl: oCfg.commonInformation.applicationHREF
			};

			var html = ["

"];
			line(html, true, true, "SAPUI5 Version", function(buffer) {
				buffer.push(oData.version, " (built at ", oData.build, ", last change ", oData.change, ")");
			});
			line(html, true, true, "User Agent", function(buffer) {
				buffer.push(oData.useragent, (oData.docmode ? ", Document Mode '" + oData.docmode + "'" : ""));
			});
			line(html, true, true, "Debug Sources", function(buffer) {
				buffer.push((oData.debug ? "ON" : "OFF") );
			});
			line(html, true, true, "Application", oData.appurl);
			multiline(html, true, true, "Configuration (bootstrap)", oData.bootconfig);
			multiline(html, true, true, "Configuration (computed)", oData.config);
			multiline(html, true, true, "URI Parameters", oData.uriparams);
			// e2e trace section
			line(html, true, true, "End-to-End Trace", function(buffer) {
				buffer.push("Trace Level:",
					"<select id='" + buildControlId(e2eTraceConst.selLevel) + "' class='sapUiSupportTxtFld' >",
						"<option value='low'>LOW</option>",
						"<option value='medium' selected>MEDIUM</option>",
						"<option value='high'>HIGH</option>",
					"</select>"
				);
				buffer.push("<button id='" + buildControlId(e2eTraceConst.btnStart) + "' class='sapUiSupportBtn'>Start</button>");
				buffer.push("
");
				buffer.push("XML Output:");
				buffer.push("<textarea id='" + buildControlId(e2eTraceConst.taContent) +  "' class='sapUiSupportTxtArea sapUiSelectable' readonly ></textarea>");
				buffer.push("
");
			});

			line(html, true, true, "Loaded Libraries", function(buffer) {
				buffer.push("
");
				$.each(oData.loadedlibs, function(i, v) {
		            if (v && (typeof (v) === "string" || typeof (v) === "boolean")) {
	                     buffer.push("
", i + " " + v, "
");
		            }
				});
				buffer.push("
");
			});

			line(html, true, true, "Loaded Modules", function(buffer) {
				buffer.push("
");
			});

			html.push("

");

			return new sap.ui.core.HTML({
				content : html.join("").replace(/\{/g, "{").replace(/\}/g, "}")
			});
		}

		function buildControlId(controlId) {
			return dialog.getId() + "-" + controlId;
		}

		function fillPanelContent(panelId, arContent) {

			var panelHeader = "Modules";
			var libsCount = 0, arDivContent = [];

			libsCount = arContent.length;
			$.each(arContent.sort(), function(i, module) {
				arDivContent.push(new sap.m.Label({ text : " - " + module }).addStyleClass("sapUiSupportPnlLbl"));
			});

			// insert content into div placeholders
			var objPanel = new sap.m.Panel({
				expandable : true,
				expanded : false,
				headerToolbar : new sap.m.Toolbar({
					design : sap.m.ToolbarDesign.Transparent,
					content : [new sap.m.Label({
						text : panelHeader + " (" + libsCount + ")",
						design : sap.m.LabelDesign.Bold
					})]
				}),
				content : arDivContent
			});

			objPanel.placeAt( buildControlId(panelId), "only");
		}

		// setup dialog elements and bind some events
		function setupDialog() {
			// setup e2e values as log level and content
			if (dialog.traceXml) {
				dialog.$(e2eTraceConst.taContent).text(dialog.traceXml);
			}
			if (dialog.e2eLogLevel) {
				dialog.$(e2eTraceConst.selLevel).val(dialog.e2eLogLevel);
			}

			fillPanelContent(controlIDs.dvLoadedModules, oData.modules);

			// bind button Start event
			dialog.$(e2eTraceConst.btnStart).one("tap", function() {

				dialog.e2eLogLevel = dialog.$(e2eTraceConst.selLevel).val();
				dialog.$(e2eTraceConst.btnStart).addClass("sapUiSupportRunningTrace").text("Running...");
				dialog.traceXml = "";
				dialog.$(e2eTraceConst.taContent).text("");

				sap.ui.core.support.trace.E2eTraceLib.start(dialog.e2eLogLevel, function(traceXml) {
					dialog.traceXml = traceXml;
				});

				// show info message about the E2E trace activation
				sap.m.MessageToast.show(e2eTraceConst.infoText, {duration: e2eTraceConst.infoDuration});

				//close the dialog, but keep it for later use
				dialog.close();
			});
		}

		// get or create dialog instance and return
		function getDialog() {
			if (dialog) {
				return dialog;
			}

			$.sap.require("sap.m.Dialog");
			$.sap.require("sap.m.Button");
			$.sap.require("sap.ui.core.HTML");
			$.sap.require("sap.m.MessageToast");
			$.sap.require("sap.ui.core.support.trace.E2eTraceLib");

			dialog = new sap.m.Dialog({
				title : "Technical Information",
				horizontalScrolling: true,
				verticalScrolling: true,
				stretch: jQuery.device.is.phone,
				leftButton : new sap.m.Button({
					text : "Close",
					press : function() {
						dialog.close();
					}
				}),
				afterOpen : function() {
					Support.off();
				},
				afterClose : function() {
					Support.on();
				}
			}).addStyleClass("sapMSupport");

			return dialog;
		}

		//function is triggered when a touch is detected
		function onTouchStart(oEvent) {
			if (oEvent.touches) {
				var currentTouches = oEvent.touches.length;

				if (currentTouches > maxFingersAllowed) {
					document.removeEventListener('touchend', onTouchEnd);
					return;
				}

				switch (currentTouches) {

					case holdFingersNumber:
						startTime = Date.now();
						document.addEventListener('touchend', onTouchEnd);
						break;

					case maxFingersAllowed:
						if (startTime) {
							timeDiff = Date.now() - startTime;
							lastTouchUID = oEvent.touches[currentTouches - 1].identifier;
						}
						break;
				}
			}
		}

		//function is triggered when a touch is removed e.g. the user’s finger is removed from the touchscreen.
		function onTouchEnd(oEvent) {
			document.removeEventListener('touchend', onTouchEnd);

			// Check if two fingers are holded for 3 seconds or more and after that it`s tapped with a third finger
			if (timeDiff > minHoldTime
					&& oEvent.touches.length === holdFingersNumber
					&& oEvent.changedTouches.length === releasedFingersNumber
					&& oEvent.changedTouches[0].identifier === lastTouchUID) {

						timeDiff = 0;
						startTime = 0;
						show();
			}
		}

		function show() {
			var container = getDialog();
			container.removeAllAggregation("content");
			container.addAggregation("content", getTechnicalContent());
			dialog.open();
			setupDialog();
		}

		return ({
			/**
			 * Enables support.
			 *
			 * @returns {sap.m.Support} this to allow method chaining
			 * @protected
			 * @name sap.m.Support.on
			 * @function
			 */
			on : function() {
				if (!isEventRegistered && "ontouchstart" in document) {
					isEventRegistered = true;
					document.addEventListener("touchstart", onTouchStart);
				}
				return this;
			},

			/**
			 * Disables support.
			 *
			 * @returns {sap.m.Support} this to allow method chaining
			 * @protected
			 * @name sap.m.Support.off
			 * @function
			 */
			off : function() {
				if (isEventRegistered) {
					isEventRegistered = false;
					document.removeEventListener("touchstart", onTouchStart);
				}
				return this;
			},

			open : function() {
				show();
			},

			/**
			 * Returns if event is registered or not.
			 *
			 * @returns {boolean}
			 * @protected
			 * @name sap.m.Support.isEventRegistered
			 * @function
			 */
			isEventRegistered : function() {
				return isEventRegistered;
			}
		}).on();

	}(jQuery, document));

	return Support;

}, /* bExport= */ true);
<|endoftext|>


<commit_before>/**
 * Populate DB with sample data on server start;
 * If we're in DB seeding mode, do it forever.
 * To disable, edit config/environment/index.js, and set `seedDB: false`
 *
 * Using $http Model-as-a-Service in AngularJS
 * @author Nick Kaye <nick.c.kaye@gmail.com>
 * @repository https://github.com/nickckaye/http-model-as-a-service
 */

'use strict';

var async = require('async')
  , moment = require('moment')
  , Thing = require('../api/thing/thing.model')
  , REPEAT_INTERVAL_MS = 10000
  , THING_EXPIRE_SECONDS = 3600
  , GENERATE_RECORDS_COUNT = 10
  , interval;

/**
 * @returns {Long|Timestamp} before which records expire
 */
function records_expire_before() {
  return moment().subtract(THING_EXPIRE_SECONDS, 'seconds');
}

/**
 * @param {Function} done callback
 */
function destroy_all_records(done) {
  Thing.find({})
    .remove(done);
}

/**
 * @param {Function} done callback
 */
function destroy_expired_records(done) {
  Thing.find({})
    .where('created_at').lt(records_expire_before())
    .remove(done);
}

/**
 * @param {Function} done callback
 */
function generate_records(done) {
  Thing.count({}, function (err, count) {
    if (count < GENERATE_RECORDS_COUNT) {
      Thing.create_random(done);
    } else {
      done();
    }
  });
}

/**
 * Run the seed generation phase
 * @param {Function} done callback
 */
function run_seed_generation(done) {
  async.series([
      destroy_expired_records,
      generate_records
    ],
    done);
}

/**
 * Begin seed generation
 */
function begin_seed_generation() {
  interval = setInterval(run_seed_generation, REPEAT_INTERVAL_MS);
}

/**
 * Run the seed generation every X milliseconds
 * @type {*|Object|number}
 */
destroy_all_records(function () {
  begin_seed_generation();
});
<commit_msg> Deploy to production with rolling randomly-generated records. #20<commit_after>/**
 * Populate DB with sample data on server start;
 * If we're in DB seeding mode, do it forever.
 * To disable, edit config/environment/index.js, and set `seedDB: false`
 *
 * Using $http Model-as-a-Service in AngularJS
 * @author Nick Kaye <nick.c.kaye@gmail.com>
 * @repository https://github.com/nickckaye/http-model-as-a-service
 */

'use strict';

var async = require('async')
  , moment = require('moment')
  , Thing = require('../api/thing/thing.model')
  , REPEAT_INTERVAL_MS = 10000
  , THING_EXPIRE_SECONDS = 3600
  , GENERATE_RECORDS_COUNT = 10
  , interval;

/**
 * @returns {Long|Timestamp} before which records expire
 */
function records_expire_before() {
  return moment().subtract(THING_EXPIRE_SECONDS, 'seconds');
}

/**
 * @param {Function} done callback
 */
function destroy_expired_records(done) {
  Thing.find({})
    .where('created_at').lt(records_expire_before())
    .remove(done);
}

/**
 * @param {Function} done callback
 */
function generate_records(done) {
  Thing.count({}, function (err, count) {
    if (count < GENERATE_RECORDS_COUNT) {
      Thing.create_random(done);
    } else {
      done();
    }
  });
}

/**
 * Run the seed generation phase
 * @param {Function} done callback
 */
function run_seed_generation(done) {
  async.series([
      destroy_expired_records,
      generate_records
    ],
    done);
}

/**
 * Begin seed generation
 */
function begin_seed_generation() {
  interval = setInterval(run_seed_generation, REPEAT_INTERVAL_MS);
}

/**
 * Run the seed generation every X milliseconds
 * @type {*|Object|number}
 */
begin_seed_generation();
<|endoftext|>


<commit_before>(function () {

    SettingsController.$inject = ['Resources', '$timeout', 'User'];

    angular
        .module('myApp.components.settings', [])
        .controller('SettingsController', SettingsController);

    function SettingsController(Resources, $timeout, User) {
        var vm = this;

        vm.subscribed = true;
        vm.showLoader = true;

        vm.subscription = subscription;

        activate();

        function activate() {

            // Give small amount of time for Resources to load
            $timeout(function() {
                vm.subscribed = Resources.getCurrentUser();

                if (typeof vm.subscribed == "undefined") {
                    User.home().then(function (val) {
                        vm.subscribed = val.subscribing;
                    });
                } else vm.subscribed = vm.subscribed.subscribing;

                vm.showLoader = false;
            }, 200);
        }

        function subscription() {
            switch (vm.subscribed){
                case true:
                    User.subscribe().then(function (val) {
                        Resources.getCurrentUser().subscribed = val;
                    });
                    break;
                case false:
                    User.unsubscribe().then(function (val) {
                        Resources.getCurrentUser().subscribed = val;
                    });
                    break;
            }
        }
    }

})();<commit_msg>Small changes for email notification<commit_after>(function () {

    SettingsController.$inject = ['Resources', '$timeout', 'User'];

    angular
        .module('myApp.components.settings', [])
        .controller('SettingsController', SettingsController);

    function SettingsController(Resources, $timeout, User) {
        var vm = this;

        vm.subscribed = false;
        vm.showLoader = true;

        vm.subscription = subscription;

        activate();

        function activate() {

            // Give small amount of time for Resources to load
            $timeout(function() {
                vm.subscribed = Resources.getCurrentUser();

                if (typeof vm.subscribed == "undefined") {
                    User.home().then(function (val) {
                        vm.subscribed = val.subscribing;
                    });
                } else vm.subscribed = vm.subscribed.subscribing;

                vm.showLoader = false;
            }, 200);
        }

        function subscription() {
            switch (vm.subscribed){
                case true:
                    User.subscribe().then(function (val) {
                        Resources.getCurrentUser().subscribed = val;
                    });
                    break;
                case false:
                    User.unsubscribe().then(function (val) {
                        Resources.getCurrentUser().subscribed = val;
                    });
                    break;
            }
        }
    }

})();<|endoftext|>


<commit_before>require(
    ["bundles/omttodo/js/jquery/jquery.validate.min.js"],
    function () {
        var messageBlock = $("#message"),
            baseUrl = $("html").data("baseUrl"),
            reloadList = function () {
                var todoContainer = $("#todoContainer");
                todoContainer.empty();
                $.isLoading({text: "Loading list of todos...", position: "inside"});
                $.getJSON(baseUrl + "/todo/list", function (data) {
                    var todoTemplate = $("#todoTemplate").html();
                    $.each(data.data.todos, function (index, todo) {
                        var cssClass, text, prefix,
                            todoPanel = $(todoTemplate);
                        switch (todo.priority) {
                            case "high":
                                cssClass = "todo-high-priority";
                                break;
                            case "medium":
                                cssClass = "todo-medium-priority";
                                break;
                            case "low":
                                cssClass = "todo-low-priority";
                                break;
                        }
                        todoPanel.addClass(cssClass);
                        todoPanel.attr('data-id', todo.id);
                        todoPanel.attr('data-priority', todo.priority);
                        todoPanel.attr('data-task', todo.task);
                        text = todo.task;
                        if (todo.isDone) {
                            todoPanel.addClass('todo-done');
                            text = "(done) " + todo.task;
                        }
                        todoPanel.children(".todo-text").text(text);
                        todoContainer.append(todoPanel);
                    });
                });
            },
            closeEditPanels = function () {
                $("#todoContainer").children('.-todo-edit-panel').remove();
            };

        $(document).on("click", ".-todo-edit-button", function () {
            var todoPanel = $(this).closest(".-todo-panel"),
                todoEditTemplate = $("#todoEditTemplate").html(),
                todoEditPanel = $(todoEditTemplate),
                form = todoEditPanel.children("form"),
                focusTaskInput = function () {
                    todoEditPanel.find("input[name='task']").select();
                },
                validator = form.validate({
                    submitHandler: function(form) {
                        var me = $(form),
                            baseUrl = $("html").data("baseUrl");
                        messageBlock.hide();
                        $.isLoading({text: "Saving...", position: "inside"});
                        $.ajax({
                            url: baseUrl + "/todo/modify/" + todoPanel.data("id"),
                            method: me.attr("method"),
                            data: me.serialize(),
                            dataType: "json",
                            success: function (data) {
                                var messageBlockText = messageBlock.children(".-message-text"),
                                    errorMessage;
                                if (!data.success) {
                                    errorMessage = 'Unknown error occurred.';
                                    messageBlockText.text(errorMessage);
                                    messageBlock.addClass("block-error").show();
                                    focusTaskInput();
                                } else {
                                    reloadList();
                                }
                            }
                        });
                    },
                    rules: {
                        "task": {
                            required: true,
                            maxlength: 200
                        }
                    },
                    errorPlacement: function() {
                        return true;
                    }
                });

            closeEditPanels();

            todoEditPanel.find("input[name='task']").val(todoPanel.data('task'));
            todoEditPanel.find("select[name='priority']").val(todoPanel.data('priority'));

            todoPanel.after(todoEditPanel);

            focusTaskInput();

            return false;
        });

        $(document).on("click", ".-todo-mark-done-button", function () {
            var todoPanel = $(this).closest(".-todo-panel");
            closeEditPanels();
            $.ajax({
                url: baseUrl + "/todo/done/" + todoPanel.data("id"),
                dataType: "json",
                success: function (data) {
                    var messageBlockText = messageBlock.children(".-message-text"),
                        messages = {
                            'not_found': 'Todo not found'
                        },
                        errorMessage;
                    if (!data.success) {
                        if (messages[data.message]) {
                            errorMessage = messages[data].message;
                        } else {
                            errorMessage = 'Unknown error occurred.';
                        }
                        messageBlockText.text(errorMessage);
                        messageBlock.addClass("block-error").show();
                    } else {
                        reloadList();
                    }
                }
            });
            return false;
        });

        $(document).on("click", ".-todo-remove-button", function () {
            var todoPanel = $(this).closest(".-todo-panel");
            closeEditPanels();
            $.ajax({
                url: baseUrl + "/todo/remove/" + todoPanel.data("id"),
                dataType: "json",
                success: function (data) {
                    var messageBlockText = messageBlock.children(".-message-text"),
                        messages = {
                            'not_found': 'Todo not found'
                        },
                        errorMessage;
                    if (!data.success) {
                        if (messages[data.message]) {
                            errorMessage = messages[data].message;
                        } else {
                            errorMessage = 'Unknown error occurred.';
                        }
                        messageBlockText.text(errorMessage);
                        messageBlock.addClass("block-error").show();
                    } else {
                        todoPanel.remove();
                    }
                }
            });
            return false;
        });

        (function ($) {
            var form = $("#createTodoForm"),
                focusTaskInput = function () {
                    form.find("input[name='task']").select();
                },
                validator = form.validate({
                    submitHandler: function(form) {
                        var me = $(form);
                        messageBlock.hide();
                        $.isLoading({text: "Creating...", position: "inside"});
                        $.ajax({
                            url: me.attr("action"),
                            method: me.attr("method"),
                            data: me.serialize(),
                            dataType: "json",
                            success: function (data) {
                                var messageBlockText = messageBlock.children(".-message-text"),
                                    errorMessage;
                                if (!data.success) {
                                    errorMessage = 'Unknown error occurred.';
                                    messageBlockText.text(errorMessage);
                                    messageBlock.addClass("block-error").show();
                                    focusTaskInput();
                                } else {
                                    reloadList();
                                }
                            }
                        });
                    },
                    rules: {
                        "task": {
                            required: true,
                            maxlength: 200
                        }
                    },
                    errorPlacement: function() {
                        return true;
                    }
                });

            focusTaskInput();

        })(jQuery);

        reloadList();
    }
);<commit_msg>Create form are now cleared after successful todo creation.<commit_after>require(
    ["bundles/omttodo/js/jquery/jquery.validate.min.js"],
    function () {
        var messageBlock = $("#message"),
            baseUrl = $("html").data("baseUrl"),
            reloadList = function () {
                var todoContainer = $("#todoContainer");
                todoContainer.empty();
                $.isLoading({text: "Loading list of todos...", position: "inside"});
                $.getJSON(baseUrl + "/todo/list", function (data) {
                    var todoTemplate = $("#todoTemplate").html();
                    $.each(data.data.todos, function (index, todo) {
                        var cssClass, text, prefix,
                            todoPanel = $(todoTemplate);
                        switch (todo.priority) {
                            case "high":
                                cssClass = "todo-high-priority";
                                break;
                            case "medium":
                                cssClass = "todo-medium-priority";
                                break;
                            case "low":
                                cssClass = "todo-low-priority";
                                break;
                        }
                        todoPanel.addClass(cssClass);
                        todoPanel.attr('data-id', todo.id);
                        todoPanel.attr('data-priority', todo.priority);
                        todoPanel.attr('data-task', todo.task);
                        text = todo.task;
                        if (todo.isDone) {
                            todoPanel.addClass('todo-done');
                            text = "(done) " + todo.task;
                        }
                        todoPanel.children(".todo-text").text(text);
                        todoContainer.append(todoPanel);
                    });
                });
            },
            closeEditPanels = function () {
                $("#todoContainer").children('.-todo-edit-panel').remove();
            };

        $(document).on("click", ".-todo-edit-button", function () {
            var todoPanel = $(this).closest(".-todo-panel"),
                todoEditTemplate = $("#todoEditTemplate").html(),
                todoEditPanel = $(todoEditTemplate),
                form = todoEditPanel.children("form"),
                focusTaskInput = function () {
                    todoEditPanel.find("input[name='task']").select();
                },
                validator = form.validate({
                    submitHandler: function(form) {
                        var me = $(form),
                            baseUrl = $("html").data("baseUrl");
                        messageBlock.hide();
                        $.isLoading({text: "Saving...", position: "inside"});
                        $.ajax({
                            url: baseUrl + "/todo/modify/" + todoPanel.data("id"),
                            method: me.attr("method"),
                            data: me.serialize(),
                            dataType: "json",
                            success: function (data) {
                                var messageBlockText = messageBlock.children(".-message-text"),
                                    errorMessage;
                                if (!data.success) {
                                    errorMessage = 'Unknown error occurred.';
                                    messageBlockText.text(errorMessage);
                                    messageBlock.addClass("block-error").show();
                                    focusTaskInput();
                                } else {
                                    reloadList();
                                }
                            }
                        });
                    },
                    rules: {
                        "task": {
                            required: true,
                            maxlength: 200
                        }
                    },
                    errorPlacement: function() {
                        return true;
                    }
                });

            closeEditPanels();

            todoEditPanel.find("input[name='task']").val(todoPanel.data('task'));
            todoEditPanel.find("select[name='priority']").val(todoPanel.data('priority'));

            todoPanel.after(todoEditPanel);

            focusTaskInput();

            return false;
        });

        $(document).on("click", ".-todo-mark-done-button", function () {
            var todoPanel = $(this).closest(".-todo-panel");
            closeEditPanels();
            $.ajax({
                url: baseUrl + "/todo/done/" + todoPanel.data("id"),
                dataType: "json",
                success: function (data) {
                    var messageBlockText = messageBlock.children(".-message-text"),
                        messages = {
                            'not_found': 'Todo not found'
                        },
                        errorMessage;
                    if (!data.success) {
                        if (messages[data.message]) {
                            errorMessage = messages[data].message;
                        } else {
                            errorMessage = 'Unknown error occurred.';
                        }
                        messageBlockText.text(errorMessage);
                        messageBlock.addClass("block-error").show();
                    } else {
                        reloadList();
                    }
                }
            });
            return false;
        });

        $(document).on("click", ".-todo-remove-button", function () {
            var todoPanel = $(this).closest(".-todo-panel");
            closeEditPanels();
            $.ajax({
                url: baseUrl + "/todo/remove/" + todoPanel.data("id"),
                dataType: "json",
                success: function (data) {
                    var messageBlockText = messageBlock.children(".-message-text"),
                        messages = {
                            'not_found': 'Todo not found'
                        },
                        errorMessage;
                    if (!data.success) {
                        if (messages[data.message]) {
                            errorMessage = messages[data].message;
                        } else {
                            errorMessage = 'Unknown error occurred.';
                        }
                        messageBlockText.text(errorMessage);
                        messageBlock.addClass("block-error").show();
                    } else {
                        todoPanel.remove();
                    }
                }
            });
            return false;
        });

        (function ($) {
            var form = $("#createTodoForm"),
                focusTaskInput = function () {
                    form.find("input[name='task']").select();
                },
                validator = form.validate({
                    submitHandler: function(form) {
                        var me = $(form);
                        messageBlock.hide();
                        $.isLoading({text: "Creating...", position: "inside"});
                        $.ajax({
                            url: me.attr("action"),
                            method: me.attr("method"),
                            data: me.serialize(),
                            dataType: "json",
                            success: function (data) {
                                var messageBlockText = messageBlock.children(".-message-text"),
                                    errorMessage;
                                if (!data.success) {
                                    errorMessage = 'Unknown error occurred.';
                                    messageBlockText.text(errorMessage);
                                    messageBlock.addClass("block-error").show();
                                    focusTaskInput();
                                } else {
                                    me.find("[name='task']").val('');
                                    me.find("[name='priority']").val(0);
                                    reloadList();
                                }
                            }
                        });
                    },
                    rules: {
                        "task": {
                            required: true,
                            maxlength: 200
                        }
                    },
                    errorPlacement: function() {
                        return true;
                    }
                });

            focusTaskInput();

        })(jQuery);

        reloadList();
    }
);<|endoftext|>


<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
Copyright 2013 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* globals chrome */

'use strict';

(function ExtensionRouterClosure() {
  var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html');
  var CRX_BASE_URL = chrome.extension.getURL('/');

  var schemes = [
    'http',
    'https',
    'ftp',
    'file',
    'chrome-extension',
    // Chromium OS
    'filesystem',
    // Chromium OS, shorthand for filesystem:<origin>/external/
    'drive'
  ];

  /**
   * @param {string} url The URL prefixed with chrome-extension://.../
   * @return {string|undefined} The percent-encoded URL of the (PDF) file.
   */
  function parseExtensionURL(url) {
    url = url.substring(CRX_BASE_URL.length);
    // Find the (url-encoded) colon and verify that the scheme is whitelisted.
    var schemeIndex = url.search(/:|%3A/i);
    if (schemeIndex === -1) {
      return;
    }
    var scheme = url.slice(0, schemeIndex).toLowerCase();
    if (schemes.indexOf(scheme) >= 0) {
      url = url.split('#')[0];
      if (url.charAt(schemeIndex) === ':') {
        url = encodeURIComponent(url);
      }
      return url;
    }
  }

  /**
   * @param {string} url URL of PDF Viewer.
   * @return {string|undefined} The percent-encoded URL of the (PDF) file.
   */
  function parseViewerURL(url) {
    if (url.lastIndexOf(VIEWER_URL, 0) !== 0) {
      // Does not even start with the correct URL. Bye!
      return;
    }
    url = url.match(/[&?]file=([^&#]+)/);
    if (url) {
      url = url[1];
      return url;
    }
  }

  /**
   * @param {number} tabId ID of tab where the page action will be shown
   * @param {string} url URL to be displayed in page action
   */
  function showPageAction(tabId, displayUrl) {
    var url = parseExtensionURL(displayUrl) || parseViewerURL(displayUrl);
    if (url) {
      chrome.pageAction.setPopup({
        tabId: tabId,
        popup: 'pageActionPopup.html?file=' + url
      });
      chrome.pageAction.show(tabId);
    } else {
      console.log('Unable to get PDF url from ' + displayUrl);
    }
  }

  // TODO(rob): Use declarativeWebRequest once declared URL-encoding is
  //            supported, see http://crbug.com/273589
  //            (or rewrite the query string parser in viewer.js to get it to
  //             recognize the non-URL-encoded PDF URL.)
  chrome.webRequest.onBeforeRequest.addListener(function(details) {
    // This listener converts chrome-extension://.../http://...pdf to
    // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
    var url = parseExtensionURL(details.url);
    if (url) {
      url = VIEWER_URL + '?file=' + url;
      console.log('Redirecting ' + details.url + ' to ' + url);
      return { redirectUrl: url };
    }
  }, {
    types: ['main_frame', 'sub_frame'],
    urls: schemes.map(function(scheme) {
      // Format: "chrome-extension://[EXTENSIONID]/<scheme>*"
      return CRX_BASE_URL + scheme + '*';
    })
  }, ['blocking']);

  chrome.runtime.onMessage.addListener(function(message, sender) {
    if (message === 'showPageAction' && sender.tab) {
      showPageAction(sender.tab.id, sender.tab.url);
    }
  });

  // When session restore is used, viewer pages may be loaded before the
  // webRequest event listener is attached (= page not found).
  // Reload these tabs.
  chrome.tabs.query({
    url: CRX_BASE_URL + '*:*'
  }, function(tabsFromLastSession) {
    for (var i = 0; i < tabsFromLastSession.length; ++i) {
      chrome.tabs.reload(tabsFromLastSession[i].id);
    }
  });
  console.log('Set up extension URL router.');
})();
<commit_msg>Preserve reference fragment in Chromium URL router<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
Copyright 2013 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* globals chrome */

'use strict';

(function ExtensionRouterClosure() {
  var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html');
  var CRX_BASE_URL = chrome.extension.getURL('/');

  var schemes = [
    'http',
    'https',
    'ftp',
    'file',
    'chrome-extension',
    // Chromium OS
    'filesystem',
    // Chromium OS, shorthand for filesystem:<origin>/external/
    'drive'
  ];

  /**
   * @param {string} url The URL prefixed with chrome-extension://.../
   * @return {string|undefined} The percent-encoded URL of the (PDF) file.
   */
  function parseExtensionURL(url) {
    url = url.substring(CRX_BASE_URL.length);
    // Find the (url-encoded) colon and verify that the scheme is whitelisted.
    var schemeIndex = url.search(/:|%3A/i);
    if (schemeIndex === -1) {
      return;
    }
    var scheme = url.slice(0, schemeIndex).toLowerCase();
    if (schemes.indexOf(scheme) >= 0) {
      url = url.split('#')[0];
      if (url.charAt(schemeIndex) === ':') {
        url = encodeURIComponent(url);
      }
      return url;
    }
  }

  /**
   * @param {string} url URL of PDF Viewer.
   * @return {string|undefined} The percent-encoded URL of the (PDF) file.
   */
  function parseViewerURL(url) {
    if (url.lastIndexOf(VIEWER_URL, 0) !== 0) {
      // Does not even start with the correct URL. Bye!
      return;
    }
    url = url.match(/[&?]file=([^&#]+)/);
    if (url) {
      url = url[1];
      return url;
    }
  }

  /**
   * @param {number} tabId ID of tab where the page action will be shown
   * @param {string} url URL to be displayed in page action
   */
  function showPageAction(tabId, displayUrl) {
    var url = parseExtensionURL(displayUrl) || parseViewerURL(displayUrl);
    if (url) {
      chrome.pageAction.setPopup({
        tabId: tabId,
        popup: 'pageActionPopup.html?file=' + url
      });
      chrome.pageAction.show(tabId);
    } else {
      console.log('Unable to get PDF url from ' + displayUrl);
    }
  }

  // TODO(rob): Use declarativeWebRequest once declared URL-encoding is
  //            supported, see http://crbug.com/273589
  //            (or rewrite the query string parser in viewer.js to get it to
  //             recognize the non-URL-encoded PDF URL.)
  chrome.webRequest.onBeforeRequest.addListener(function(details) {
    // This listener converts chrome-extension://.../http://...pdf to
    // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
    var url = parseExtensionURL(details.url);
    if (url) {
      url = VIEWER_URL + '?file=' + url;
      var i = details.url.indexOf('#');
      if (i > 0) {
        url += details.url.slice(i);
      }
      console.log('Redirecting ' + details.url + ' to ' + url);
      return { redirectUrl: url };
    }
  }, {
    types: ['main_frame', 'sub_frame'],
    urls: schemes.map(function(scheme) {
      // Format: "chrome-extension://[EXTENSIONID]/<scheme>*"
      return CRX_BASE_URL + scheme + '*';
    })
  }, ['blocking']);

  chrome.runtime.onMessage.addListener(function(message, sender) {
    if (message === 'showPageAction' && sender.tab) {
      showPageAction(sender.tab.id, sender.tab.url);
    }
  });

  // When session restore is used, viewer pages may be loaded before the
  // webRequest event listener is attached (= page not found).
  // Reload these tabs.
  chrome.tabs.query({
    url: CRX_BASE_URL + '*:*'
  }, function(tabsFromLastSession) {
    for (var i = 0; i < tabsFromLastSession.length; ++i) {
      chrome.tabs.reload(tabsFromLastSession[i].id);
    }
  });
  console.log('Set up extension URL router.');
})();
<|endoftext|>