<commit_before>dojo.provide("dijit.layout.ContentPane");

dojo.require("dijit._Widget");
dojo.require("dijit.layout._LayoutWidget");

dojo.require("dojo.parser");
dojo.require("dojo.string");
dojo.requireLocalization("dijit", "loading");

dojo.declare(
	"dijit.layout.ContentPane",
	dijit._Widget,
{
	// summary:
	//		A widget that acts as a Container for other widgets, and includes a ajax interface
	// description:
	//		A widget that can be used as a standalone widget
	//		or as a baseclass for other widgets
	//		Handles replacement of document fragment using either external uri or javascript
	//		generated markup or DOM content, instantiating widgets within that content.
	//		Don't confuse it with an iframe, it only needs/wants document fragments.
	//		It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
	//		But note that those classes can contain any widget as a child.
	// example:
	//		Some quick samples:
	//		To change the innerHTML use .setContent('new content')
	//
	//		Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection))
	//		please note that the nodes in NodeList will copied, not moved
	//
	//		To do a ajax update use .setHref('url')
	//
	// href: String
	//		The href of the content that displays now.
	//		Set this at construction if you want to load data externally when the
	//		pane is shown.  (Set preload=true to load it immediately.)
	//		Changing href after creation doesn't have any effect; see setHref();
	href: "",

	// extractContent: Boolean
	//	Extract visible content from inside of  ....
	extractContent: false,

	// parseOnLoad: Boolean
	//	parse content and create the widgets, if any
	parseOnLoad:	true,

	// preventCache: Boolean
	//		Cache content retreived externally
	preventCache:	false,

	// preload: Boolean
	//	Force load of data even if pane is hidden.
	preload: false,

	// refreshOnShow: Boolean
	//		Refresh (re-download) content when pane goes from hidden to shown
	refreshOnShow: false,

	// loadingMessage: String
	//	Message that shows while downloading
	loadingMessage: "${loadingState}",

	// errorMessage: String
	//	Message that shows if an error occurs
	errorMessage: "${errorState}",

	// isLoaded: Boolean
	//	Tells loading status see onLoad|onUnload for event hooks
	isLoaded: false,

	// class: String
	//	Class name to apply to ContentPane dom nodes
	// TODO: this should be called "baseClass" like in the other widgets
	"class": "dijitContentPane",

	// doLayout: String/Boolean
	//	false - don't adjust size of children
	//	true - looks for the first sizable child widget (ie, having resize() method) and sets it's size to
	//			however big the ContentPane is (TODO: implement)
	//	auto - if there is a single sizable child widget (ie, having resize() method), set it's size to
	//			however big the ContentPane is
	doLayout: "auto",

	postCreate: function(){
		// remove the title attribute so it doesn't show up when i hover
		// over a node
		this.domNode.title = "";

		if(!this.containerNode){
			// make getDescendants() work
			this.containerNode = this.domNode;
		}

		if(this.preload){
			this._loadCheck();
		}

		var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
		this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
		this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
		var curRole = dijit.getWaiRole(this.domNode);
		if (!curRole){
			dijit.setWaiRole(this.domNode, "group");
		}

		// for programatically created ContentPane (with  tag), need to muck w/CSS
		// or it's as though overflow:visible is set
		dojo.addClass(this.domNode, this["class"]);
	},

	startup: function(){
		if(this._started){ return; }
		if(this.doLayout != "false" && this.doLayout !== false){
			this._checkIfSingleChild();
			if(this._singleChild){
				this._singleChild.startup();
			}
		}
		this._loadCheck();
		this.inherited(arguments);
	},

	_checkIfSingleChild: function(){
		// summary:
		// 	Test if we have exactly one widget as a child, and if so assume that we are a container for that widget,
		//	and should propogate startup() and resize() calls to it.

		// TODO: if there are two child widgets (a data store and a TabContainer, for example),
		//	should still find the TabContainer
		var childNodes = dojo.query(">", this.containerNode || this.domNode),
			childWidgets = childNodes.filter("[widgetId]");

		if(childNodes.length == 1 && childWidgets.length == 1){
			this.isContainer = true;
			this._singleChild = dijit.byNode(childWidgets[0]);
		}else{
			delete this.isContainer;
			delete this._singleChild;
		}
	},

	refresh: function(){
		// summary:
		//	Force a refresh (re-download) of content, be sure to turn off cache

		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
		return this._prepareLoad(true);
	},

	setHref: function(/*String|Uri*/ href){
		// summary:
		//		Reset the (external defined) content of this pane and replace with new url
		//		Note: It delays the download until widget is shown if preload is false
		//	href:
		//		url to the page you want to get, must be within the same domain as your mainpage
		this.href = href;

		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
		return this._prepareLoad();
	},

	setContent: function(/*String|DomNode|Nodelist*/data){
		// summary:
		//		Replaces old content with data content, include style classes from old content
		//	data:
		//		the new Content may be String, DomNode or NodeList
		//
		//		if data is a NodeList (or an array of nodes) nodes are copied
		//		so you can import nodes from another document implicitly

		// clear href so we cant run refresh and clear content
		// refresh should only work if we downloaded the content
		if(!this._isDownloaded){
			this.href = "";
			this._onUnloadHandler();
		}

		this._setContent(data || "");

		this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane

		if(this.parseOnLoad){
			this._createSubWidgets();
		}

		if(this.doLayout != "false" && this.doLayout !== false){
			this._checkIfSingleChild();
			if(this._singleChild && this._singleChild.resize){
				this._singleChild.startup();
				this._singleChild.resize(this._contentBox || dojo.contentBox(this.containerNode || this.domNode));
			}
		}

		this._onLoadHandler();
	},

	cancel: function(){
		// summary:
		//		Cancels a inflight download of content
		if(this._xhrDfd && (this._xhrDfd.fired == -1)){
			this._xhrDfd.cancel();
		}
		delete this._xhrDfd; // garbage collect
	},

	destroy: function(){
		// if we have multiple controllers destroying us, bail after the first
		if(this._beingDestroyed){
			return;
		}
		// make sure we call onUnload
		this._onUnloadHandler();
		this._beingDestroyed = true;
		this.inherited("destroy",arguments);
	},

	resize: function(size){
		dojo.marginBox(this.domNode, size);

		// Compute content box size in case we [later] need to size child
		// If either height or width wasn't specified by the user, then query node for it.
		// But note that setting the margin box and then immediately querying dimensions may return
		// inaccurate results, so try not to depend on it.
		var node = this.containerNode || this.domNode,
			mb = dojo.mixin(dojo.marginBox(node), size||{});

		this._contentBox = dijit.layout.marginBox2contentBox(node, mb);

		// If we have a single widget child then size it to fit snugly within my borders
		if(this._singleChild && this._singleChild.resize){
			this._singleChild.resize(this._contentBox);
		}
	},

	_prepareLoad: function(forceLoad){
		// sets up for a xhrLoad, load is deferred until widget onShow
		// cancels a inflight download
		this.cancel();
		this.isLoaded = false;
		this._loadCheck(forceLoad);
	},

	_isOpen: function(){
		// summary: returns true if the content is currently shown
		if("open" in this){
			return this.open;		// for TitlePane, etc.
		}else if("selected" in this){
			return this.selected;	// AccordionContainer, TabContainer
		}else{
			var node = this.domNode;
			return (node.style.display != 'none')  && (node.style.visibility != 'hidden');
		}
	},

	_loadCheck: function(/*Boolean*/ forceLoad){
		// call this when you change onShow (onSelected) status when selected in parent container
		// it's used as a trigger for href download when this.domNode.display != 'none'

		// sequence:
		// if no href -> bail
		// forceLoad -> always load
		// this.preload -> load when download not in progress, domNode display doesn't matter
		// this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND
		//						this.open !== false (undefined is ok), isLoaded doesn't matter
		// else -> load when download not in progress, if this.open !== false (undefined is ok) AND
		//						domNode display != 'none', isLoaded must be false

		var displayState = this._isOpen();

		if(this.href &&
			(forceLoad ||
				(this.preload && !this._xhrDfd) ||
				(this.refreshOnShow && displayState && !this._xhrDfd) ||
				(!this.isLoaded && displayState && !this._xhrDfd)
			)
		){
			this._downloadExternalContent();
		}
	},

	_downloadExternalContent: function(){
		this._onUnloadHandler();

		// display loading message
		this._setContent(
			this.onDownloadStart.call(this)
		);

		var self = this;
		var getArgs = {
			preventCache: (this.preventCache || this.refreshOnShow),
			url: this.href,
			handleAs: "text"
		};
		if(dojo.isObject(this.ioArgs)){
			dojo.mixin(getArgs, this.ioArgs);
		}

		var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);

		hand.addCallback(function(html){
			try{
				self.onDownloadEnd.call(self);
				self._isDownloaded = true;
				self.setContent.call(self, html); // onload event is called from here
			}catch(err){
				self._onError.call(self, 'Content', err); // onContentError
			}
			delete self._xhrDfd;
			return html;
		});

		hand.addErrback(function(err){
			if(!hand.cancelled){
				// show error message in the pane
				self._onError.call(self, 'Download', err); // onDownloadError
			}
			delete self._xhrDfd;
			return err;
		});
	},

	_onLoadHandler: function(){
		this.isLoaded = true;
		try{
			this.onLoad.call(this);
		}catch(e){
			console.error('Error '+this.widgetId+' running custom onLoad code');
		}
	},

	_onUnloadHandler: function(){
		this.isLoaded = false;
		this.cancel();
		try{
			this.onUnload.call(this);
		}catch(e){
			console.error('Error '+this.widgetId+' running custom onUnload code');
		}
	},

	_setContent: function(cont){
		this.destroyDescendants();

		try{
			var node = this.containerNode || this.domNode;
			while(node.firstChild){
				dojo._destroyElement(node.firstChild);
			}
			if(typeof cont == "string"){
				// dijit.ContentPane does only minimal fixes,
				// No pathAdjustments, script retrieval, style clean etc
				// some of these should be available in the dojox.layout.ContentPane
				if(this.extractContent){
					match = cont.match(/]*>\s*([\s\S]+)\s*<\/body>/im);
					if(match){ cont = match[1]; }
				}
				node.innerHTML = cont;
			}else{
				// domNode or NodeList
				if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
					node.appendChild(cont);
				}else{// nodelist or array such as dojo.Nodelist
					dojo.forEach(cont, function(n){
						node.appendChild(n.cloneNode(true));
					});
				}
			}
		}catch(e){
			// check if a domfault occurs when we are appending this.errorMessage
			// like for instance if domNode is a UL and we try append a DIV
			var errMess = this.onContentError(e);
			try{
				node.innerHTML = errMess;
			}catch(e){
				console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
			}
		}
	},

	_onError: function(type, err, consoleText){
		// shows user the string that is returned by on[type]Error
		// overide on[type]Error and return your own string to customize
		var errText = this['on' + type + 'Error'].call(this, err);
		if(consoleText){
			console.error(consoleText, err);
		}else if(errText){// a empty string won't change current content
			this._setContent.call(this, errText);
		}
	},

	_createSubWidgets: function(){
		// summary: scan my contents and create subwidgets
		var rootNode = this.containerNode || this.domNode;
		try{
			dojo.parser.parse(rootNode, true);
		}catch(e){
			this._onError('Content', e, "Couldn't create widgets in "+this.id
				+(this.href ? " from "+this.href : ""));
		}
	},

	// EVENT's, should be overide-able
	onLoad: function(e){
		// summary:
		//		Event hook, is called after everything is loaded and widgetified
	},

	onUnload: function(e){
		// summary:
		//		Event hook, is called before old content is cleared
	},

	onDownloadStart: function(){
		// summary:
		//		called before download starts
		//		the string returned by this function will be the html
		//		that tells the user we are loading something
		//		override with your own function if you want to change text
		return this.loadingMessage;
	},

	onContentError: function(/*Error*/ error){
		// summary:
		//		called on DOM faults, require fault etc in content
		//		default is to display errormessage inside pane
	},

	onDownloadError: function(/*Error*/ error){
		// summary:
		//		Called when download error occurs, default is to display
		//		errormessage inside pane. Overide function to change that.
		//		The string returned by this function will be the html
		//		that tells the user a error happend
		return this.errorMessage;
	},

	onDownloadEnd: function(){
		// summary:
		//		called when download is finished
	}
});
<commit_msg>Don't check this.selected to see if a ContentPane is shown or not. If StackContainer/TabContainer is included on the page then every ContentPane has a "selected" attribute, even if it isn't a child of a StackContainer. Fixes #6048, refs #5697.<commit_after>dojo.provide("dijit.layout.ContentPane");

dojo.require("dijit._Widget");
dojo.require("dijit.layout._LayoutWidget");

dojo.require("dojo.parser");
dojo.require("dojo.string");
dojo.requireLocalization("dijit", "loading");

dojo.declare(
	"dijit.layout.ContentPane",
	dijit._Widget,
{
	// summary:
	//		A widget that acts as a Container for other widgets, and includes a ajax interface
	// description:
	//		A widget that can be used as a standalone widget
	//		or as a baseclass for other widgets
	//		Handles replacement of document fragment using either external uri or javascript
	//		generated markup or DOM content, instantiating widgets within that content.
	//		Don't confuse it with an iframe, it only needs/wants document fragments.
	//		It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
	//		But note that those classes can contain any widget as a child.
	// example:
	//		Some quick samples:
	//		To change the innerHTML use .setContent('new content')
	//
	//		Or you can send it a NodeList, .setContent(dojo.query('div [class=selected]', userSelection))
	//		please note that the nodes in NodeList will copied, not moved
	//
	//		To do a ajax update use .setHref('url')
	//
	// href: String
	//		The href of the content that displays now.
	//		Set this at construction if you want to load data externally when the
	//		pane is shown.  (Set preload=true to load it immediately.)
	//		Changing href after creation doesn't have any effect; see setHref();
	href: "",

	// extractContent: Boolean
	//	Extract visible content from inside of  ....
	extractContent: false,

	// parseOnLoad: Boolean
	//	parse content and create the widgets, if any
	parseOnLoad:	true,

	// preventCache: Boolean
	//		Cache content retreived externally
	preventCache:	false,

	// preload: Boolean
	//	Force load of data even if pane is hidden.
	preload: false,

	// refreshOnShow: Boolean
	//		Refresh (re-download) content when pane goes from hidden to shown
	refreshOnShow: false,

	// loadingMessage: String
	//	Message that shows while downloading
	loadingMessage: "${loadingState}",

	// errorMessage: String
	//	Message that shows if an error occurs
	errorMessage: "${errorState}",

	// isLoaded: Boolean
	//	Tells loading status see onLoad|onUnload for event hooks
	isLoaded: false,

	// class: String
	//	Class name to apply to ContentPane dom nodes
	// TODO: this should be called "baseClass" like in the other widgets
	"class": "dijitContentPane",

	// doLayout: String/Boolean
	//	false - don't adjust size of children
	//	true - looks for the first sizable child widget (ie, having resize() method) and sets it's size to
	//			however big the ContentPane is (TODO: implement)
	//	auto - if there is a single sizable child widget (ie, having resize() method), set it's size to
	//			however big the ContentPane is
	doLayout: "auto",

	postCreate: function(){
		// remove the title attribute so it doesn't show up when i hover
		// over a node
		this.domNode.title = "";

		if(!this.containerNode){
			// make getDescendants() work
			this.containerNode = this.domNode;
		}

		if(this.preload){
			this._loadCheck();
		}

		var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
		this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
		this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
		var curRole = dijit.getWaiRole(this.domNode);
		if (!curRole){
			dijit.setWaiRole(this.domNode, "group");
		}

		// for programatically created ContentPane (with  tag), need to muck w/CSS
		// or it's as though overflow:visible is set
		dojo.addClass(this.domNode, this["class"]);
	},

	startup: function(){
		if(this._started){ return; }
		if(this.doLayout != "false" && this.doLayout !== false){
			this._checkIfSingleChild();
			if(this._singleChild){
				this._singleChild.startup();
			}
		}
		this._loadCheck();
		this.inherited(arguments);
	},

	_checkIfSingleChild: function(){
		// summary:
		// 	Test if we have exactly one widget as a child, and if so assume that we are a container for that widget,
		//	and should propogate startup() and resize() calls to it.

		// TODO: if there are two child widgets (a data store and a TabContainer, for example),
		//	should still find the TabContainer
		var childNodes = dojo.query(">", this.containerNode || this.domNode),
			childWidgets = childNodes.filter("[widgetId]");

		if(childNodes.length == 1 && childWidgets.length == 1){
			this.isContainer = true;
			this._singleChild = dijit.byNode(childWidgets[0]);
		}else{
			delete this.isContainer;
			delete this._singleChild;
		}
	},

	refresh: function(){
		// summary:
		//	Force a refresh (re-download) of content, be sure to turn off cache

		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
		return this._prepareLoad(true);
	},

	setHref: function(/*String|Uri*/ href){
		// summary:
		//		Reset the (external defined) content of this pane and replace with new url
		//		Note: It delays the download until widget is shown if preload is false
		//	href:
		//		url to the page you want to get, must be within the same domain as your mainpage
		this.href = href;

		// we return result of _prepareLoad here to avoid code dup. in dojox.layout.ContentPane
		return this._prepareLoad();
	},

	setContent: function(/*String|DomNode|Nodelist*/data){
		// summary:
		//		Replaces old content with data content, include style classes from old content
		//	data:
		//		the new Content may be String, DomNode or NodeList
		//
		//		if data is a NodeList (or an array of nodes) nodes are copied
		//		so you can import nodes from another document implicitly

		// clear href so we cant run refresh and clear content
		// refresh should only work if we downloaded the content
		if(!this._isDownloaded){
			this.href = "";
			this._onUnloadHandler();
		}

		this._setContent(data || "");

		this._isDownloaded = false; // must be set after _setContent(..), pathadjust in dojox.layout.ContentPane

		if(this.parseOnLoad){
			this._createSubWidgets();
		}

		if(this.doLayout != "false" && this.doLayout !== false){
			this._checkIfSingleChild();
			if(this._singleChild && this._singleChild.resize){
				this._singleChild.startup();
				this._singleChild.resize(this._contentBox || dojo.contentBox(this.containerNode || this.domNode));
			}
		}

		this._onLoadHandler();
	},

	cancel: function(){
		// summary:
		//		Cancels a inflight download of content
		if(this._xhrDfd && (this._xhrDfd.fired == -1)){
			this._xhrDfd.cancel();
		}
		delete this._xhrDfd; // garbage collect
	},

	destroy: function(){
		// if we have multiple controllers destroying us, bail after the first
		if(this._beingDestroyed){
			return;
		}
		// make sure we call onUnload
		this._onUnloadHandler();
		this._beingDestroyed = true;
		this.inherited("destroy",arguments);
	},

	resize: function(size){
		dojo.marginBox(this.domNode, size);

		// Compute content box size in case we [later] need to size child
		// If either height or width wasn't specified by the user, then query node for it.
		// But note that setting the margin box and then immediately querying dimensions may return
		// inaccurate results, so try not to depend on it.
		var node = this.containerNode || this.domNode,
			mb = dojo.mixin(dojo.marginBox(node), size||{});

		this._contentBox = dijit.layout.marginBox2contentBox(node, mb);

		// If we have a single widget child then size it to fit snugly within my borders
		if(this._singleChild && this._singleChild.resize){
			this._singleChild.resize(this._contentBox);
		}
	},

	_prepareLoad: function(forceLoad){
		// sets up for a xhrLoad, load is deferred until widget onShow
		// cancels a inflight download
		this.cancel();
		this.isLoaded = false;
		this._loadCheck(forceLoad);
	},

	_isShown: function(){
		// summary: returns true if the content is currently shown
		if("open" in this){
			return this.open;		// for TitlePane, etc.
		}else{
			var node = this.domNode;
			return (node.style.display != 'none')  && (node.style.visibility != 'hidden');
		}
	},

	_loadCheck: function(/*Boolean*/ forceLoad){
		// call this when you change onShow (onSelected) status when selected in parent container
		// it's used as a trigger for href download when this.domNode.display != 'none'

		// sequence:
		// if no href -> bail
		// forceLoad -> always load
		// this.preload -> load when download not in progress, domNode display doesn't matter
		// this.refreshOnShow -> load when download in progress bails, domNode display !='none' AND
		//						this.open !== false (undefined is ok), isLoaded doesn't matter
		// else -> load when download not in progress, if this.open !== false (undefined is ok) AND
		//						domNode display != 'none', isLoaded must be false

		var displayState = this._isShown();

		if(this.href &&
			(forceLoad ||
				(this.preload && !this._xhrDfd) ||
				(this.refreshOnShow && displayState && !this._xhrDfd) ||
				(!this.isLoaded && displayState && !this._xhrDfd)
			)
		){
			this._downloadExternalContent();
		}
	},

	_downloadExternalContent: function(){
		this._onUnloadHandler();

		// display loading message
		this._setContent(
			this.onDownloadStart.call(this)
		);

		var self = this;
		var getArgs = {
			preventCache: (this.preventCache || this.refreshOnShow),
			url: this.href,
			handleAs: "text"
		};
		if(dojo.isObject(this.ioArgs)){
			dojo.mixin(getArgs, this.ioArgs);
		}

		var hand = this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs);

		hand.addCallback(function(html){
			try{
				self.onDownloadEnd.call(self);
				self._isDownloaded = true;
				self.setContent.call(self, html); // onload event is called from here
			}catch(err){
				self._onError.call(self, 'Content', err); // onContentError
			}
			delete self._xhrDfd;
			return html;
		});

		hand.addErrback(function(err){
			if(!hand.cancelled){
				// show error message in the pane
				self._onError.call(self, 'Download', err); // onDownloadError
			}
			delete self._xhrDfd;
			return err;
		});
	},

	_onLoadHandler: function(){
		this.isLoaded = true;
		try{
			this.onLoad.call(this);
		}catch(e){
			console.error('Error '+this.widgetId+' running custom onLoad code');
		}
	},

	_onUnloadHandler: function(){
		this.isLoaded = false;
		this.cancel();
		try{
			this.onUnload.call(this);
		}catch(e){
			console.error('Error '+this.widgetId+' running custom onUnload code');
		}
	},

	_setContent: function(cont){
		this.destroyDescendants();

		try{
			var node = this.containerNode || this.domNode;
			while(node.firstChild){
				dojo._destroyElement(node.firstChild);
			}
			if(typeof cont == "string"){
				// dijit.ContentPane does only minimal fixes,
				// No pathAdjustments, script retrieval, style clean etc
				// some of these should be available in the dojox.layout.ContentPane
				if(this.extractContent){
					match = cont.match(/]*>\s*([\s\S]+)\s*<\/body>/im);
					if(match){ cont = match[1]; }
				}
				node.innerHTML = cont;
			}else{
				// domNode or NodeList
				if(cont.nodeType){ // domNode (htmlNode 1 or textNode 3)
					node.appendChild(cont);
				}else{// nodelist or array such as dojo.Nodelist
					dojo.forEach(cont, function(n){
						node.appendChild(n.cloneNode(true));
					});
				}
			}
		}catch(e){
			// check if a domfault occurs when we are appending this.errorMessage
			// like for instance if domNode is a UL and we try append a DIV
			var errMess = this.onContentError(e);
			try{
				node.innerHTML = errMess;
			}catch(e){
				console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
			}
		}
	},

	_onError: function(type, err, consoleText){
		// shows user the string that is returned by on[type]Error
		// overide on[type]Error and return your own string to customize
		var errText = this['on' + type + 'Error'].call(this, err);
		if(consoleText){
			console.error(consoleText, err);
		}else if(errText){// a empty string won't change current content
			this._setContent.call(this, errText);
		}
	},

	_createSubWidgets: function(){
		// summary: scan my contents and create subwidgets
		var rootNode = this.containerNode || this.domNode;
		try{
			dojo.parser.parse(rootNode, true);
		}catch(e){
			this._onError('Content', e, "Couldn't create widgets in "+this.id
				+(this.href ? " from "+this.href : ""));
		}
	},

	// EVENT's, should be overide-able
	onLoad: function(e){
		// summary:
		//		Event hook, is called after everything is loaded and widgetified
	},

	onUnload: function(e){
		// summary:
		//		Event hook, is called before old content is cleared
	},

	onDownloadStart: function(){
		// summary:
		//		called before download starts
		//		the string returned by this function will be the html
		//		that tells the user we are loading something
		//		override with your own function if you want to change text
		return this.loadingMessage;
	},

	onContentError: function(/*Error*/ error){
		// summary:
		//		called on DOM faults, require fault etc in content
		//		default is to display errormessage inside pane
	},

	onDownloadError: function(/*Error*/ error){
		// summary:
		//		Called when download error occurs, default is to display
		//		errormessage inside pane. Overide function to change that.
		//		The string returned by this function will be the html
		//		that tells the user a error happend
		return this.errorMessage;
	},

	onDownloadEnd: function(){
		// summary:
		//		called when download is finished
	}
});
<|endoftext|>


<commit_before>(function(){
    var redirectKey = 'ir.loginRedirectRoute';
    Session.set(redirectKey, null);

    _.extend(Router, {
       _route: Router.route,
        route: function(name, options){
            if(options.redirectOnLogin && Session.get(redirectKey)){
                var rname = Session.get(redirectKey);
                Session.set(redirectKey, null);

                return this.redirect(rname);
            }

            if(options.loginRequired || options.redirectOnLogin){
                var before = null;
                if(options.before && _.isFunction(options.before)){
                    before = options.before;
                }

                options.before = function(){
                    if(Meteor.loggingIn())
                        return stop();

                    if(options.redirectOnLogin && Meteor.user()){
                        if(Session.get(redirectKey)){
                            var rname = Session.get(redirectKey);
                            Session.set(redirectKey, null);

                            return this.redirect(rname);
                        }
                    }

                    if(options.loginRequired){
                        if(typeof options.loginRequired == 'string' || options.loginRequired instanceof String){
                            var routeName = options.loginRequired;

                            options.loginRequired = {
                                name: routeName,
                                shouldRoute: true
                            };
                        }

                        if(!options.loginRequired.hasOwnProperty('shouldRoute')){
                            options.loginRequired.shouldRoute = true;
                        }

                        if(!Meteor.user()){
                            Session.set(redirectKey, Router.current().path);

                            if(options.loginRequired.shouldRoute){
                                return this.redirect(options.loginRequired.name);
                            }else{
                                if(options.loginRequired.layout){
                                    this.layoutTemplate = options.loginRequired.layout;
                                }
                                this.stop();
                                return this.render(options.loginRequired.name);
                            }
                        }
                    }

                    if(before)before.call(this);
                };
            }

            Router._route(name, options);
        }
    });
})();
<commit_msg>Change stop() to pause()<commit_after>(function(){
    var redirectKey = 'ir.loginRedirectRoute';
    Session.set(redirectKey, null);

    _.extend(Router, {
       _route: Router.route,
        route: function(name, options){
            if(options.redirectOnLogin && Session.get(redirectKey)){
                var rname = Session.get(redirectKey);
                Session.set(redirectKey, null);

                return this.redirect(rname);
            }

            if(options.loginRequired || options.redirectOnLogin){
                var before = null;
                if(options.before && _.isFunction(options.before)){
                    before = options.before;
                }

                options.before = function(pause){
                    if(Meteor.loggingIn())
                        return pause();

                    if(options.redirectOnLogin && Meteor.user()){
                        if(Session.get(redirectKey)){
                            var rname = Session.get(redirectKey);
                            Session.set(redirectKey, null);

                            return this.redirect(rname);
                        }
                    }

                    if(options.loginRequired){
                        if(typeof options.loginRequired == 'string' || options.loginRequired instanceof String){
                            var routeName = options.loginRequired;

                            options.loginRequired = {
                                name: routeName,
                                shouldRoute: true
                            };
                        }

                        if(!options.loginRequired.hasOwnProperty('shouldRoute')){
                            options.loginRequired.shouldRoute = true;
                        }

                        if(!Meteor.user()){
                            Session.set(redirectKey, Router.current().path);

                            if(options.loginRequired.shouldRoute){
                                return this.redirect(options.loginRequired.name);
                            }else{
                                if(options.loginRequired.layout){
                                    this.layoutTemplate = options.loginRequired.layout;
                                }
                                pause();
                                return this.render(options.loginRequired.name);
                            }
                        }
                    }

                    if(before)before.call(this);
                };
            }

            Router._route(name, options);
        }
    });
})();
<|endoftext|>


<commit_before>import Promise from 'promise';
import Emitter from 'component-emitter';
import errors from '../errors';

const debug = require('debug')('recurly:apple-pay');

export function factory (options) {
  return new ApplePay(Object.assign({}, options, { recurly: this }));
};

class ApplePay extends Emitter {
  constructor (options) {
    super();

    this._ready = false;
    this.config = {};
    this.once('ready', () => this._ready = true);

    if (!global.ApplePaySession) return this.fatal = this.error('apple-pay-not-supported');
    if (!ApplePaySession.canMakePayments()) return this.fatal = this.error('apple-pay-not-available');

    this.configure(options);
    if (!this.fatal) this.emit('ready');
  }

  get session () {
    if (this._session) return this._session;

    debug('Creating new Apple Pay session');

    let session = new ApplePaySession(2, {
      countryCode: this.config.country,
      currencyCode: this.config.currency,
      supportedNetworks: this.config.supportedNetworks,
      merchantCapabilities: this.config.merchantCapabilities,
      total: { label: this.config.label, amount: this.config.total }
    });

    session.onvalidatemerchant = this.onValidate.bind(this);
    session.onpaymentauthorized = e => console.info('onpaymentauthorized', e);
    session.onpaymentmethodselected = e => console.info('onpaymentmethodselected', e);
    session.onshippingcontactselected = e => console.info('onshippingcontactselected', e);
    session.onshippingmethodselected = e => console.info('onshippingmethodselected', e);
    session.oncancel = this.onCancel.bind(this);

    return this._session = session;
  }

  ready (cb) {
    if (this._ready) cb();
    else this.once('ready', cb);
  }

  configure (options) {
    if ('label' in options) this.config.label = options.label;
    else return this.fatal = this.error('apple-pay-config-missing', { opt: 'label' });

    if ('total' in options) this.config.total = options.total;
    else return this.fatal = this.error('apple-pay-config-missing', { opt: 'total' });

    if ('recurly' in options) this.recurly = options.recurly;
    else this.fatal = this.error('apple-pay-factory-only');

    // Retrieve remote configuration
    this.recurly.request('get', '/apple_pay/info', (err, info) => {
      if (err) return this.fatal = this.error(err);

      if ('countries' in info && ~info.countries.indexOf(options.country)) this.config.country = options.country;
      else return this.fatal = this.error('apple-pay-config-invalid', { opt: 'country', set: info.countries });

      if ('currencies' in info && ~info.currencies.indexOf(options.currency)) this.config.currency = options.currency;
      else return this.fatal = this.error('apple-pay-config-invalid', { opt: 'currency', set: info.currencies });

      this.config.merchantCapabilities = info.merchantCapabilities || [];
      this.config.supportedNetworks = info.supportedNetworks || [];
    });
  }

  begin (done) {
    if (this.fatal) return this.error('apple-pay-fatal-error', { err: this.fatal });
    delete this._session;
    this.session.begin();
  }

  /**
   * Validates the merchant session
   *
   * @param  {Event} event ApplePayValidateMerchantEvent
   * @return {[type]}       [description]
   */
  onValidate (event) {
    debug('Validating Apple Pay merchant session', event);

    const validationURL = event.validationURL;

    this.recurly.request('post', '/apple_pay/start', { validationURL }, (err, session) => {
      if (err) return this.error(err);
      this.session.completeMerchantValidation(session);
    });
  }

  onCancel (event) {
    debug('User canceled Apple Pay payment', event);

    // TODO: mark the session as dead?
  }

  error (...params) {
    let err = params[0] instanceof Error ? params[0] : errors(...params);
    this.emit('error', err);
    return err;
  }
}
<commit_msg>Adds apple pay payment method selection handler<commit_after>import Promise from 'promise';
import Emitter from 'component-emitter';
import errors from '../errors';
import {Pricing} from './pricing';

const debug = require('debug')('recurly:apple-pay');

const SESSION_TIMEOUT = 300000; // 5m

export function factory (options) {
  return new ApplePay(Object.assign({}, options, { recurly: this }));
};

class ApplePay extends Emitter {
  constructor (options) {
    super();

    this._ready = false;
    this.config = {};
    this.once('ready', () => this._ready = true);

    if (!global.ApplePaySession) return this.fatal = this.error('apple-pay-not-supported');
    if (!ApplePaySession.canMakePayments()) return this.fatal = this.error('apple-pay-not-available');

    this.configure(options);
    if (!this.fatal) this.emit('ready');
  }

  get session () {
    if (this._session) return this._session;

    debug('Creating new Apple Pay session');

    let session = new ApplePaySession(2, {
      countryCode: this.config.country,
      currencyCode: this.config.currency,
      supportedNetworks: this.config.supportedNetworks,
      merchantCapabilities: this.config.merchantCapabilities,
      total: { label: this.config.label, amount: this.config.total }
    });

    session.onvalidatemerchant = this.onValidate.bind(this);
    session.onpaymentauthorized = e => console.info('onpaymentauthorized', e);
    session.onpaymentmethodselected = this.onPaymentMethodSelected.bind(this);
    session.onshippingcontactselected = e => console.info('onshippingcontactselected', e);
    session.onshippingmethodselected = e => console.info('onshippingmethodselected', e);
    session.oncancel = this.onCancel.bind(this);

    return this._session = session;
  }

  ready (cb) {
    if (this._ready) cb();
    else this.once('ready', cb);
  }

  configure (options) {
    if ('label' in options) this.config.label = options.label;
    else return this.fatal = this.error('apple-pay-config-missing', { opt: 'label' });

    if ('pricing' in options) {
      this.config.pricing = options.pricing;
      this.config.pricing.on('change', p => this.config.total = p.now.total);
    } else if ('total' in options) {
      this.config.total = options.total;
    } else {
      return this.fatal = this.error('apple-pay-config-missing', { opt: 'total' });
    }

    if ('recurly' in options) this.recurly = options.recurly;
    else this.fatal = this.error('apple-pay-factory-only');

    // Retrieve remote configuration
    this.recurly.request('get', '/apple_pay/info', (err, info) => {
      if (err) return this.fatal = this.error(err);

      if ('countries' in info && ~info.countries.indexOf(options.country)) this.config.country = options.country;
      else return this.fatal = this.error('apple-pay-config-invalid', { opt: 'country', set: info.countries });

      if ('currencies' in info && ~info.currencies.indexOf(options.currency)) this.config.currency = options.currency;
      else return this.fatal = this.error('apple-pay-config-invalid', { opt: 'currency', set: info.currencies });

      this.config.merchantCapabilities = info.merchantCapabilities || [];
      this.config.supportedNetworks = info.supportedNetworks || [];
      this.config.supportedNetworks = ['visa'];
    });
  }

  begin (done) {
    if (this.fatal) return this.error('apple-pay-fatal-error', { err: this.fatal });
    delete this._session;
    this.session.begin();
  }

  /**
   * Validates the merchant session
   *
   * @param  {Event} event ApplePayValidateMerchantEvent
   * @return {[type]}       [description]
   */
  onValidate (event) {
    debug('Validating Apple Pay merchant session', event);

    const validationURL = event.validationURL;

    this.recurly.request('post', '/apple_pay/start', { validationURL }, (err, merchantSession) => {
      if (err) return this.error(err);
      console.info('session started', err, merchantSession);
      this.session.completeMerchantValidation(merchantSession);
    });
  }

  onPaymentMethodSelected (event) {
    debug('Payment method selected', event);

    // TODO: integrate pricing API
    const newTotal = {
      type: 'final',
      label: this.config.label,
      amount: this.config.total
    };

    const newLineItems = [
      { type: 'final', label: this.config.label, amount: this.config.total }
      // TODO: Apply discounts and shipping costs
    ];

    this.session.completePaymentMethodSelection(newTotal, newLineItems);
  }

  onCancel (event) {
    debug('User canceled Apple Pay payment', event);

    // TODO: mark the session as dead?
  }

  error (...params) {
    let err = params[0] instanceof Error ? params[0] : errors(...params);
    this.emit('error', err);
    return err;
  }
}
<|endoftext|>


<commit_before>var cadence = require('cadence')
var Procession = require('procession')

// Utilities.
var coalesce = require('extant')

// Evented stream reading and writing.
var Staccato = require('staccato')

// JSON for use in packets.
var Jacket = require('nascent.jacket')

// Ever increasing serial value with no maximum value.
var Monotonic = require('monotonic').asString

// Orderly destruction of complicated objects.
var Destructible = require('destructible')

var Signal = require('signal')

function Conduit (input, output) {
    this.destroyed = false
    this._input = new Staccato.Readable(input)
    this._output = new Staccato.Writable(output)
    this.read = new Procession
    this.write = new Procession
    this.wrote = new Procession
    this.write.pump(this, 'enqueue')
    this._record = new Jacket
    this._closed = new Signal
    this._destructible = new Destructible
    this._destructible.markDestroyed(this)
    this._destructible.addDestructor('closed', this._closed, 'unlatch')
    this.ready = new Signal
    this._destructible.addDestructor('ready', this.ready, 'unlatch')
}

Conduit.prototype.enqueue = cadence(function (async, envelope) {
    if (this.destroyed) {
        return []
    }
    async(function () {
        if (envelope == null) {
            // TODO And then destroy the conduit.
            this._output.write(JSON.stringify({
                module: 'conduit',
                method: 'trailer',
                body: null
            }) + '\n', async())
            this._closed.unlatch()
        } else {
            var e = envelope
            while (e.body != null && typeof e.body == 'object' && !Buffer.isBuffer(e.body)) {
                e = e.body
            }
            if (Buffer.isBuffer(e.body)) {
                var body = e.body
                e.body = null
                var packet = JSON.stringify({
                    module: 'conduit',
                    method: 'chunk',
                    length: body.length,
                    body: envelope
                }) + '\n'
                e.body = body
                this._output.write(packet, async())
                this._output.write(e.body, async())
            } else {
                this._output.write(JSON.stringify({
                    module: 'conduit',
                    method: 'envelope',
                    body: envelope
                }) + '\n', async())
            }
        }
    }, function () {
        this.wrote.enqueue(envelope, async())
    }, function () {
        return []
    })
})

Conduit.prototype._listen = cadence(function (async, buffer) {
    async(function () {
        this._parse(coalesce(buffer, new Buffer(0)), async())
        this.ready.unlatch()
    }, function () {
        this._read(async())
    }, function () {
        this._closed.wait(async())
    })
})

Conduit.prototype.listen = cadence(function (async, buffer) {
    this._destructible.addDestructor('shutdown', this, '_shutdown')
    this._listen(buffer, this._destructible.monitor('listen'))
    this._destructible.completed(async())
})

Conduit.prototype._shutdown = function () {
    this._output.destroy()
    this._input.destroy()
    for (var key in this._sockets) {
        var socket = this._sockets[key]
        socket.basin.requests.push(null)
        socket.spigot.responses.push(null)
        socket.basin.responses.push(null)
        socket.spigot.requests.push(null)
    }
}

Conduit.prototype.destroy = function () {
    this._destructible.destroy()
}

Conduit.prototype._buffer = cadence(function (async, buffer, start, end) {
    async(function () {
        var length = Math.min(buffer.length, this._chunk.length)
        var slice = buffer.slice(start, start + length)
        start += length
        this._chunk.length -= length
        var envelope = this._chunk.body
        if (this._chunk.length != 0) {
            envelope = JSON.parse(JSON.stringify(envelope))
        }
        var e = envelope
        while (e.body != null) {
            e = e.body
        }
        e.body = slice
        this.read.enqueue(envelope, async())
    }, function () {
        if (this._chunk.length == 0) {
            this._chunk = null
            this._record = new Jacket
        }
        return start
    })
})

Conduit.prototype._json = cadence(function (async, buffer, start, end) {
    start = this._record.parse(buffer, start, end)
    async(function () {
        if (this._record.object != null) {
            var envelope = this._record.object
            switch (envelope.method) {
            case 'header':
                var socket = new Socket(this, envelope.to, true)
                this._sockets[socket._serverKey] = socket
                // Not sure what to do in the case of these errors, no sockets
                // to send them through, so maybe we do just crash.
                this._connect.call(null, socket, envelope.body)
                break
            case 'envelope':
                this.read.enqueue(envelope.body, async())
                break
            case 'chunk':
                this._chunk = this._record.object
                break
            case 'trailer':
                // var socket = this._sockets[envelope.to]
                this.read.enqueue(null, async())
                // delete this._sockets[envelope.to]
                break
            }
            this._record = new Jacket
        }
    }, function () {
        return start
    })
})

Conduit.prototype._parse = cadence(function (async, buffer) {
    var parse = async(function (start) {
        if (start == buffer.length) {
            return [ parse.break ]
        }
        if (this._chunk != null) {
            this._buffer(buffer, start, buffer.length, async())
        } else {
            this._json(buffer, start, buffer.length, async())
        }
    })(0)
})

Conduit.prototype._read = cadence(function (async) {
    var read = async(function () {
        this._input.read(async())
    }, function (buffer) {
        if (buffer == null) {
            return [ read.break ]
        }
        this._parse(buffer, async())
    })()
})

module.exports = Conduit
<commit_msg>Add key to Conduit's Destructible.<commit_after>var cadence = require('cadence')
var Procession = require('procession')

// Utilities.
var coalesce = require('extant')

// Evented stream reading and writing.
var Staccato = require('staccato')

// JSON for use in packets.
var Jacket = require('nascent.jacket')

// Ever increasing serial value with no maximum value.
var Monotonic = require('monotonic').asString

// Orderly destruction of complicated objects.
var Destructible = require('destructible')

var Signal = require('signal')

function Conduit (input, output) {
    this.destroyed = false
    this._input = new Staccato.Readable(input)
    this._output = new Staccato.Writable(output)
    this.read = new Procession
    this.write = new Procession
    this.wrote = new Procession
    this.write.pump(this, 'enqueue')
    this._record = new Jacket
    this._closed = new Signal
    this._destructible = new Destructible('conduit')
    this._destructible.markDestroyed(this)
    this._destructible.addDestructor('closed', this._closed, 'unlatch')
    this.ready = new Signal
    this._destructible.addDestructor('ready', this.ready, 'unlatch')
}

Conduit.prototype.enqueue = cadence(function (async, envelope) {
    if (this.destroyed) {
        return []
    }
    async(function () {
        if (envelope == null) {
            // TODO And then destroy the conduit.
            this._output.write(JSON.stringify({
                module: 'conduit',
                method: 'trailer',
                body: null
            }) + '\n', async())
            this._closed.unlatch()
        } else {
            var e = envelope
            while (e.body != null && typeof e.body == 'object' && !Buffer.isBuffer(e.body)) {
                e = e.body
            }
            if (Buffer.isBuffer(e.body)) {
                var body = e.body
                e.body = null
                var packet = JSON.stringify({
                    module: 'conduit',
                    method: 'chunk',
                    length: body.length,
                    body: envelope
                }) + '\n'
                e.body = body
                this._output.write(packet, async())
                this._output.write(e.body, async())
            } else {
                this._output.write(JSON.stringify({
                    module: 'conduit',
                    method: 'envelope',
                    body: envelope
                }) + '\n', async())
            }
        }
    }, function () {
        this.wrote.enqueue(envelope, async())
    }, function () {
        return []
    })
})

Conduit.prototype._listen = cadence(function (async, buffer) {
    async(function () {
        this._parse(coalesce(buffer, new Buffer(0)), async())
        this.ready.unlatch()
    }, function () {
        this._read(async())
    }, function () {
        this._closed.wait(async())
    })
})

Conduit.prototype.listen = cadence(function (async, buffer) {
    this._destructible.addDestructor('shutdown', this, '_shutdown')
    this._listen(buffer, this._destructible.monitor('listen'))
    this._destructible.completed(async())
})

Conduit.prototype._shutdown = function () {
    this._output.destroy()
    this._input.destroy()
    for (var key in this._sockets) {
        var socket = this._sockets[key]
        socket.basin.requests.push(null)
        socket.spigot.responses.push(null)
        socket.basin.responses.push(null)
        socket.spigot.requests.push(null)
    }
}

Conduit.prototype.destroy = function () {
    this._destructible.destroy()
}

Conduit.prototype._buffer = cadence(function (async, buffer, start, end) {
    async(function () {
        var length = Math.min(buffer.length, this._chunk.length)
        var slice = buffer.slice(start, start + length)
        start += length
        this._chunk.length -= length
        var envelope = this._chunk.body
        if (this._chunk.length != 0) {
            envelope = JSON.parse(JSON.stringify(envelope))
        }
        var e = envelope
        while (e.body != null) {
            e = e.body
        }
        e.body = slice
        this.read.enqueue(envelope, async())
    }, function () {
        if (this._chunk.length == 0) {
            this._chunk = null
            this._record = new Jacket
        }
        return start
    })
})

Conduit.prototype._json = cadence(function (async, buffer, start, end) {
    start = this._record.parse(buffer, start, end)
    async(function () {
        if (this._record.object != null) {
            var envelope = this._record.object
            switch (envelope.method) {
            case 'header':
                var socket = new Socket(this, envelope.to, true)
                this._sockets[socket._serverKey] = socket
                // Not sure what to do in the case of these errors, no sockets
                // to send them through, so maybe we do just crash.
                this._connect.call(null, socket, envelope.body)
                break
            case 'envelope':
                this.read.enqueue(envelope.body, async())
                break
            case 'chunk':
                this._chunk = this._record.object
                break
            case 'trailer':
                // var socket = this._sockets[envelope.to]
                this.read.enqueue(null, async())
                // delete this._sockets[envelope.to]
                break
            }
            this._record = new Jacket
        }
    }, function () {
        return start
    })
})

Conduit.prototype._parse = cadence(function (async, buffer) {
    var parse = async(function (start) {
        if (start == buffer.length) {
            return [ parse.break ]
        }
        if (this._chunk != null) {
            this._buffer(buffer, start, buffer.length, async())
        } else {
            this._json(buffer, start, buffer.length, async())
        }
    })(0)
})

Conduit.prototype._read = cadence(function (async) {
    var read = async(function () {
        this._input.read(async())
    }, function (buffer) {
        if (buffer == null) {
            return [ read.break ]
        }
        this._parse(buffer, async())
    })()
})

module.exports = Conduit
<|endoftext|>


<commit_before>var gulp        = require('gulp');
var $           = require('gulp-load-plugins')();
var browserSync = require('browser-sync');
var cache       = require('gulp-cache');
var concat      = require('gulp-concat');
var cp          = require('child_process');
var footer      = require('gulp-footer');
var header      = require('gulp-header');
var minifyCss   = require('gulp-minify-css');
var ngmin       = require('gulp-ngmin');
var pagespeed   = require('psi');
var pkg         = require('./package.json');
var prefix      = require('gulp-autoprefixer');
var rename      = require('gulp-rename');
var sass        = require('gulp-sass');
var shell       = require('gulp-shell');
var uglify      = require('gulp-uglify');

var messages = {
  jekyllBuild: 'Running: $ jekyll build'
};

var AUTOPREFIXER_BROWSERS = [
  'ie >= 9',
  'ie_mob >= 9',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];
var closureStart =
  '/*!\n' +
  ' * Ionic IO\n' +
  ' * Copyright ' + new Date().getYear() + ' Drifty Co. http://drifty.com/\n' +
  ' */\n' +
  '(function() {\n';
var closureEnd = '\n})();\n';
var version = pkg.version;

gulp.task('styles:v2', function() {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src('_scss/v2.scss')
    // .pipe($.sourcemaps.init())
    .pipe(sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    //
    // .pipe($.sourcemaps.write())
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))

    // .pipe($.sourcemaps.write('./css/'))
    // Concatenate and minify styles
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(rename({extname: '.min.css'}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))
    .pipe($.size({title: 'styles'}));
});

gulp.task('styles:v1', function(done) {
  return gulp.src('scss/**/*.scss')
    .pipe(sass({onError: browserSync.notify}))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(rename({extname: '.min.css'}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'));
});

// Optimize images
gulp.task('images', function() {
  return gulp.src('_img/**/*')
    .pipe(cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('img'))
    .pipe(gulp.dest('_site/img'))
    .pipe($.size({title: 'images'}));
});

// compress and concat JS
gulp.task('js', function() {
  return gulp.src(['_js/**/*.js'])
    .pipe(concat('ionic-docs.js'))
    .pipe(header(closureStart))
    .pipe(footer(closureEnd))
    .pipe(gulp.dest('js'))
    .pipe(gulp.dest('_site/js'))
    .pipe(uglify())
    .pipe(rename({extname: '.min.js'}))
    .pipe(gulp.dest('js'))
    .pipe(gulp.dest('_site/js'))
    .pipe($.size({title: 'js'}));
});

/**
 * Build the Jekyll Site
 */
gulp.task('jekyll-build', [], function(done) {
  browserSync.notify(messages.jekyllBuild);
  return cp.spawn('jekyll',
                  ['build', '--config', '_config_development.yml'],
                  {stdio: 'inherit'})
           .on('close', done);
});

/**
 * Run Generate linkchecker page
 */
gulp.task('linkchecker', ['build'],
  shell.task('_scripts/linkchecker.sh',{verbose: true})
);

/**
 * Rebuild Jekyll & do page reload
 */
gulp.task('jekyll-rebuild', ['jekyll-build'], function() {
  browserSync.reload();
});

/**
 * Wait for jekyll-build, then launch the Server
 */
gulp.task('server', ['server:ionicons','server:stylesv1', 'server:stylesv2', 'images',
  'jekyll-build'
], function() {
  browserSync({
    server: {
      baseDir: '_site'
    }
  });
});

gulp.task('server:ionicons', ['ionicons'], function() {
  browserSync.reload();
});
gulp.task('server:stylesv1', ['styles:v1'], function() {
  browserSync.reload();
});
gulp.task('server:stylesv2', ['styles:v2'], function() {
  browserSync.reload();
});
gulp.task('server:jekyll', ['jekyll-build'], function() {
  browserSync.reload();
});
gulp.task('server:images', ['images'], function() {
  browserSync.reload();
});
gulp.task('server:js', ['js'], function() {
  browserSync.reload();
});

gulp.task('watch', ['server'], function() {
  gulp.watch('scss/**.scss', ['server:stylesv1']);
  gulp.watch(['_scss/*.scss', '_scss/docs/*.scss', '_scss/pages/*.scss'],
             ['server:stylesv2']);
  gulp.watch(['_img/*', '_img/*/*'], ['server:images']);
  gulp.watch(['_js/**/*.js', 'submit-issue/*/*.js'], ['server:js']);
  gulp.watch(['*.html', 'submit-issue/*.html', 'getting-started/*.html',
    '_layouts/*', '_layouts/*/*', '_posts/*', '_includes/**/*',
    'docs/**/*.{md,html,js,css}', '!docs/v2/2*', '!docs/v2/nightly',
    '!docs/1.*', 'dist/preview-app/www/**/*'
  ], ['server:jekyll']);

});

gulp.task('cli-docs', function() {
  try {
    var fs = require('fs');
    var cliTasks = JSON.stringify(require('../ionic-cli/lib/tasks/cliTasks'));
    fs.writeFileSync('_data/cliData.json', cliTasks);
  } catch (e) {
    console.error('Cannot find module cliTasks, please make sure the ionic-' +
                  'cli repo is cloned locally. ionic-site and ionic-cli ' +
                  'should be sibling directories.');
  }
});

gulp.task('docs.index', function() {
  var lunr = require('lunr');
  var gutil = require('gulp-util');
  var es = require('event-stream');
  var yaml = require('js-yaml');
  var htmlparser = require('htmlparser2');
  var mkdirp = require('mkdirp');
  var fs = require('fs');

  var idx = lunr(function() {
    this.field('path');
    this.field('title', {boost: 10});
    this.field('body');
    this.ref('id');
  });
  var ref = {};
  var refId = 0;

  function addToIndex(path, title, layout, body) {
    // Add the data to the indexer and ref object
    idx.add({'path': path, 'body': body, 'title': title, id: refId});
    ref[refId] = {'p': path, 't': title, 'l': layout};
    refId++;
  }

  var docPath = 'docs/v2';
  gutil.log('Reading docs from', gutil.colors.cyan(docPath));

  return gulp.src([
    docPath + '/{api,cli,components,faq,getting-started,native,resources,theming,utils}/**/*.{md,html,markdown}',
    docPath + '/index.md',
  ])
  .pipe(es.map(function(file, callback) {
    //docs for gulp file objects: https://github.com/wearefractal/vinyl
    var contents = file.contents.toString(); //was buffer

    // Grab relative path from ionic-site root
    var relpath = file.path.replace(RegExp('^.*?' + docPath.replace('/docs/v2', '') + '/'), '');

    // Read out the yaml portion of the Jekyll file
    var yamlStartIndex = contents.indexOf('---');

    if (yamlStartIndex === -1) {
      return callback();
    }

    // read Jekyll's page yaml variables at the top of the file
    var yamlEndIndex = contents.indexOf('---', yamlStartIndex+3); //starting from start
    var yamlRaw = contents.substring(yamlStartIndex+3, yamlEndIndex);

    var pageData =  yaml.safeLoad(yamlRaw);
    if(!pageData.title || !pageData.layout) {
      return callback();
    }

    // manually set to not be searchable, or for a blog post, manually set to be searchable
    if(pageData.searchable === false || (pageData.layout == 'post' && pageData.searchable !== true)) {
      return callback();
    }

    // clean up some content so code variables are searchable too
    contents = contents.substring(yamlEndIndex+3);
    contents = contents.replace(//gi, '');
    contents = contents.replace(/<\/code>/gi, '');
    contents = contents.replace(/</gi, '');
    contents = contents.replace(/><\/code>/gi, '');
    contents = contents.replace(/`</gi, '');
    contents = contents.replace(/>`/gi, '');

    // create a clean path to the URL
    var path = '/' + relpath.replace('index.md', '')
                            .replace('index.html', '')
                            .replace('.md', '.html')
                            .replace('.markdown', '.html');
    if(pageData.layout == 'post') {
      path = '/blog/' + path.substring(19).replace('.html', '/');
    }

    var parser;
    if(pageData.search_sections === true) {
      // each section within the content should be its own search result
      var section = { body: '', title: '' };
      var isTitleOpen = false;

      parser = new htmlparser.Parser({
        ontext: function(text){
          if(isTitleOpen) {
            section.title += text; // get the title of this section
          } else {
            section.body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions
          }
        },
        onopentag: function(name, attrs) {
          if(name == 'section' && attrs.id) {
            // start building new section data
            section = { body: '', path: path + '#' + attrs.id, title: '' };
          } else if( (name == 'h1' || name == 'h2' || name == 'h3') && attrs.class == 'title') {
            isTitleOpen = true; // the next text will be this sections title
          }
        },
        onclosetag: function(name) {
          if(name == 'section') {
            // section closed, index this section then clear it out
            addToIndex(section.path, section.title, pageData.layout, section.body);
            section = { body: '', title: '' };
          } else if( (name == 'h1' || name == 'h2' || name == 'h3') && isTitleOpen) {
            isTitleOpen = false;
          }
        }
      });
      parser.write(contents);
      parser.end();

    } else {
      // index the entire page
      var body = '';
      parser = new htmlparser.Parser({
        ontext: function(text){
          body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions
        }
      });
      parser.write(contents);
      parser.end();

      addToIndex(path, pageData.title, pageData.layout, body);
    }

    callback();

  })).on('end', function() {
    // Write out as one json file
    mkdirp.sync(docPath + '/data');
    fs.writeFileSync(
      docPath + '/data/index.json',
      JSON.stringify({'ref': ref, 'index': idx.toJSON()})
    );
  });
});

gulp.task('ionicons', function() {
  gulp.src('node_modules/ionicons/dist/css/ionicons.min.css')
    .pipe(gulp.dest('./css/v2-demos/ionicons/'));

  gulp.src('node_modules/ionicons/dist/fonts/*{eot,svg,ttf,woff}')
    .pipe(gulp.dest('./css/v2-demos/fonts/'));

  gulp.src('node_modules/ionicons/dist/data/ionicons.json')
    .pipe(rename('site_data.json'))
    .pipe(gulp.dest('./docs/v2/resources/ionicons/'));

  gulp.src('node_modules/ionicons/dist/data/mode-icons.json')
    .pipe(gulp.dest('./docs/v2/resources/ionicons/data/'));

  gulp.src('node_modules/ionicons/dist/data/logo-icons.json')
    .pipe(rename('generic-icons.json'))
    .pipe(gulp.dest('./docs/v2/resources/ionicons/data/'));
});

gulp.task('build', ['ionicons', 'cli-docs', 'styles:v1', 'styles:v2', 'jekyll-build', 'images', 'js']);
gulp.task('default', ['build']);
<commit_msg>watch nightly docs now<commit_after>var gulp        = require('gulp');
var $           = require('gulp-load-plugins')();
var browserSync = require('browser-sync');
var cache       = require('gulp-cache');
var concat      = require('gulp-concat');
var cp          = require('child_process');
var footer      = require('gulp-footer');
var header      = require('gulp-header');
var minifyCss   = require('gulp-minify-css');
var ngmin       = require('gulp-ngmin');
var pagespeed   = require('psi');
var pkg         = require('./package.json');
var prefix      = require('gulp-autoprefixer');
var rename      = require('gulp-rename');
var sass        = require('gulp-sass');
var shell       = require('gulp-shell');
var uglify      = require('gulp-uglify');

var messages = {
  jekyllBuild: 'Running: $ jekyll build'
};

var AUTOPREFIXER_BROWSERS = [
  'ie >= 9',
  'ie_mob >= 9',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];
var closureStart =
  '/*!\n' +
  ' * Ionic IO\n' +
  ' * Copyright ' + new Date().getYear() + ' Drifty Co. http://drifty.com/\n' +
  ' */\n' +
  '(function() {\n';
var closureEnd = '\n})();\n';
var version = pkg.version;

gulp.task('styles:v2', function() {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src('_scss/v2.scss')
    // .pipe($.sourcemaps.init())
    .pipe(sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    //
    // .pipe($.sourcemaps.write())
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))

    // .pipe($.sourcemaps.write('./css/'))
    // Concatenate and minify styles
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(rename({extname: '.min.css'}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))
    .pipe($.size({title: 'styles'}));
});

gulp.task('styles:v1', function(done) {
  return gulp.src('scss/**/*.scss')
    .pipe(sass({onError: browserSync.notify}))
    .pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'))
    .pipe(minifyCss({
      keepSpecialComments: 0
    }))
    .pipe(rename({extname: '.min.css'}))
    .pipe(gulp.dest('./css/'))
    .pipe(gulp.dest('./_site/css/'));
});

// Optimize images
gulp.task('images', function() {
  return gulp.src('_img/**/*')
    .pipe(cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('img'))
    .pipe(gulp.dest('_site/img'))
    .pipe($.size({title: 'images'}));
});

// compress and concat JS
gulp.task('js', function() {
  return gulp.src(['_js/**/*.js'])
    .pipe(concat('ionic-docs.js'))
    .pipe(header(closureStart))
    .pipe(footer(closureEnd))
    .pipe(gulp.dest('js'))
    .pipe(gulp.dest('_site/js'))
    .pipe(uglify())
    .pipe(rename({extname: '.min.js'}))
    .pipe(gulp.dest('js'))
    .pipe(gulp.dest('_site/js'))
    .pipe($.size({title: 'js'}));
});

/**
 * Build the Jekyll Site
 */
gulp.task('jekyll-build', [], function(done) {
  browserSync.notify(messages.jekyllBuild);
  return cp.spawn('jekyll',
                  ['build', '--config', '_config_development.yml'],
                  {stdio: 'inherit'})
           .on('close', done);
});

/**
 * Run Generate linkchecker page
 */
gulp.task('linkchecker', ['build'],
  shell.task('_scripts/linkchecker.sh',{verbose: true})
);

/**
 * Rebuild Jekyll & do page reload
 */
gulp.task('jekyll-rebuild', ['jekyll-build'], function() {
  browserSync.reload();
});

/**
 * Wait for jekyll-build, then launch the Server
 */
gulp.task('server', ['server:ionicons','server:stylesv1', 'server:stylesv2', 'images',
  'jekyll-build'
], function() {
  browserSync({
    server: {
      baseDir: '_site'
    }
  });
});

gulp.task('server:ionicons', ['ionicons'], function() {
  browserSync.reload();
});
gulp.task('server:stylesv1', ['styles:v1'], function() {
  browserSync.reload();
});
gulp.task('server:stylesv2', ['styles:v2'], function() {
  browserSync.reload();
});
gulp.task('server:jekyll', ['jekyll-build'], function() {
  browserSync.reload();
});
gulp.task('server:images', ['images'], function() {
  browserSync.reload();
});
gulp.task('server:js', ['js'], function() {
  browserSync.reload();
});

gulp.task('watch', ['server'], function() {
  gulp.watch('scss/**.scss', ['server:stylesv1']);
  gulp.watch(['_scss/*.scss', '_scss/docs/*.scss', '_scss/pages/*.scss'],
             ['server:stylesv2']);
  gulp.watch(['_img/*', '_img/*/*'], ['server:images']);
  gulp.watch(['_js/**/*.js', 'submit-issue/*/*.js'], ['server:js']);
  gulp.watch(['*.html', 'submit-issue/*.html', 'getting-started/*.html',
    '_layouts/*', '_layouts/*/*', '_posts/*', '_includes/**/*',
    'docs/**/*.{md,html,js,css}', '!docs/v2/2*', '!docs/1.*',
    'dist/preview-app/www/**/*'
  ], ['server:jekyll']);

});

gulp.task('cli-docs', function() {
  try {
    var fs = require('fs');
    var cliTasks = JSON.stringify(require('../ionic-cli/lib/tasks/cliTasks'));
    fs.writeFileSync('_data/cliData.json', cliTasks);
  } catch (e) {
    console.error('Cannot find module cliTasks, please make sure the ionic-' +
                  'cli repo is cloned locally. ionic-site and ionic-cli ' +
                  'should be sibling directories.');
  }
});

gulp.task('docs.index', function() {
  var lunr = require('lunr');
  var gutil = require('gulp-util');
  var es = require('event-stream');
  var yaml = require('js-yaml');
  var htmlparser = require('htmlparser2');
  var mkdirp = require('mkdirp');
  var fs = require('fs');

  var idx = lunr(function() {
    this.field('path');
    this.field('title', {boost: 10});
    this.field('body');
    this.ref('id');
  });
  var ref = {};
  var refId = 0;

  function addToIndex(path, title, layout, body) {
    // Add the data to the indexer and ref object
    idx.add({'path': path, 'body': body, 'title': title, id: refId});
    ref[refId] = {'p': path, 't': title, 'l': layout};
    refId++;
  }

  var docPath = 'docs/v2';
  gutil.log('Reading docs from', gutil.colors.cyan(docPath));

  return gulp.src([
    docPath + '/{api,cli,components,faq,getting-started,native,resources,theming,utils}/**/*.{md,html,markdown}',
    docPath + '/index.md',
  ])
  .pipe(es.map(function(file, callback) {
    //docs for gulp file objects: https://github.com/wearefractal/vinyl
    var contents = file.contents.toString(); //was buffer

    // Grab relative path from ionic-site root
    var relpath = file.path.replace(RegExp('^.*?' + docPath.replace('/docs/v2', '') + '/'), '');

    // Read out the yaml portion of the Jekyll file
    var yamlStartIndex = contents.indexOf('---');

    if (yamlStartIndex === -1) {
      return callback();
    }

    // read Jekyll's page yaml variables at the top of the file
    var yamlEndIndex = contents.indexOf('---', yamlStartIndex+3); //starting from start
    var yamlRaw = contents.substring(yamlStartIndex+3, yamlEndIndex);

    var pageData =  yaml.safeLoad(yamlRaw);
    if(!pageData.title || !pageData.layout) {
      return callback();
    }

    // manually set to not be searchable, or for a blog post, manually set to be searchable
    if(pageData.searchable === false || (pageData.layout == 'post' && pageData.searchable !== true)) {
      return callback();
    }

    // clean up some content so code variables are searchable too
    contents = contents.substring(yamlEndIndex+3);
    contents = contents.replace(//gi, '');
    contents = contents.replace(/<\/code>/gi, '');
    contents = contents.replace(/</gi, '');
    contents = contents.replace(/><\/code>/gi, '');
    contents = contents.replace(/`</gi, '');
    contents = contents.replace(/>`/gi, '');

    // create a clean path to the URL
    var path = '/' + relpath.replace('index.md', '')
                            .replace('index.html', '')
                            .replace('.md', '.html')
                            .replace('.markdown', '.html');
    if(pageData.layout == 'post') {
      path = '/blog/' + path.substring(19).replace('.html', '/');
    }

    var parser;
    if(pageData.search_sections === true) {
      // each section within the content should be its own search result
      var section = { body: '', title: '' };
      var isTitleOpen = false;

      parser = new htmlparser.Parser({
        ontext: function(text){
          if(isTitleOpen) {
            section.title += text; // get the title of this section
          } else {
            section.body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions
          }
        },
        onopentag: function(name, attrs) {
          if(name == 'section' && attrs.id) {
            // start building new section data
            section = { body: '', path: path + '#' + attrs.id, title: '' };
          } else if( (name == 'h1' || name == 'h2' || name == 'h3') && attrs.class == 'title') {
            isTitleOpen = true; // the next text will be this sections title
          }
        },
        onclosetag: function(name) {
          if(name == 'section') {
            // section closed, index this section then clear it out
            addToIndex(section.path, section.title, pageData.layout, section.body);
            section = { body: '', title: '' };
          } else if( (name == 'h1' || name == 'h2' || name == 'h3') && isTitleOpen) {
            isTitleOpen = false;
          }
        }
      });
      parser.write(contents);
      parser.end();

    } else {
      // index the entire page
      var body = '';
      parser = new htmlparser.Parser({
        ontext: function(text){
          body += text.replace(/{%.*%}/, '', 'g'); // Ignore any Jekyll expressions
        }
      });
      parser.write(contents);
      parser.end();

      addToIndex(path, pageData.title, pageData.layout, body);
    }

    callback();

  })).on('end', function() {
    // Write out as one json file
    mkdirp.sync(docPath + '/data');
    fs.writeFileSync(
      docPath + '/data/index.json',
      JSON.stringify({'ref': ref, 'index': idx.toJSON()})
    );
  });
});

gulp.task('ionicons', function() {
  gulp.src('node_modules/ionicons/dist/css/ionicons.min.css')
    .pipe(gulp.dest('./css/v2-demos/ionicons/'));

  gulp.src('node_modules/ionicons/dist/fonts/*{eot,svg,ttf,woff}')
    .pipe(gulp.dest('./css/v2-demos/fonts/'));

  gulp.src('node_modules/ionicons/dist/data/ionicons.json')
    .pipe(rename('site_data.json'))
    .pipe(gulp.dest('./docs/v2/resources/ionicons/'));

  gulp.src('node_modules/ionicons/dist/data/mode-icons.json')
    .pipe(gulp.dest('./docs/v2/resources/ionicons/data/'));

  gulp.src('node_modules/ionicons/dist/data/logo-icons.json')
    .pipe(rename('generic-icons.json'))
    .pipe(gulp.dest('./docs/v2/resources/ionicons/data/'));
});

gulp.task('build', ['ionicons', 'cli-docs', 'styles:v1', 'styles:v2', 'jekyll-build', 'images', 'js']);
gulp.task('default', ['build']);
<|endoftext|>


<commit_before>var GOVUK = GOVUK || {};
GOVUK.Analytics = GOVUK.Analytics || {};

GOVUK.Analytics.startAnalytics = function () {
    var shouldIDoAnalyticsForThisPage = function () {
        if (!GOVUK.Analytics.NeedID) return false;
        var artefactURL = document.URL.split('/').slice(0, 4).join('/');
        return (document.referrer.substr(0, artefactURL.length) !== artefactURL);
    };

    var trackingStrategies = {
        "guide":GOVUK.Analytics.guideTracking,
        "transaction":GOVUK.Analytics.transactionTracking
    };

    var success = false;

    var trackSuccess = function () {
        if (success) return;
        success = true;
        GOVUK.sendToAnalytics(['_trackEvent', 'MS_' + GOVUK.Analytics.Format, GOVUK.Analytics.NeedID, 'Success']);
    };

    var handleExternalLink = function() {
        if (success) return;
        success = true;
        var slug = document.URL.split('/')[3];
        var target = $(this).attr('href');
        var exitLink = '/exit?slug=' + slug + '&target=' + target + '&needId=' + GOVUK.Analytics.NeedID;
        $(this).prop('href', exitLink);
    };

    var handleInternalLink = function () {
        if (success) return;
        success = true;
        GOVUK.Analytics.internalSiteEvents.push(['_trackEvent', 'MS_' + GOVUK.Analytics.Format, GOVUK.Analytics.NeedID, 'Success'])
    };

    var control = {
        trackLinks:function (selector) {
            $(selector).each(function () {
                var linkToTrack = $(this);
                var trackingFunction;
                if (linkToTrack.attr('rel') === 'external') {
                    trackingFunction = handleExternalLink;
                } else {
                    trackingFunction = handleInternalLink;
                }
                linkToTrack.click(trackingFunction);
            });
        },
        trackTimeBasedSuccess:function (time) {
            setTimeout(trackSuccess, time);
        }
    };

    var format = GOVUK.Analytics.Format;
    if (shouldIDoAnalyticsForThisPage() && trackingStrategies[format]) {
        GOVUK.sendToAnalytics(['_trackEvent', 'MS_' + format, GOVUK.Analytics.NeedID, 'Entry']);
        trackingStrategies[format](control);
    }

    GOVUK.Analytics.internalSiteEvents.sendAll();

    return control;
};

$(GOVUK.Analytics.startAnalytics);<commit_msg>Refactor out event construction<commit_after>var GOVUK = GOVUK || {};
GOVUK.Analytics = GOVUK.Analytics || {};

GOVUK.Analytics.startAnalytics = function () {
    var shouldIDoAnalyticsForThisPage = function () {
        if (!GOVUK.Analytics.NeedID) return false;
        var artefactURL = document.URL.split('/').slice(0, 4).join('/');
        return (document.referrer.substr(0, artefactURL.length) !== artefactURL);
    };

    var trackingStrategies = {
        "guide":GOVUK.Analytics.guideTracking,
        "transaction":GOVUK.Analytics.transactionTracking
    };

    var success = false;

    var createEvent = function(type) {
        return ['_trackEvent', 'MS_' + GOVUK.Analytics.Format, GOVUK.Analytics.NeedID, type];
    };

    var trackSuccess = function () {
        if (success) return;
        success = true;
        GOVUK.sendToAnalytics(createEvent("Success"));
    };

    var handleExternalLink = function() {
        if (success) return;
        success = true;
        var slug = document.URL.split('/')[3];
        var target = $(this).attr('href');
        var exitLink = '/exit?slug=' + slug + '&target=' + target + '&needId=' + GOVUK.Analytics.NeedID;
        $(this).prop('href', exitLink);
    };

    var handleInternalLink = function () {
        if (success) return;
        success = true;
        GOVUK.Analytics.internalSiteEvents.push(createEvent("Success"));
    };

    var control = {
        trackLinks:function (selector) {
            $(selector).each(function () {
                var linkToTrack = $(this);
                var trackingFunction;
                if (linkToTrack.attr('rel') === 'external') {
                    trackingFunction = handleExternalLink;
                } else {
                    trackingFunction = handleInternalLink;
                }
                linkToTrack.click(trackingFunction);
            });
        },
        trackTimeBasedSuccess:function (time) {
            setTimeout(trackSuccess, time);
        }
    };

    var format = GOVUK.Analytics.Format;
    if (shouldIDoAnalyticsForThisPage() && trackingStrategies[format]) {
        GOVUK.sendToAnalytics(createEvent("Entry"));
        trackingStrategies[format](control);
    }

    GOVUK.Analytics.internalSiteEvents.sendAll();

    return control;
};

$(GOVUK.Analytics.startAnalytics);<|endoftext|>


<commit_before>'use strict';
let columnify = require('columnify');
let api = require('./shared.js');
let cli = require('heroku-cli-util');

module.exports = {
  topic: 'redis',
  command: 'info',
  needsApp: true,
  needsAuth: true,
  args: [{name: 'database', optional: true}],
  description: 'gets information about redis',
  run: cli.command(function *(context, heroku) {
    let addons = yield heroku.apps(context.app).addons().listByApp();
    // filter out non-redis addons
    addons = api.make_addons_filter(context.args.database)(addons);
    // get info for each db
    let databases = yield addons.map(function* (addon) {
      return yield {
        addon: addon,
        redis: api.request(context, addon.name).catch(function (err) {
          if (err.statusCode !== 404) {
            throw(err);
          }
          return null;
        })
      };
    });

    // print out the info of the addon and redis db info
    for (let db of databases) {
      if (db.redis === null) {
        continue;
      }

      console.log(`=== ${db.addon.name} (${db.addon.config_vars.join(', ')})`);
      console.log(columnify(db.redis.info, {showHeaders: false}));
      console.log();
    }
  })
};
<commit_msg>Make redis:info the default command<commit_after>'use strict';
let columnify = require('columnify');
let api = require('./shared.js');
let cli = require('heroku-cli-util');

module.exports = {
  topic: 'redis',
  command: 'info',
  needsApp: true,
  needsAuth: true,
  default: true,
  args: [{name: 'database', optional: true}],
  description: 'gets information about redis',
  run: cli.command(function *(context, heroku) {
    let addons = yield heroku.apps(context.app).addons().listByApp();
    // filter out non-redis addons
    addons = api.make_addons_filter(context.args.database)(addons);
    // get info for each db
    let databases = yield addons.map(function* (addon) {
      return yield {
        addon: addon,
        redis: api.request(context, addon.name).catch(function (err) {
          if (err.statusCode !== 404) {
            throw(err);
          }
          return null;
        })
      };
    });

    // print out the info of the addon and redis db info
    for (let db of databases) {
      if (db.redis === null) {
        continue;
      }

      console.log(`=== ${db.addon.name} (${db.addon.config_vars.join(', ')})`);
      console.log(columnify(db.redis.info, {showHeaders: false}));
      console.log();
    }
  })
};
<|endoftext|>


<commit_before>$(document).ready(function() {
	$('#foodTable').on('click', '.clickable-row', function(event) {
		if($(this).hasClass('active')){
			$(this).removeClass('active');
		} else {
			$(this).addClass('active').siblings().removeClass('active');
		}
	});

	$(document).on('click', '#addIngredient', function() {
		var ingredient = $('.clickable-row.active');
		var name = ingredient.find('.ingredient-name').text();
		var quantity = $('#q').val();
		var pathArray = window.location.pathname.split('/');
		var date = pathArray[4] + "-" + pathArray[5] + "-" + pathArray[6];

		var postData = {
			'ingredientName' : name,
			'quantity' : quantity,
			'date' : date
		};

		//console.log(postData);
		//console.log("http://localhost/index.php/FoodController/addMealIngredient");
		$.ajax({
			type: "GET",
			url: "http://localhost/index.php/FoodController/addMealIngredient",
			data: postData,
			success: function(ingredientData) {
				var stringData = JSON.stringify(ingredientData);
				var parsedData = $.parseJSON(JSON.stringify(ingredientData));
				//console.log(parsedData);

			$('#ingredientTable tr:last').after('
'+name+'	'+quantity+'	'+parsedData[0].ingredient_energy+'	'+parsedData[0].carbohydrates+'	'+parsedData[0].fat+'	'+parsedData[0].protein+'
');
			},
			error: function(xhr, status, error) {
					console.log(xhr);
					console.log(status);
					console.log(error);
				}
		});
		return false;
	});
});<commit_msg>Frontend validation toidukorda lisamisele<commit_after>$(document).ready(function() {
	$('#foodTable').on('click', '.clickable-row', function(event) {
		if($(this).hasClass('active')){
			$(this).removeClass('active');
		} else {
			$(this).addClass('active').siblings().removeClass('active');
		}
	});

	$(document).on('click', '#addIngredient', function() {
		var ingredient = $('.clickable-row.active');
		var name = ingredient.find('.ingredient-name').text();
		var quantity = $('#q').val();
		var pathArray = window.location.pathname.split('/');
		var date = pathArray[4] + "-" + pathArray[5] + "-" + pathArray[6];

		var postData = {
			'ingredientName' : name,
			'quantity' : quantity,
			'date' : date
		};

		// TODO backend validation w/ language support
		if (quantity <= 0) {
			alert('quantity <= 0');
			$('#q').val('');
		} else {

		//console.log(postData);
		//console.log("http://localhost/index.php/FoodController/addMealIngredient");
		$.ajax({
			type: "GET",
			url: "http://localhost/index.php/FoodController/addMealIngredient",
			data: postData,
			success: function(ingredientData) {
				var stringData = JSON.stringify(ingredientData);
				var parsedData = $.parseJSON(JSON.stringify(ingredientData));
				//console.log(parsedData);

			$('#ingredientTable tr:last').after('
'+name+'	'+quantity+'	'+parsedData[0].ingredient_energy+'	'+parsedData[0].carbohydrates+'	'+parsedData[0].fat+'	'+parsedData[0].protein+'
');
			},
			error: function(xhr, status, error) {
					console.log(xhr);
					console.log(status);
					console.log(error);
				}
		});
		}
		return false;
	});
});<|endoftext|>