<commit_before>var users, me;

function sendMsg(msg, callback) {
    chrome.extension.sendMessage(msg, callback);
}
function getCartId(callback) {
    sendMsg({fn: 'getCartId'}, callback);
}
function joinToCart(cart) {
    sendMsg({fn: 'join', guid: cart}, function () {
        console.log('got new cart', cart);
        me.cart = cart;
        callPageFunction('_ShoppingCart.ReloadShoppingCart');
    });
}
function initSocket(user, callback) {
    sendMsg({fn: 'login', user: user}, callback);
}

function refreshUsers(callback) {
    sendMsg({fn: 'refreshUsers'}, callback);
}

function shareCart(callback) {
        initSocket(me, function (user) {
            me = user;
        callback(null, 'success');
        });

    }

// updates cart
var actions = {
    updatePeers: function (event) {
        event.cart = me.cart;
        sendMsg(event);
    },
    updateUsers: function (data) {
        if (_.get(data, 'users')) {
            users = data.users;
            renderUsers(users);
        }
    },
    updateDish: function (data) {
        console.log(data.cart, me.cart);
        if (data.cart == me.cart) {
            console.log('disturbance in the force felt');
            callPageFunction('_ShoppingCart.ReloadShoppingCart');
    }
    }
};

function renderUsers(users) {
    var index = 0;
    createElement('div', '', document.getElementsByClassName('menu')[0], null, 'users');
    for (var key in users) {
        index++;
        createElement('div', '', document.getElementsByClassName('users')[0], null, 'user' + index + ' user');
        createElement('div', users[key].name, document.getElementsByClassName('user' + index)[0], null, 'username');
        createElement('span', 'join', document.getElementsByClassName('user' + index)[0], function(){
            join(users[key]);
        }, 'join');
    }
}

function listenOnChanges(fn) {
    var scriptContent = 'var extId="' + chrome.runtime.id + '";$(document).bind("MenuDishesChanged QuantityChanged MealDealRemoved", ' + fn + ')';
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}
function listenOnOrderConfirm(fn) {
    var scriptContent = '$(document).bind("MOrderConfirmed", ' + fn + ')';
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}
function onChange() {
    chrome.runtime.sendMessage(extId, {fn: 'updatePeers', event: 'update'});
}

//function onConfirm() {
//    chrome.runtime.sendMessage({fn: 'updatePeers', event: 'order'});
//}
function callPageFunction(name) {
    var scriptContent = name + "();";
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}

function main() {
    var rawData = $('.HeaderTexture[data-login-user-email]').data();
    if (rawData) {
        me = {
            name: rawData.loginUserName,
            mail: rawData.loginUserEmail
        };
        getCartId(function (cart) {
            me.cart = cart;
    injectMenu();
    refreshUsers(actions.updateUsers);
            listenOnChanges(onChange);
            //listenOnOrderConfirm(onConfirm);
        });
    } else {
        callback('no user');
}

}

function injectMenu() {
    createElement('div', '', document.body, null, 'content');
    createElement('div', '', document.getElementsByClassName('content')[0], null, 'menu');
    createElement('div', 'x', document.getElementsByClassName('menu')[0], closeMenu, 'close');
    createElement('button', 'share', document.getElementsByClassName('menu')[0], share, 'share');
    createElement('div', 'Cart sharing menu', document.getElementsByClassName('content')[0], openMenu, 'handle');

}

function createElement(type, html, locationElement, click, className) {
    //Create an input type dynamically.
    var element = document.createElement(type);
    //Assign different attributes to the element.
    element.type = 'button';
    element.innerHTML = html;
    element.onclick = click;
    element.className = className;
    element.style = {
        width: '100%'
    };

    //Append the element in page (in span).
    locationElement.appendChild(element);
}

function share() {
    shareCart(function (err, data) {
        console.log(err || data);
    });
}

function join(user) {
    //to do - validate uuid
    if (user.cart) {
        joinToCart(user.cart);
        //refresh the delivery ui
        if (location.pathname.indexOf("/Restaurants/Menu/Delivery/") === -1) {
            //not restaurant page
            location.reload();
        } else {
            $("div[data-shoppingCart-main-div='true']").show();
            callPageFunction('_ShoppingCart.ReloadShoppingCart');
        }
    }
}

function openMenu() {
    $(".content .handle").css('left', $(".content .handle").width() * -1 + 'px');
    $(".content .menu").css('left', 0);
}

function closeMenu() {
    $(".content .menu").css('left', 220 * -1 + 'px');
    setTimeout(function () {
        $(".content .handle").css('left', 0);
    }, 200);
}

function messageHandler(request, sender, sendResponse) {
    console.log('got msg', request.fn);
    actions[request.fn](request);
}

chrome.runtime.onMessage.addListener(messageHandler); // messages from background script

var readyStateCheckInterval = setInterval(function () {
    if (document.readyState === "complete") {
        clearInterval(readyStateCheckInterval);
        main();
    }
});<commit_msg>master<commit_after>var users, me;

function sendMsg(msg, callback) {
    chrome.extension.sendMessage(msg, callback);
}
function getCartId(callback) {
    sendMsg({fn: 'getCartId'}, callback);
}
function joinToCart(cart) {
    sendMsg({fn: 'join', guid: cart}, function () {
        console.log('got new cart', cart);
        me.cart = cart;
        callPageFunction('_ShoppingCart.ReloadShoppingCart');
    });
}
function initSocket(user, callback) {
    sendMsg({fn: 'login', user: user}, callback);
}

function refreshUsers(callback) {
    sendMsg({fn: 'refreshUsers'}, callback);
}

function shareCart(callback) {
        initSocket(me, function (user) {
            me = user;
        callback(null, 'success');
        });
    }

// updates cart
var actions = {
    updatePeers: function (event) {
        event.cart = me.cart;
        sendMsg(event);
    },
    updateUsers: function (data) {
        console.log("updateUsers",data);
        if (_.get(data, 'users')) {
            console.log("data.users",data.users);
            users = data.users;
            renderUsers(users);
        }
    },
    updateDish: function (data) {
        console.log(data.cart, me.cart);
        if (data.cart == me.cart) {
            console.log('disturbance in the force felt');
            callPageFunction('_ShoppingCart.ReloadShoppingCart');
    }
    }
};

function renderUsers(users) {
    var index = 0;
    createElement('div', '', document.getElementsByClassName('menu')[0], null, 'users');
    for (var key in users) {
        index++;
        createElement('div', '', document.getElementsByClassName('users')[0], null, 'user' + index + ' user');
        createElement('div', users[key].name, document.getElementsByClassName('user' + index)[0], null, 'username');
        createElement('span', 'join', document.getElementsByClassName('user' + index)[0], function(){
            join(users[key]);
        }, 'join');
    }
}

function listenOnChanges(fn) {
    var scriptContent = 'var extId="' + chrome.runtime.id + '";$(document).bind("MenuDishesChanged QuantityChanged MealDealRemoved", ' + fn + ')';
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}
function listenOnOrderConfirm(fn) {
    var scriptContent = '$(document).bind("MOrderConfirmed", ' + fn + ')';
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}
function onChange() {
    chrome.runtime.sendMessage(extId, {fn: 'updatePeers', event: 'update'});
}

//function onConfirm() {
//    chrome.runtime.sendMessage({fn: 'updatePeers', event: 'order'});
//}
function callPageFunction(name) {
    var scriptContent = name + "();";
    var script = document.createElement('script');
    script.id = 'tmpScript';
    script.appendChild(document.createTextNode(scriptContent));
    (document.body || document.head || document.documentElement).appendChild(script);
    $("#tmpScript").remove();
}

function main() {
    var rawData = $('.HeaderTexture[data-login-user-email]').data();
    if (rawData) {
        me = {
            name: rawData.loginUserName,
            mail: rawData.loginUserEmail
        };
        getCartId(function (cart) {
            me.cart = cart;
    injectMenu();
    refreshUsers(actions.updateUsers);
            listenOnChanges(onChange);
            //listenOnOrderConfirm(onConfirm);
        });
    } else {
        callback('no user');
}

}

function injectMenu() {
    createElement('div', '', document.body, null, 'content');
    createElement('div', '', document.getElementsByClassName('content')[0], null, 'menu');
    createElement('div', 'x', document.getElementsByClassName('menu')[0], closeMenu, 'close');
    createElement('button', 'share', document.getElementsByClassName('menu')[0], share, 'share');
    createElement('div', 'Cart sharing menu', document.getElementsByClassName('content')[0], openMenu, 'handle');

}

function createElement(type, html, locationElement, click, className) {
    //Create an input type dynamically.
    var element = document.createElement(type);
    //Assign different attributes to the element.
    element.type = 'button';
    element.innerHTML = html;
    element.onclick = click;
    element.className = className;
    element.style = {
        width: '100%'
    };

    //Append the element in page (in span).
    locationElement.appendChild(element);
}

function share() {
    shareCart(function (err, data) {
        console.log(err || data);
    });
}

function join(user) {
    //to do - validate uuid
    if (user.cart) {
        joinToCart(user.cart);
        //refresh the delivery ui
        if (location.pathname.indexOf("/Restaurants/Menu/Delivery/") === -1) {
            //not restaurant page
            location.reload();
        } else {
            $("div[data-shoppingCart-main-div='true']").show();
            callPageFunction('_ShoppingCart.ReloadShoppingCart');
        }
    }
}

function openMenu() {
    $(".content .handle").css('left', $(".content .handle").width() * -1 + 'px');
    $(".content .menu").css('left', 0);
}

function closeMenu() {
    $(".content .menu").css('left', 220 * -1 + 'px');
    setTimeout(function () {
        $(".content .handle").css('left', 0);
    }, 200);
}

function messageHandler(request, sender, sendResponse) {
    console.log('got msg', request.fn);
    actions[request.fn](request);
}

chrome.runtime.onMessage.addListener(messageHandler); // messages from background script

var readyStateCheckInterval = setInterval(function () {
    if (document.readyState === "complete") {
        clearInterval(readyStateCheckInterval);
        main();
    }
});<|endoftext|>


<commit_before>chrome.extension.sendMessage({}, function(response) {

	stopWords = {'commenting':1,
				'great':5}

	function huntTrolls(){
		var comments = $('.comment-renderer-text-content');
      	comments.each(function() {
      		commentScore = rateComment($(this).text().toLowerCase());
      		if (commentScore > 0){
      			actionOnTrollComment(this,commentScore);
				}
		});
	}

	function actionOnTrollComment(trollComment,score){

		console.log($( trollComment ).text()  +' score: '+ score);
  		$(trollComment)
	  		.closest(".comment-renderer")
	  		.parent()
	  		.css("background-color", "yellow");
  		// $(".comment-renderer").hide();
	}

	function rateComment(comment){
		var score = 0;
		for (var word in stopWords) {
		    if (stopWords.hasOwnProperty(word)) {
		        if (comment.indexOf(word) !== -1)
		        	score += stopWords[word]
		    }
		}
		return score
	}

	  function pollVisibility() {
      if (!$(".action-panel-loading").is(":visible")) {
          // call a function here, or do whatever now that the div is not visible
		  huntTrolls();
      } else {
          setTimeout(pollVisibility, 500);
      }
  	}

	var readyStateCheckInterval = setInterval(function() {
	if (document.readyState === "complete") {
		clearInterval(readyStateCheckInterval);

		// ----------------------------------------------------------
		// This part of the script triggers when page is done loading
		console.log("Hello. This message was sent from scripts/inject.js");
		// ----------------------------------------------------------

		pollVisibility();
	}
	}, 10);
});
<commit_msg>Update inject.js<commit_after>chrome.extension.sendMessage({}, function(response) {

	function huntTrolls(){
		var comments = $('.comment-renderer-text-content');
      	comments.each(function() {
      		commentScore = rateComment($(this).text().toLowerCase());
      		if (commentScore > 0){
      			actionOnTrollComment(this,commentScore);
				}
		});
	}

	function actionOnTrollComment(trollComment,score){

		console.log($( trollComment ).text()  +' score: '+ score);
  		$(trollComment)
	  		.closest(".comment-renderer")
	  		.parent()
	  		.css("background-color", "yellow");
  		// $(".comment-renderer").hide();
	}

	function rateComment(comment){
		var score = 0;
		for (var word in wordScores) {
		    if (wordScores.hasOwnProperty(word)) {
		        if (comment.indexOf(word) !== -1)
		        	score += wordScores[word]
		    }
		}
		return score
	}

	  function pollVisibility() {
      if (!$(".action-panel-loading").is(":visible")) {
          // call a function here, or do whatever now that the div is not visible
		  huntTrolls();
      } else {
          setTimeout(pollVisibility, 500);
      }
  	}

	var readyStateCheckInterval = setInterval(function() {
	if (document.readyState === "complete") {
		clearInterval(readyStateCheckInterval);

		// ----------------------------------------------------------
		// This part of the script triggers when page is done loading
		console.log("Hello. This message was sent from scripts/inject.js");
		// ----------------------------------------------------------

		pollVisibility();
	}
	}, 10);
});
<|endoftext|>


<commit_before>(function($, undefined) {

	/**
	 * @author Radek Šerý
	 * @author Jiří Pudil
	 *
	 * Extension pro ajaxový pdbox, včetně historie (volitelně). Pro použití stačí pro ovládací element (tj. ten
	 * s class thickbox) přidat class ajax, pak např. data-spinner=".pd-box-content" atd., další extension by měla být
	 * kompatibilní :)
	 *
	 * @todo pokud otevřu TB, zavřu TB, zafiltruji, otevřu nový TB a pak v historii přejdu přímo na ten 1. otevřený (tj. přeskočím několik mezikroků), pak po zavření TB budu mít URL nekonzistentní vůči obsahu. Nevím, zda je to vůbec řešitelné, respektive jak snadno/těžko.
	 */
	$.nette.ext('pdbox', {
		init: function () {
			if (this.historySupported) {
				$(window).on('popstate.pdbox', $.proxy(function (e) {
					var state = e.originalEvent.state || this.history.initialState;
					this.historyEnabled = true; // protože jde o popstate, jde o TB s historií (pokud je to TB, není třeba řešit tady), tj. nastavíme historyEnabled na true, aby po zavření křížkem došlo k pushState
					this.popstate = true;

					// při popstate kontrolujeme, zda nový stav má nastavenou vlastnost pdbox, pokud ano, otevřeme jej (metoda open kontroluje, zda již otevřený není), vložíme obsah a spustíme událost load
					if ('pdbox' in state && state.pdbox) {
						this.box.open();
						this.box.content(state.pdbox.content);
						this.box.setOptions(state.pdbox.options);
						this.box.dispatchEvent('load', {content: state.pdbox.content});
					} else {
						// pokud pdbox není, zavřeme jej (metoda close opět kontroluje, zda je TB otevřený)
						this.box.close();
					}

					// udržujeme si aktuální stav, díky němu víme, kam se vracet (viz metoda open a push do original) i v případě popstate
					// upravit až po otevření TB (pokud se otevíral, není třeba kontrolovat)
					this.lastState = {
						location: location.href,
						state: state,
						title: document.title
					};
				}, this));

				if (this.box) {
					this.box.addEventListener('open', $.proxy(function () {
						// po otevření TB vždy uložíme kontext, který je pod TB, aby bylo možné po zavření vše vrátit zpět
						if (this.popstate) {
							this.original.push(this.lastState);
						}
						else {
							var state = {
								location: location.href,
								state: history.state,
								title: document.title
							};
							this.original.push(state);
						}

						// navážeme událost po zavření, preventivně ji předtím odstraňujeme, aby nebyla duplicitní
						this.box.removeEventListener('afterClose');
						this.box.addEventListener('afterClose', $.proxy(this.pushOriginal, this));
					}, this));
				}
			}
		},
		before: function (xhr, settings) {
			xhr.setRequestHeader('Pd-Box-Opened', +this.box.isOpen);
			this.box.addEventListener('afterClose', function () {
				xhr.abort();
			});
		},
		success: function (payload, status, xhr, settings) {
			this.popstate = false;
			this.lastState = {
				location: location.href,
				state: history.state,
				title: document.title
			};

			// pokud success nastal po kliknutí na elementu s class thickbox, vyvoláme load událost, nastavíme vlastnosti TB a pokud je povolená historie, nahradíme stávající stav naším, kde přidáváme do state vlastnost pdbox
			if (('nette' in settings && 'el' in settings.nette && settings.nette.el.is('.thickbox')) || settings.pdbox) {
				if (payload.redirect) {
					var options = {
						url: payload.redirect,
						pdbox: true
					};

					if ($.inArray('history', settings.off) !== -1 || !this.historySupported) {
						options.off = ['history'];
					}
					if ('spinnerQueue' in settings) {
						options.spinnerQueue = settings.spinnerQueue.slice(0);
						settings.spinnerQueue = [];
					}

					$.nette.ajax(options);

					if (this.historySupported) {
						this.historyEnabled = true;
					}

					return;
				}

				this.box.window.content.find('a.ajax').addClass('thickbox');
				this.box.dispatchEvent('load', {content: payload});

				if ($.inArray('history', settings.off) === -1 && this.historySupported) {
					this.historyEnabled = true;

					pdbox = {
						content: this.box.content(),
						options: this.box.options
					};

					history.replaceState($.extend(history.state || {}, {
						title: document.title,
						pdbox: pdbox,
						ui: findSnippets()
					}), document.title, location.href);
				}
			}
		}
	}, {
		historySupported: window.history && history.pushState && window.history.replaceState && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/),
		historyEnabled: false, // zabrání/povolí pushState po zavření okna TB; jdeme-li zpět (popstate), tak při zavření TB nechceme vkládat nový stav do historie, dále brání vložení stavu pro TB bez historie (např. obrázkový TB)
		history: $.nette.ext('history'),
		popstate: false,
		pushOriginal: function () {
			// fce pro vložení stavu do historie, volá se po zavření TB; nový stav vkládá pouze pokud má co vložit (tj. víme co je pod TB) a pokud nezavíráme TB tlačítkem zpět
			// pop stavu se vyvolá vždy při zavření TB, pushState ale chceme jen pokud není zavření vyvoláno popstatem
			if (this.historyEnabled) {
				var state = this.original.pop();
				if (state) {
					if (history.state === undefined || (history.state.href != state.location)) { // zavření TB tlačítkem zpět pop je stejný stav, jako aktuální a nechceme jej zduplikovat
						history.pushState(state.state, state.title, state.location);
						document.title = state.title;

						this.popstate = false;
						this.lastState = {
							location: state.location,
							state: state.state,
							title: state.title
						};
					}
				}

				this.historyEnabled = false;
			}
		},
		original: [], // zásobník stavů "pod TB" - po zavření TB musíme nastavit URL, title a stav do historie přes pushState
		lastState: null,
		box: null
	});

})(jQuery);
<commit_msg>Extension: pdbox.ajax.js findSnippets function<commit_after>(function($, undefined) {

	/**
	 * @author Radek Šerý
	 * @author Jiří Pudil
	 *
	 * Extension pro ajaxový pdbox, včetně historie (volitelně). Pro použití stačí pro ovládací element (tj. ten
	 * s class thickbox) přidat class ajax, pak např. data-spinner=".pd-box-content" atd., další extension by měla být
	 * kompatibilní :)
	 *
	 * @todo pokud otevřu TB, zavřu TB, zafiltruji, otevřu nový TB a pak v historii přejdu přímo na ten 1. otevřený (tj. přeskočím několik mezikroků), pak po zavření TB budu mít URL nekonzistentní vůči obsahu. Nevím, zda je to vůbec řešitelné, respektive jak snadno/těžko.
	 */
	$.nette.ext('pdbox', {
		init: function () {
			if (this.historySupported) {
				$(window).on('popstate.pdbox', $.proxy(function (e) {
					var state = e.originalEvent.state || this.history.initialState;
					this.historyEnabled = true; // protože jde o popstate, jde o TB s historií (pokud je to TB, není třeba řešit tady), tj. nastavíme historyEnabled na true, aby po zavření křížkem došlo k pushState
					this.popstate = true;

					// při popstate kontrolujeme, zda nový stav má nastavenou vlastnost pdbox, pokud ano, otevřeme jej (metoda open kontroluje, zda již otevřený není), vložíme obsah a spustíme událost load
					if ('pdbox' in state && state.pdbox) {
						this.box.open();
						this.box.content(state.pdbox.content);
						this.box.setOptions(state.pdbox.options);
						this.box.dispatchEvent('load', {content: state.pdbox.content});
					} else {
						// pokud pdbox není, zavřeme jej (metoda close opět kontroluje, zda je TB otevřený)
						this.box.close();
					}

					// udržujeme si aktuální stav, díky němu víme, kam se vracet (viz metoda open a push do original) i v případě popstate
					// upravit až po otevření TB (pokud se otevíral, není třeba kontrolovat)
					this.lastState = {
						location: location.href,
						state: state,
						title: document.title
					};
				}, this));

				if (this.box) {
					this.box.addEventListener('open', $.proxy(function () {
						// po otevření TB vždy uložíme kontext, který je pod TB, aby bylo možné po zavření vše vrátit zpět
						if (this.popstate) {
							this.original.push(this.lastState);
						}
						else {
							var state = {
								location: location.href,
								state: history.state,
								title: document.title
							};
							this.original.push(state);
						}

						// navážeme událost po zavření, preventivně ji předtím odstraňujeme, aby nebyla duplicitní
						this.box.removeEventListener('afterClose');
						this.box.addEventListener('afterClose', $.proxy(this.pushOriginal, this));
					}, this));
				}
			}
		},
		before: function (xhr, settings) {
			xhr.setRequestHeader('Pd-Box-Opened', +this.box.isOpen);
			this.box.addEventListener('afterClose', function () {
				xhr.abort();
			});
		},
		success: function (payload, status, xhr, settings) {
			this.popstate = false;
			this.lastState = {
				location: location.href,
				state: history.state,
				title: document.title
			};

			// pokud success nastal po kliknutí na elementu s class thickbox, vyvoláme load událost, nastavíme vlastnosti TB a pokud je povolená historie, nahradíme stávající stav naším, kde přidáváme do state vlastnost pdbox
			if (('nette' in settings && 'el' in settings.nette && settings.nette.el.is('.thickbox')) || settings.pdbox) {
				if (payload.redirect) {
					var options = {
						url: payload.redirect,
						pdbox: true
					};

					if ($.inArray('history', settings.off) !== -1 || !this.historySupported) {
						options.off = ['history'];
					}
					if ('spinnerQueue' in settings) {
						options.spinnerQueue = settings.spinnerQueue.slice(0);
						settings.spinnerQueue = [];
					}

					$.nette.ajax(options);

					if (this.historySupported) {
						this.historyEnabled = true;
					}

					return;
				}

				this.box.window.content.find('a.ajax').addClass('thickbox');
				this.box.dispatchEvent('load', {content: payload});

				if ($.inArray('history', settings.off) === -1 && this.historySupported) {
					this.historyEnabled = true;

					pdbox = {
						content: this.box.content(),
						options: this.box.options
					};

					history.replaceState($.extend(history.state || {}, {
						title: document.title,
						pdbox: pdbox,
						ui: $.nette.ext('snippets').findSnippets()
					}), document.title, location.href);
				}
			}
		}
	}, {
		historySupported: window.history && history.pushState && window.history.replaceState && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/),
		historyEnabled: false, // zabrání/povolí pushState po zavření okna TB; jdeme-li zpět (popstate), tak při zavření TB nechceme vkládat nový stav do historie, dále brání vložení stavu pro TB bez historie (např. obrázkový TB)
		history: $.nette.ext('history'),
		popstate: false,
		pushOriginal: function () {
			// fce pro vložení stavu do historie, volá se po zavření TB; nový stav vkládá pouze pokud má co vložit (tj. víme co je pod TB) a pokud nezavíráme TB tlačítkem zpět
			// pop stavu se vyvolá vždy při zavření TB, pushState ale chceme jen pokud není zavření vyvoláno popstatem
			if (this.historyEnabled) {
				var state = this.original.pop();
				if (state) {
					if (history.state === undefined || (history.state.href != state.location)) { // zavření TB tlačítkem zpět pop je stejný stav, jako aktuální a nechceme jej zduplikovat
						history.pushState(state.state, state.title, state.location);
						document.title = state.title;

						this.popstate = false;
						this.lastState = {
							location: state.location,
							state: state.state,
							title: state.title
						};
					}
				}

				this.historyEnabled = false;
			}
		},
		original: [], // zásobník stavů "pod TB" - po zavření TB musíme nastavit URL, title a stav do historie přes pushState
		lastState: null,
		box: null
	});

})(jQuery);
<|endoftext|>


<commit_before>/**
 * Copyright 2015 creditease Inc. All rights reserved.
 * @description marmot server command
 * @author evan2x(evan2zaw@gmail.com/aiweizhang@creditease.cn)
 * @date  2015/07/27
 */

import fs from 'fs';
import path from 'path';
import os from 'os';
import net from 'net';
import { spawn } from 'child_process';

import { pd } from 'pretty-data';
import chalk from 'chalk';
import del from 'del';

import * as _ from './helper';
import * as tmpl from './template';
import pkg from '../package.json';
import {
    CWD,
    USERHOME,
    TOMCAT_PATH,
    SERVER_CONFIG_PATH,
    TOMCAT_START_IDENTIFIER
} from './constant';

/**
 * 检查JDK是否安装
 * @return {Promise}
 */
function checkJDK() {
    let regex = /\b((?:\d+\.){2}\d+(?:_\d+)?)\b/;

    return new Promise((resolve, reject) => {
        let jdk = spawn('java', ['-version']),
            version = null,
            fail = () => {
                console.error(chalk.red('Please installing the JDK Software and Setting Environment Variables'));
                process.exit(1);
                reject();
            };

        jdk.stderr.on('data', data => {
            let ret = data.toString().match(regex);
            if(!version && ret){
                version = ret[0];
                console.log('JVM version: %s', version);
                resolve();
            }
        });

        jdk.on('error', fail).on('exit', () => !version && fail());
    });
}

/**
 * 检查tomcat是否安装，没有安装则下载安装tomcat
 * @return {Promise}
 */
function checkTomcat() {
    return new Promise((resolve, reject) => {
        if(fs.existsSync(TOMCAT_PATH)){
            resolve();
        } else {
            _.download(pkg._server, USERHOME)
            .then(file => _.untargz({
                pack: file,
                target: TOMCAT_PATH,
                strip: 1
            }))
            .then(resolve)
            .catch(reject);
        }
    });
}

/**
 * 执行服务器脚本
 * @param  {String} name 命令名称
 * @return {Child_Process}
 */
function execCatalinaScript(name){
    if(!name) return null;

    let command = null,
        suffix = '.sh',
        args = [name],
        scriptPath = '';

    if(_.isWin()){
        suffix = '.bat';
        // windows的startup.bat、shutdown.bat被改造了下
        // 将CATALINA_HOME环境变量指向tomcat所在的位置
        args.push(TOMCAT_PATH);
    }

    scriptPath = path.join(TOMCAT_PATH, 'bin', `catalina${suffix}`);
    if(fs.existsSync(scriptPath)){
        command = spawn(scriptPath, args);
    }

    return command;
}

/**
 * 启动tomcat server
 * @param {Object} opts
 * @param {Boolean} opts.hasMessage 是否显示提示信息
 * @param {Boolean} opts.port 端口号
 * @return {Promise}
 */
function startTomcat(opts = {}){
    let
        /**
         * 使用tomcat自带脚本启动server
         */
        startup = () => {

            return new Promise((resolve, reject) => {
                let command = execCatalinaScript('start');

                if(command == null){
                    opts.hasMessage && console.error(chalk.red('starting tomcat server has encountered a error'));
                    reject();
                } else {
                    fs.writeFileSync(TOMCAT_START_IDENTIFIER, '');
                    opts.hasMessage && console.log(chalk.green('tomcat server start successfully'));
                    resolve();
                }

                // windows下1秒后退出当前进程...
                if(_.isWin()){
                    setTimeout(() => process.exit(0), 1000);
                }
            });
        },

        /**
         * 检查service配置，两种情况:
         * 1.存在同名的service，只修改port
         * 2.创建全新的service
         * @return {Promise}
         */
        checkService = () => {

            return new Promise((resolve, reject) => {
                let options = path.parse(CWD),
                    $ = _.readServerFile(),
                    service = $(`Service[name="${options.base}"]`);

                // 清理项目已经删除的相关service
                $('Service').each((index, item) => {
                    let $item = $(item),
                        docBase = $item.find('Context').attr('docBase') || '',
                        appBase = $item.find('Host').attr('appBase') || '';

                    if(!fs.existsSync(path.join(appBase, docBase))){
                        $item.remove();
                    }
                });

                // service已经存在的情况下只调整port
                if(service[0]){
                    service.find('Connector').attr('port', opts.port);

                // 生成一个service配置
                } else {
                    // 没有端口的情况下不创建Service
                    if(!opts.port){
                        resolve();
                        return;
                    }
                    //检查server.xml是否有重复的端口号
                    if($(`Connector[port="${opts.port}"]`, 'Service')[0]){
                        opts.hasMessage && console.error(chalk.red('port already in use'));
                        reject();
                        return;
                    }

                    $('Server').append(tmpl.service({
                        name: options.base,
                        dir: options.dir,
                        port: opts.port
                    }));
                }

                // 重写server.xml文件
                fs.writeFile(SERVER_CONFIG_PATH, pd.xml($.html()), err => {

                    if(err) reject(err);
                    resolve();
                });
            });
        },
        /**
         * 使用node net模块创建一个server用来测试端口是否处于可用状态
         * @return {Promise}
         */
        checkPort = () => {
            return new Promise((resolve, reject) => {
                let sniffer = net.createServer();

                sniffer.on('error', () => {
                    opts.hasMessage && console.error(chalk.red('port already in use'));
                    reject();
                });

                sniffer.once('close', resolve);
                sniffer.listen(opts.port, sniffer.close);
            });
        };

    return (
        checkPort()
        .then(checkService)
        .then(() => {
            return stopTomcat();
        })
        .then(startup)
    );
}

/**
 * 使用tomcat自带脚本停止server
 * @param {Object} opts
 * @param {Boolean} opts.hasMessage 是否显示提示信息
 * @return {Promise}
 */
function stopTomcat(opts = {}){

    return new Promise((resolve, reject) => {
        let errMsg = chalk.red('stopping tomcat server has encountered a error'),
            command = execCatalinaScript('stop');

        if(command == null){
            opts.hasMessage && console.error(errMsg);
            reject();
            return;
        }

        command.on('error', () => {
            opts.hasMessage && console.error(errMsg);
            reject();
        });

        command.on('exit', () => {
            del.sync(TOMCAT_START_IDENTIFIER, {force: true});
            opts.hasMessage && console.log(chalk.green('tomcat server stop successfully'));
            resolve();
        });
    });
}

/**
 * 显示tomcat配置中已经存在的service
 */
function showServices(){
    let $ = _.readServerFile(),
        list = [];

    // 提取services组成二维数组
    $('Service').each((index, item) => {
        let $item = $(item),
            name = $item.attr('name'),
            port = $item.find('Connector').attr('port'),
            base = path.join($item.find('Host').attr('appBase'), name);
        list.push([name, port, base]);
    });

    _.printTables({
        head: ['name', 'port', 'path'],
        body: list
    });
}

/**
 * 根据端口号删除对应的service
 * @param  {String} port
 */
function removeServiceByName(name){
    if(typeof name === 'boolean'){
        return;
    }

    let $ = _.readServerFile(),
        hasExist = false;

    $('Service').each((index, item) => {
        let $item = $(item);
        if($item.attr('name') === name){
            hasExist = true;
            $item.remove();
            return false;
        }
    });

    if(!hasExist){
        console.error(chalk.red(`service "${name}" is not found`));
        return;
    }

    fs.writeFile(SERVER_CONFIG_PATH, pd.xml($.html()), err => {
        if(err){
            console.error(err);
            return;
        }

        console.log(chalk.green('remove the service successfully'));
        if(fs.existsSync(TOMCAT_START_IDENTIFIER)){
            startTomcat();
        }
    });
}

export default options => {
    // 是否同时存在-s, --start和-S, --stop两个参数
    if(options.start && options.stop){
        console.log(chalk.red('\'-s, --start\' and \'-S, --stop\' are mutually exclusive'));
        return;
    }

    if(options.list){
        showServices();
        return;
    }

    if(options.remove){
        removeServiceByName(options.remove);
        return;
    }

    let clean = null;
    // 存在-c, --clean的话删除掉已下载的tomcat并停止tomcat进程
    if(options.clean){
        clean = stopTomcat().then(() => {
            return del(TOMCAT_PATH, {force: true});
        });
    }

    if(options.start || options.stop){
        Promise.resolve(clean)
        .then(checkJDK)
        .then(checkTomcat)
        .then(() => {
            if(options.start){
                startTomcat({hasMessage: true, port: options.port});
            } else if(options.stop){
                stopTomcat({hasMessage: true});
            }
        });
    }
};
<commit_msg>fix bug<commit_after>/**
 * Copyright 2015 creditease Inc. All rights reserved.
 * @description marmot server command
 * @author evan2x(evan2zaw@gmail.com/aiweizhang@creditease.cn)
 * @date  2015/07/27
 */

import fs from 'fs';
import path from 'path';
import os from 'os';
import net from 'net';
import { spawn } from 'child_process';

import chalk from 'chalk';
import del from 'del';

import * as _ from './helper';
import * as tmpl from './template';
import pkg from '../package.json';
import {
    CWD,
    USERHOME,
    TOMCAT_PATH,
    SERVER_CONFIG_PATH,
    TOMCAT_START_IDENTIFIER
} from './constant';

/**
 * 检查JDK是否安装
 * @return {Promise}
 */
function checkJDK() {
    let regex = /\b((?:\d+\.){2}\d+(?:_\d+)?)\b/;

    return new Promise((resolve, reject) => {
        let jdk = spawn('java', ['-version']),
            version = null,
            fail = () => {
                console.error(chalk.red('Please installing the JDK Software and Setting Environment Variables'));
                process.exit(1);
                reject();
            };

        jdk.stderr.on('data', data => {
            let ret = data.toString().match(regex);
            if(!version && ret){
                version = ret[0];
                console.log('JVM version: %s', version);
                resolve();
            }
        });

        jdk.on('error', fail).on('exit', () => !version && fail());
    });
}

/**
 * 检查tomcat是否安装，没有安装则下载安装tomcat
 * @return {Promise}
 */
function checkTomcat() {
    return new Promise((resolve, reject) => {
        if(fs.existsSync(TOMCAT_PATH)){
            resolve();
        } else {
            _.download(pkg._server, USERHOME)
            .then(file => _.untargz({
                pack: file,
                target: TOMCAT_PATH,
                strip: 1
            }))
            .then(resolve)
            .catch(reject);
        }
    });
}

/**
 * 执行服务器脚本
 * @param  {String} name 命令名称
 * @return {Child_Process}
 */
function execCatalinaScript(name){
    if(!name) return null;

    let command = null,
        suffix = '.sh',
        args = [name],
        scriptPath = '';

    if(_.isWin()){
        suffix = '.bat';
        // windows的startup.bat、shutdown.bat被改造了下
        // 将CATALINA_HOME环境变量指向tomcat所在的位置
        args.push(TOMCAT_PATH);
    }

    scriptPath = path.join(TOMCAT_PATH, 'bin', `catalina${suffix}`);
    if(fs.existsSync(scriptPath)){
        command = spawn(scriptPath, args);
    }

    return command;
}

/**
 * 启动tomcat server
 * @param {Object} opts
 * @param {Boolean} opts.hasMessage 是否显示提示信息
 * @param {Boolean} opts.port 端口号
 * @return {Promise}
 */
function startTomcat(opts = {}){
    let options = path.parse(CWD),
        $ = _.readServerFile(),
        // 匹配服务是否存在
        service = $(`Service[name="${options.base}"]`),
        // 匹配已使用指定端口的connector
        connector = $(`Connector[port="${opts.port}"]`, 'Service'),

        /**
         * 使用tomcat自带脚本启动server
         * @return {Promise}
         */
        startup = () => {
            return new Promise((resolve, reject) => {
                let command = execCatalinaScript('start');

                if(command == null){
                    opts.hasMessage && console.error(chalk.red('starting tomcat server has encountered a error'));
                    reject();
                } else {
                    fs.writeFileSync(TOMCAT_START_IDENTIFIER, '');
                    opts.hasMessage && console.log(chalk.green('tomcat server start successfully'));
                    resolve();
                }

                // windows下1秒后退出当前进程...
                if(_.isWin()){
                    setTimeout(() => process.exit(0), 1000);
                }
            });
        },

        /**
         * 检查端口是否被占用
         * @return {Promise}
         */
        checkPort = () => {
            return new Promise((resolve, reject) => {
                let portExists = chalk.red(`port [${opts.port}] is already in use`);
                // 检查service配置中当前端口是否被占用
                if(connector[0] && !service.has(connector)[0]){
                    opts.hasMessage && console.error(portExists);
                    reject();
                    return;
                }

                // 使用net模块创建一个server用来测试端口是否处于可用状态
                let sniffer = net.createServer();

                sniffer.once('error', () => {
                    // 如果端口被包含在当前服务中，提示为当前服务已占用此端口
                    if(service.has(connector)[0]){
                        opts.hasMessage && console.error(chalk.red(`the current service is already using port [${opts.port}]`));
                    } else {
                        opts.hasMessage && console.error(portExists);
                    }
                    reject();
                });

                sniffer.once('close', resolve);
                sniffer.listen(opts.port, sniffer.close);
            });
        },

        /**
         * 设置端口号
         * @todo 如果指定端口号与当前服务所使用的端口号一致则不设置
         * @return {Promise}
         */
        setPort = () => {
            return new Promise((resolve, reject) => {

                if(!opts.port || service.has(connector)[0]){
                    resolve();
                    return;
                }

                if(service[0]){
                    service.find('Connector').attr('port', opts.port);
                } else {
                    $('Server').append(tmpl.service({
                        name: options.base,
                        dir: options.dir,
                        port: opts.port
                    }));
                }
                _.writeServerFile($.html()).then(resolve).catch(reject);
            });
        },

        /**
         * 清理项目已经删除的相关service
         * @return {Promise}
         */
        cleanService = () => {
            return new Promise((resolve, reject) => {
                $('Service').each((index, item) => {
                    let $item = $(item),
                        docBase = $item.find('Context').attr('docBase') || '',
                        appBase = $item.find('Host').attr('appBase') || '';

                    if(!fs.existsSync(path.join(appBase, docBase))){
                        $item.remove();
                    }
                });

                _.writeServerFile($.html()).then(resolve).catch(reject);
            });
        };

    return (
        checkPort()
        .then(setPort)
        .then(stopTomcat)
        .then(cleanService)
        .then(startup)
    );
}

/**
 * 使用tomcat自带脚本停止server
 * @param {Object} opts
 * @param {Boolean} opts.hasMessage 是否显示提示信息
 * @return {Promise}
 */
function stopTomcat(opts = {}){

    return new Promise((resolve, reject) => {
        let errMsg = chalk.red('stopping tomcat server has encountered a error'),
            command = execCatalinaScript('stop');

        if(command == null){
            opts.hasMessage && console.error(errMsg);
            reject();
            return;
        }

        command.on('error', () => {
            opts.hasMessage && console.error(errMsg);
            reject();
        });

        command.on('exit', () => {
            del.sync(TOMCAT_START_IDENTIFIER, {force: true});
            opts.hasMessage && console.log(chalk.green('tomcat server stop successfully'));
            resolve();
        });
    });
}

/**
 * 显示tomcat配置中已经存在的service
 */
function showServices(){
    let $ = _.readServerFile(),
        list = [];

    // 提取services组成二维数组
    $('Service').each((index, item) => {
        let $item = $(item),
            name = $item.attr('name'),
            port = $item.find('Connector').attr('port'),
            base = path.join($item.find('Host').attr('appBase'), name);
        list.push([name, port, base]);
    });

    _.printTables({
        head: ['name', 'port', 'path'],
        body: list
    });
}

/**
 * 根据端口号删除对应的service
 * @param  {String} port
 */
function deleteServiceByName(name){
    if(typeof name === 'boolean'){
        return;
    }

    let $ = _.readServerFile(),
        hasExist = false;

    $('Service').each((index, item) => {
        let $item = $(item);
        if($item.attr('name') === name){
            hasExist = true;
            $item.remove();
            return false;
        }
    });

    if(!hasExist){
        console.error(chalk.red(`service "${name}" is not found`));
        return;
    }

    _.writeServerFile($.html())
    .then(() => {
        console.log(chalk.green('remove the service successfully'));

        if(fs.existsSync(TOMCAT_START_IDENTIFIER)){
            startTomcat();
        }
    });
}

export default options => {
    // 检查启动，停止以及重启是否有两个以上参数同时存在
    if(_.checkParamsMutex([
        options.start,
        options.stop,
        options.restart])
    ){
        console.error(chalk.red('\'-s, --start\', \'-S, --stop\', \'-r, --restart\' are mutually exclusive'));
        return;
    }

    // 打印已配置的服务列表
    if(options.list){
        showServices();
        return;
    }

    // 根据服务的名称删除相关的配置项
    if(options.delete){
        deleteServiceByName(options.delete);
        return;
    }

    let clean = null;
    // 存在-c, --clean的话删除掉已下载的tomcat并停止tomcat进程
    if(options.clean){
        clean = stopTomcat().then(() => {
            return del(TOMCAT_PATH, {force: true});
        });
    }

    if(options.start || options.stop || options.restart){
        Promise.resolve(clean)
        .then(checkJDK)
        .then(checkTomcat)
        .then(() => {
            if(options.start){
                startTomcat({
                    hasMessage: true,
                    port: options.port
                });
            } else if(options.stop){
                stopTomcat({hasMessage: true});
            } else if(options.restart){
                stopTomcat()
                .then(startTomcat)
                .then(() => {
                    console.log(chalk.green('tomcat server restart successfully'));
                });
            }
        });
    }
};
<|endoftext|>


<commit_before>/**
 * @file Manages Canvas and Portal feeds for caching purposes
 * @module feeds
 */

const _      = require('underscore');
const canvas = require(__dirname + '/canvas.js');
const portal = require(__dirname + '/portal.js');
const users  = require(__dirname + '/users.js');

/**
 * Update cached Canvas feed for a user
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {updateCanvasCacheCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback updateCanvasCacheCallback
 *
 * @param {Object} err - Null if success, error object if failure
 */

function updateCanvasCache(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	const canvasdata = db.collection('canvasFeeds');

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		canvas.getUserCal(db, userDoc.user, (err, hasURL, events) => {
			if(err) {
				callback(err);
				return;
			}

			canvasdata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback('There was an error removing the old events from the database!');
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
				}

				canvasdata.insertMany(events, err => {
					if(err) {
						callback('There was an error inserting events into the database!');
						return;
					}

					callback(null);
				});
			});
		});
	});
}

/**
 * Add a user to the Portal queue
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {addPortalQueueCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback addPortalQueueCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Array} events - Array of Portal events if success, null if failure
 */

function addPortalQueueClasses(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		const portaldata = db.collection('portalFeedsClasses');
		const userdata   = db.collection('users');

		portal.getFromCalClasses(db, user, (err, hasURL, events) => {
			if(err) {
				callback(err, null);
				return;
			}
			if(_.isEmpty(events)) {
				userdata.update({ user }, { $set: { inPortalQueueClasses: true } }, err => {
					if(err) {
						callback(new Error('There was an error adding the user to the queue!'), null);
						return;
					}

					callback(null, events);
				});
				return;
			}

			portaldata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback(new Error('There was an error removing the old events from the database!'), null);
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
				}

				portaldata.insertMany(events, err => {
					if(err) {
						callback(new Error(`There was an error inserting events into the database! (${err})`), null);
						return;
					}

					userdata.update({ user }, { $set: { inPortalQueueClasses: false } }, err => {
						if(err) {
							callback(new Error('There was an error removing the user from the queue!'), null);
							return;
						}

						callback(null, events);
					});
				});
			});
		});
	});
}

/**
 * Add a user to the Portal queue
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {addPortalQueueCallback} callback - Callback
 */

function addPortalQueueCalendar(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		const portaldata = db.collection('portalFeedsCalendar');
		const userdata   = db.collection('users');

		portal.getFromCalCalendar(db, user, (err, hasURL, events) => {
			if(err) {
				callback(err, null);
				return;
			}
			if(_.isEmpty(events)) {
				userdata.update({ user }, { $set: { inPortalQueueCalendar: true } }, err => {
					if(err) {
						callback(new Error('There was an error adding the user to the queue!'), null);
						return;
					}

					callback(null, events);
				});
				return;
			}

			portaldata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback(new Error('There was an error removing the old events from the database!'), null);
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
				}

				portaldata.insertMany(events, err => {
					if(err) {
						callback(new Error(`There was an error inserting events into the database! (${err})`), null);
						return;
					}

					userdata.update({ user }, { $set: { inPortalQueueCalendar: false } }, err => {
						if(err) {
							callback(new Error('There was an error removing the user from the queue!'), null);
							return;
						}

						callback(null, events);
					});
				});
			});
		});
	});
}

/**
 * Process the queue for updating cached Portal feeds
 * @param {Object} db - Database object
 * @param {processPortalQueueCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback processPortalQueueCallback
 *
 * @param {Object} err - Null if success, error object if failure
 */

function processPortalQueue(db, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	const userdata = db.collection('users');

	userdata.find({ $or:[{ inPortalQueueClasses: true }, { inPortalQueueCalendar: true }] }).toArray((err, queue) => {
		if(err) {
			callback(new Error('There was a problem querying the database!'));
			return;
		}

		function handleQueue(i) {
			if(i >= queue.length) {
				callback(null);
				return;
			}

			// Check which feeds to update
			if (queue[i].inPortalQueueClasses && queue[i].inPortalQueueCalendar) {
				// Update both
				addPortalQueueClasses(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}

					addPortalQueueCalendar(db, queue[i].user, err => {
						if(err) {
							callback(err);
							return;
						}

						handleQueue(++i);
					});
				});
			} else if (queue[i].inPortalQueueClasses) {
				// Update only class feed
				addPortalQueueClasses(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}
					handleQueue(++i);
				});
			} else {
				// Update only claendar feed
				addPortalQueueCalendar(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}

					handleQueue(++i);
				});
			}
		}

		handleQueue(0);
	});
}

/**
 * Tries to get Canvas calendar from cache. If it's empty, it tries updating the cache and returning that response.
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {canvasCacheRetryCallback} callback - Callback
 */

/**
 * Returns array containing Canvas events
 * @callback canvasCacheRetryCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} events - Array of events if success, null if failure.
 */

function canvasCacheRetry(db, user, callback) {
	if (typeof callback !== 'function') return;

	canvas.getFromCache(db, user, (err, hasURL, events) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!hasURL || !events || events.length > 0) {
			callback(null, hasURL, events);
			return;
		}

		// If the events are empty, there's a chance that we just didn't cache results yet
		updateCanvasCache(db, user, err => {
			if(err) {
				callback(err, null, null);
				return;
			}

			canvas.getFromCache(db, user, callback);
		});
	});
}

module.exports.updateCanvasCache      = updateCanvasCache;
module.exports.addPortalQueueClasses  = addPortalQueueClasses;
module.exports.addPortalQueueCalendar = addPortalQueueCalendar;
module.exports.processPortalQueue     = processPortalQueue;
module.exports.canvasCacheRetry       = canvasCacheRetry;
<commit_msg>Add fix to not add null to db<commit_after>/**
 * @file Manages Canvas and Portal feeds for caching purposes
 * @module feeds
 */

const _      = require('underscore');
const canvas = require(__dirname + '/canvas.js');
const portal = require(__dirname + '/portal.js');
const users  = require(__dirname + '/users.js');

/**
 * Update cached Canvas feed for a user
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {updateCanvasCacheCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback updateCanvasCacheCallback
 *
 * @param {Object} err - Null if success, error object if failure
 */

function updateCanvasCache(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	const canvasdata = db.collection('canvasFeeds');

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		canvas.getUserCal(db, userDoc.user, (err, hasURL, events) => {
			if(err) {
				callback(err);
				return;
			}

			canvasdata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback('There was an error removing the old events from the database!');
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
					canvasdata.insertMany(events, err => {
						if(err) {
							callback('There was an error inserting events into the database!');
							return;
						}

						callback(null);
					});
				} else {
					callback(null);
				}
			});
		});
	});
}

/**
 * Add a user to the Portal queue
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {addPortalQueueCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback addPortalQueueCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Array} events - Array of Portal events if success, null if failure
 */

function addPortalQueueClasses(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		const portaldata = db.collection('portalFeedsClasses');
		const userdata   = db.collection('users');

		portal.getFromCalClasses(db, user, (err, hasURL, events) => {
			if(err) {
				callback(err, null);
				return;
			}
			if(_.isEmpty(events)) {
				userdata.update({ user }, { $set: { inPortalQueueClasses: true } }, err => {
					if(err) {
						callback(new Error('There was an error adding the user to the queue!'), null);
						return;
					}

					callback(null, events);
				});
				return;
			}

			portaldata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback(new Error('There was an error removing the old events from the database!'), null);
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
					portaldata.insertMany(events, err => {
						if(err) {
							callback(new Error(`There was an error inserting events into the database! (${err})`), null);
							return;
						}

						userdata.update({ user }, { $set: { inPortalQueueClasses: false } }, err => {
							if(err) {
								callback(new Error('There was an error removing the user from the queue!'), null);
								return;
							}

							callback(null, events);
						});
					});
				} else {
					userdata.update({ user }, { $set: { inPortalQueueClasses: false } }, err => {
						if(err) {
							callback(new Error('There was an error removing the user from the queue!'), null);
							return;
						}

						callback(null, events);
					});
				}
			});
		});
	});
}

/**
 * Add a user to the Portal queue
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {addPortalQueueCallback} callback - Callback
 */

function addPortalQueueCalendar(db, user, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'), null);
		return;
	}

	users.get(db, user, (err, isUser, userDoc) => {
		if(err) {
			callback(err, null);
			return;
		}
		if(!isUser) {
			callback(new Error('User doesn\'t exist!'));
			return;
		}

		const portaldata = db.collection('portalFeedsCalendar');
		const userdata   = db.collection('users');

		portal.getFromCalCalendar(db, user, (err, hasURL, events) => {
			if(err) {
				callback(err, null);
				return;
			}
			if(_.isEmpty(events)) {
				userdata.update({ user }, { $set: { inPortalQueueCalendar: true } }, err => {
					if(err) {
						callback(new Error('There was an error adding the user to the queue!'), null);
						return;
					}

					callback(null, events);
				});
				return;
			}

			portaldata.deleteMany({ user: userDoc._id }, err => {
				if(err) {
					callback(new Error('There was an error removing the old events from the database!'), null);
					return;
				}

				if (events) {
					events.forEach(e => e.user = userDoc._id);
					portaldata.insertMany(events, err => {
						if(err) {
							callback(new Error(`There was an error inserting events into the database! (${err})`), null);
							return;
						}

						userdata.update({ user }, { $set: { inPortalQueueCalendar: false } }, err => {
							if(err) {
								callback(new Error('There was an error removing the user from the queue!'), null);
								return;
							}

							callback(null, events);
						});
					});
				} else {
					userdata.update({ user }, { $set: { inPortalQueueCalendar: false } }, err => {
						if(err) {
							callback(new Error('There was an error removing the user from the queue!'), null);
							return;
						}

						callback(null, events);
					});
				}
			});
		});
	});
}

/**
 * Process the queue for updating cached Portal feeds
 * @param {Object} db - Database object
 * @param {processPortalQueueCallback} callback - Callback
 */

/**
 * Returns an error if any
 * @callback processPortalQueueCallback
 *
 * @param {Object} err - Null if success, error object if failure
 */

function processPortalQueue(db, callback) {
	if(typeof callback !== 'function') return;

	if(typeof db !== 'object') {
		callback(new Error('Invalid database connection!'));
		return;
	}

	const userdata = db.collection('users');

	userdata.find({ $or:[{ inPortalQueueClasses: true }, { inPortalQueueCalendar: true }] }).toArray((err, queue) => {
		if(err) {
			callback(new Error('There was a problem querying the database!'));
			return;
		}

		function handleQueue(i) {
			if(i >= queue.length) {
				callback(null);
				return;
			}

			// Check which feeds to update
			if (queue[i].inPortalQueueClasses && queue[i].inPortalQueueCalendar) {
				// Update both
				addPortalQueueClasses(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}

					addPortalQueueCalendar(db, queue[i].user, err => {
						if(err) {
							callback(err);
							return;
						}

						handleQueue(++i);
					});
				});
			} else if (queue[i].inPortalQueueClasses) {
				// Update only class feed
				addPortalQueueClasses(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}
					handleQueue(++i);
				});
			} else {
				// Update only claendar feed
				addPortalQueueCalendar(db, queue[i].user, err => {
					if(err) {
						callback(err);
						return;
					}

					handleQueue(++i);
				});
			}
		}

		handleQueue(0);
	});
}

/**
 * Tries to get Canvas calendar from cache. If it's empty, it tries updating the cache and returning that response.
 * @param {Object} db - Database object
 * @param {string} user - Username
 * @param {canvasCacheRetryCallback} callback - Callback
 */

/**
 * Returns array containing Canvas events
 * @callback canvasCacheRetryCallback
 *
 * @param {Object} err - Null if success, error object if failure
 * @param {Boolean} hasURL - Whether or not user has a Canvas URL set. Null if error.
 * @param {Array} events - Array of events if success, null if failure.
 */

function canvasCacheRetry(db, user, callback) {
	if (typeof callback !== 'function') return;

	canvas.getFromCache(db, user, (err, hasURL, events) => {
		if(err) {
			callback(err, null, null);
			return;
		}
		if(!hasURL || !events || events.length > 0) {
			callback(null, hasURL, events);
			return;
		}

		// If the events are empty, there's a chance that we just didn't cache results yet
		updateCanvasCache(db, user, err => {
			if(err) {
				callback(err, null, null);
				return;
			}

			canvas.getFromCache(db, user, callback);
		});
	});
}

module.exports.updateCanvasCache      = updateCanvasCache;
module.exports.addPortalQueueClasses  = addPortalQueueClasses;
module.exports.addPortalQueueCalendar = addPortalQueueCalendar;
module.exports.processPortalQueue     = processPortalQueue;
module.exports.canvasCacheRetry       = canvasCacheRetry;
<|endoftext|>


<commit_before>import 'core-js';
import 'normalize.css';
import './index.scss';
import { isDuringLiffRedirect } from './lib';
import App from './App.svelte';

liff.init({ liffId: LIFF_ID }).then(() => {
  // liff.init should have corrected the path now, don't initialize app and just wait...
  // Ref: https://www.facebook.com/groups/linebot/permalink/2380490388948200/?comment_id=2380868955577010
  if (isDuringLiffRedirect) return;

  if (!liff.isLoggedIn()) {
    liff.login({
      // redirectUri:
      // will default redirect to endpointURL set in Line LOGIN developer console.
    });
  }

  // Cleanup loading
  document.getElementById('loading').remove();
  new App({ target: document.body });
});
<commit_msg>Update liff.login redirectUri<commit_after>import 'core-js';
import 'normalize.css';
import './index.scss';
import { isDuringLiffRedirect } from './lib';
import App from './App.svelte';

liff.init({ liffId: LIFF_ID }).then(() => {
  // liff.init should have corrected the path now, don't initialize app and just wait...
  // Ref: https://www.facebook.com/groups/linebot/permalink/2380490388948200/?comment_id=2380868955577010
  if (isDuringLiffRedirect) return;

  if (!liff.isLoggedIn()) {
    liff.login({
      // https://github.com/line/line-liff-v2-starter/issues/4
      redirectUri: `${location.href}${location.search}`,
    });
  }

  // Cleanup loading
  document.getElementById('loading').remove();
  new App({ target: document.body });
});
<|endoftext|>


<commit_before>(function (root, factory) {
  var name = 'mag$';
  if (typeof define === 'function' && define.amd) {
    define(['mag',' jquery'], function (mag, $) {
        return (root[name] = factory(mag, $));
    });
  } else if (typeof exports === 'object') {
    module.exports = factory(require('./mag'), require('jquery'));
  } else {
    root[name] = factory(mag, $);
  }
}(this, function (mag, $) {

  var normalizeOffsets = function (e) {
    var offset = $(e.target).offset();
    return {
      x: e.pageX - offset.left,
      y: e.pageY - offset.top
    };
    return e;
  };

  var ratioOffsets = function (e) {
    var normOff = normalizeOffsets(e);
    var $target = $(e.target);
    return {
      x: normOff.x / $target.width(),
      y: normOff.y / $target.height()
    }
  };

  var ratioOffsetsFor = function ($target, x, y) {
    return {
      x: x / $target.width(),
      y: y / $target.height()
    }
  };

  var cssPerc = function (frac) {
    return (frac * 100) + '%';
  };

  var toCSS = function (pt) {
    var css = {};
    if (pt.x !== undefined) css.left = cssPerc(pt.x);
    if (pt.y !== undefined) css.top = cssPerc(pt.y);
    if (pt.w !== undefined) css.width = cssPerc(pt.w);
    if (pt.h !== undefined) css.height = cssPerc(pt.h);
    return css;
  };

  var mag$ = function (options) {

    return this.each(function() {

      var $el = $(this);

      options = $.extend({
        //position: 'mirror',
        //positionEvent: 'move'
        ////positionEvent: 'drag'
      }, options);

      var model = {
        mode: 'overflow',
        focus: {
          x: 0.5,
          y: 0.5
        },
        zoom: 1,
        lens: {
          w: 0,
          h: 0
        }
      };

      var render = function () {
        var lens, full;
        lens = model.lazyLens;
        full = model.lazyFull;
        var css = toCSS(lens);
        $lens.css(css);
        var fullCSS = toCSS(full);
        $full.css(fullCSS);
      };

      $el.addClass('mag-host');

      var $lens = $('

');
      $lens.appendTo($el);

      var $noflow = $('

');
      $noflow.appendTo($el);

      var $zone = $('

');
      $zone.appendTo($noflow);

      var $full = $('

');
      $full.html(options.content);
      $full.appendTo($noflow);

      mag.compute(model);

      model.lazyFocus = {
        x: model.focus.x,
        y: model.focus.y
      };
      model.lazyLens = {
        x: model.lens.x,
        y: model.lens.y,
        w: model.lens.w,
        h: model.lens.h
      };
      model.lazyFull = {
        x: model.full.x,
        y: model.full.y,
        w: model.full.w,
        h: model.full.h
      };
      model.lazyZoom = model.zoom;

      render();

      var lazyRate = 1;
      var frameSpeed = 100;
      var dragRate = 0.2;
      var zoomRate = 0.5;

      var approach = function (rate, dest, src, props, srcProps) {
        srcProps = srcProps ? srcProps : props;
        if (! $.isArray(props)) {
          props = [props];
          srcProps = [srcProps];
        }
        for (var i = 0, m = props.length; i < m; ++i) {
          var prop = props[i];
          var srcProp = srcProps[i];
          var diff = src[srcProp] - dest[prop];
          dest[prop] += diff * rate;
        }
      };

      var renderLoop = function () {
        approach(lazyRate, model.lazyFocus, model.focus, 'x');
        approach(lazyRate, model.lazyFocus, model.focus, 'y');
        approach(lazyRate, model.lazyFull, model.full, ['x', 'y', 'w', 'h']);
        approach(lazyRate, model.lazyLens, model.lens, ['x', 'y', 'w', 'h']);
        approach(lazyRate, model, model, 'lazyZoom', 'zoom');

        render();
      };

      if (options.position === 'mirror') {
        if (options.positionEvent === 'move') {
          lazyRate = 0.2;
          frameSpeed = 20;

          $zone.on('mousemove', function(e){
            console.log('e', e);

            var ratios = ratioOffsets(e);
            ratios = {
              x: mag.minMax(ratios.x, 0, 1),
              y: mag.minMax(ratios.y, 0, 1)
            };
            console.log('ratios', ratios);
            model.focus.x = ratios.x;
            model.focus.y = ratios.y;

            mag.compute(model);
          });
        }
        else if (options.positionEvent === 'hold') {
          // TODO: implement
        }
        else {
          throw new Error("Invalid 'positionEvent' option.");
        }
      }
      else if (options.position === 'joystick') {
        if (options.positionEvent === 'move') {
          // TODO: implement
        }
        else if (options.positionEvent === 'hold') {
          lazyRate = 0.5;
          frameSpeed = 20;

          var dragging = false;

          var dragInterval = setInterval(function () {
            // console.log('focus', model.focus);
            if (! dragging) return;

            var focus = model.focus;

            //var adjustedDragRate =  dragRate * model.zoom;
            var adjustedDragRate =  dragRate;
            focus.x += (ratios.x - 0.5) * adjustedDragRate;
            focus.y += (ratios.y - 0.5) * adjustedDragRate;

            focus = {
              x: mag.minMax(focus.x, 0, 1),
              y: mag.minMax(focus.y, 0, 1)
            };

            model.focus.x = focus.x;
            model.focus.y = focus.y;

            mag.compute(model);
          }, 50);

          $zone.drag('start', function () {
            dragging = true;
          });

          $zone.drag('end', function () {
            dragging = false;
          });

          $zone.drag(function( e, dd ){
            console.log('drag', dd);
            console.log('e', e);

            var offset = $zone.offset();
            ratios = ratioOffsetsFor($zone, e.pageX - offset.left, e.pageY - offset.top);
          });
        }
        else {
          throw new Error("Invalid 'positionEvent' option.");
        }
      }
      else {
        throw new Error("Invalid 'position' option.");
      }

      $zone.on('mousewheel', function (e) {
        e.preventDefault();

        var delta = e.deltaY * zoomRate;

        var zoom = model.zoom;
        zoom += delta;
        zoom = mag.minMax(zoom, 1, 10);

        model.zoom = zoom;

        mag.compute(model);

        console.log('full', model.full);
        console.log('focus', model.focus);
        console.log('zoom', model.zoom);
      })

      var interval = setInterval(renderLoop, frameSpeed);

    });
  };

  $.fn.mag = mag$;

  return mag$;
}));
<commit_msg>default zoom 2<commit_after>(function (root, factory) {
  var name = 'mag$';
  if (typeof define === 'function' && define.amd) {
    define(['mag',' jquery'], function (mag, $) {
        return (root[name] = factory(mag, $));
    });
  } else if (typeof exports === 'object') {
    module.exports = factory(require('./mag'), require('jquery'));
  } else {
    root[name] = factory(mag, $);
  }
}(this, function (mag, $) {

  var normalizeOffsets = function (e) {
    var offset = $(e.target).offset();
    return {
      x: e.pageX - offset.left,
      y: e.pageY - offset.top
    };
    return e;
  };

  var ratioOffsets = function (e) {
    var normOff = normalizeOffsets(e);
    var $target = $(e.target);
    return {
      x: normOff.x / $target.width(),
      y: normOff.y / $target.height()
    }
  };

  var ratioOffsetsFor = function ($target, x, y) {
    return {
      x: x / $target.width(),
      y: y / $target.height()
    }
  };

  var cssPerc = function (frac) {
    return (frac * 100) + '%';
  };

  var toCSS = function (pt) {
    var css = {};
    if (pt.x !== undefined) css.left = cssPerc(pt.x);
    if (pt.y !== undefined) css.top = cssPerc(pt.y);
    if (pt.w !== undefined) css.width = cssPerc(pt.w);
    if (pt.h !== undefined) css.height = cssPerc(pt.h);
    return css;
  };

  var mag$ = function (options) {

    return this.each(function() {

      var $el = $(this);

      options = $.extend({
        //position: 'mirror',
        //positionEvent: 'move'
        ////positionEvent: 'drag'
      }, options);

      var model = {
        mode: 'overflow',
        focus: {
          x: 0.5,
          y: 0.5
        },
        zoom: 2,
        lens: {
          w: 0,
          h: 0
        }
      };

      var render = function () {
        var lens, full;
        lens = model.lazyLens;
        full = model.lazyFull;
        var css = toCSS(lens);
        $lens.css(css);
        var fullCSS = toCSS(full);
        $full.css(fullCSS);
      };

      $el.addClass('mag-host');

      var $lens = $('

');
      $lens.appendTo($el);

      var $noflow = $('

');
      $noflow.appendTo($el);

      var $zone = $('

');
      $zone.appendTo($noflow);

      var $full = $('

');
      $full.html(options.content);
      $full.appendTo($noflow);

      mag.compute(model);

      model.lazyFocus = {
        x: model.focus.x,
        y: model.focus.y
      };
      model.lazyLens = {
        x: model.lens.x,
        y: model.lens.y,
        w: model.lens.w,
        h: model.lens.h
      };
      model.lazyFull = {
        x: model.full.x,
        y: model.full.y,
        w: model.full.w,
        h: model.full.h
      };
      model.lazyZoom = model.zoom;

      render();

      var lazyRate = 1;
      var frameSpeed = 100;
      var dragRate = 0.2;
      var zoomRate = 0.5;

      var approach = function (rate, dest, src, props, srcProps) {
        srcProps = srcProps ? srcProps : props;
        if (! $.isArray(props)) {
          props = [props];
          srcProps = [srcProps];
        }
        for (var i = 0, m = props.length; i < m; ++i) {
          var prop = props[i];
          var srcProp = srcProps[i];
          var diff = src[srcProp] - dest[prop];
          dest[prop] += diff * rate;
        }
      };

      var renderLoop = function () {
        approach(lazyRate, model.lazyFocus, model.focus, 'x');
        approach(lazyRate, model.lazyFocus, model.focus, 'y');
        approach(lazyRate, model.lazyFull, model.full, ['x', 'y', 'w', 'h']);
        approach(lazyRate, model.lazyLens, model.lens, ['x', 'y', 'w', 'h']);
        approach(lazyRate, model, model, 'lazyZoom', 'zoom');

        render();
      };

      if (options.position === 'mirror') {
        if (options.positionEvent === 'move') {
          lazyRate = 0.2;
          frameSpeed = 20;

          $zone.on('mousemove', function(e){
            console.log('e', e);

            var ratios = ratioOffsets(e);
            ratios = {
              x: mag.minMax(ratios.x, 0, 1),
              y: mag.minMax(ratios.y, 0, 1)
            };
            console.log('ratios', ratios);
            model.focus.x = ratios.x;
            model.focus.y = ratios.y;

            mag.compute(model);
          });
        }
        else if (options.positionEvent === 'hold') {
          // TODO: implement
        }
        else {
          throw new Error("Invalid 'positionEvent' option.");
        }
      }
      else if (options.position === 'joystick') {
        if (options.positionEvent === 'move') {
          // TODO: implement
        }
        else if (options.positionEvent === 'hold') {
          lazyRate = 0.5;
          frameSpeed = 20;

          var dragging = false;

          var dragInterval = setInterval(function () {
            // console.log('focus', model.focus);
            if (! dragging) return;

            var focus = model.focus;

            //var adjustedDragRate =  dragRate * model.zoom;
            var adjustedDragRate =  dragRate;
            focus.x += (ratios.x - 0.5) * adjustedDragRate;
            focus.y += (ratios.y - 0.5) * adjustedDragRate;

            focus = {
              x: mag.minMax(focus.x, 0, 1),
              y: mag.minMax(focus.y, 0, 1)
            };

            model.focus.x = focus.x;
            model.focus.y = focus.y;

            mag.compute(model);
          }, 50);

          $zone.drag('start', function () {
            dragging = true;
          });

          $zone.drag('end', function () {
            dragging = false;
          });

          $zone.drag(function( e, dd ){
            console.log('drag', dd);
            console.log('e', e);

            var offset = $zone.offset();
            ratios = ratioOffsetsFor($zone, e.pageX - offset.left, e.pageY - offset.top);
          });
        }
        else {
          throw new Error("Invalid 'positionEvent' option.");
        }
      }
      else {
        throw new Error("Invalid 'position' option.");
      }

      $zone.on('mousewheel', function (e) {
        e.preventDefault();

        var delta = e.deltaY * zoomRate;

        var zoom = model.zoom;
        zoom += delta;
        zoom = mag.minMax(zoom, 1, 10);

        model.zoom = zoom;

        mag.compute(model);

        console.log('full', model.full);
        console.log('focus', model.focus);
        console.log('zoom', model.zoom);
      })

      var interval = setInterval(renderLoop, frameSpeed);

    });
  };

  $.fn.mag = mag$;

  return mag$;
}));
<|endoftext|>


<commit_before>/*
Mag.JS AddOns v0.10.2
(c) Michael Glazer
https://github.com/magnumjs/mag.js
*/
mag.addons = {};
// helper function for non proxy supported browser i.e. NOT firefox
// state.fom = mag.addons.binds(state)
mag.addons.binds = function(data, attachTo, callback) {
  var oldVal,handler = function(e) {

    var val = e.target.type == 'checkbox' ? e.target.checked : e.target.value
    if (oldVal && val === oldVal) return

    oldVal = val;

    var name = e.target.name
    if (data[name] && data[name].type == 'fun' && typeof data[name] == 'function') {
        data[name](val)
    } else if(name) {
      //necessary ?
      if(data[name] && typeof data[name]._text !=='undefined')
        data[name]._text=val
      else
        data[name]=val
    }

    if(typeof Object.observe !== 'undefined'){
      Object.observe(data, function(changes) {
        // update target with changes
        changes.forEach(function(change) {
          if (change.type == 'update' || change.type == 'add') {
              // update the related dom
            if (e.target.name == change.name && e.target.value!==change.object[change.name])
                e.target.value = change.object[change.name]
          }
        })
      })
    }
    if (callback && typeof callback == 'function') callback()
  }
  var addThis = {}

  var events = ['_onclick', '_onchange', '_oninput']
  for (var k in events) addThis[events[k]] = handler

  addThis['_config'] = function(node, isNew) {
    if (isNew) {
        for (var j in data) {
            if (j) {
             document.querySelector('[name="' + j + '"]')?
               document.querySelector('[name="' + j + '"]').click() : 0
            }
        }
    }
  }
  if (attachTo) mag.addons.merge(addThis, attachTo)

  return addThis
}

// simple bind to keyup event
// mag.addons.change(state, state.form={})
// state.form = mag.addons.change(state)
mag.addons.change=function(data, addTo){
  var src = {
    _onkeyup : function(e){
      data[e.target.name](e.target.value)
    }
  }
  if(addTo){
    return mag.addons.merge(src, addTo)
  }
  return src
}

// BINDS
// this is the context which should be the 'state'
//  mag.addons.bindEvent.call(state, 'input', 'keyup', props.model.name)

mag.addons.bindEvent = function(field, event, model) {
  var src = {}
  src._value = model()
  src['_on' + event] = mag.withProp('value', model)

  mag.addons.merge(src, this[field] = {});
}

// bind methods from a model to a controller instance
//  mag.addons.bindToModel(this, Todo)
mag.addons.bindToModel = function(context, model) {
  for (var meth in model) {
    context[meth] = model[meth].bind(context)
  }
}

//UTILITY
mag.addons.copy =function (o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object") ? mag.addons.copy(v) : v;
   }
   return out;
}

mag.addons.merge = function(source, destination) {
    for (var k in source) destination[k] = source[k]
}

// return object of getter values
mag.addons.getProp = function(data) {
    var newData = {}
    Object.keys(data).forEach(function(k) {
        if (data[k].type == 'fun' && typeof data[k] == 'function') newData[k] = data[k]()
    })
    return newData
}

// show hide

// remove class hide to given context/this
// show(this) inside controller
mag.addons.show = function(condition) {
  if (typeof condition === 'boolean') {
    return {
      _config: function(n) {
        if (!condition) n.style.display = 'none';
        else n.style.display = 'block';
      }
    };
  } else if(typeof condition === 'object') {
    condition.didload = function(e, n) {
      n.classList.remove('hide')
      // both or just if no class hide ..
      //node.style.display = 'none'
    }
  } else if (arguments.length == 0){
    return function(e, n) {
      // next didupdate event
      n.style.display = 'block'
    }
  }
}

mag.addons.hide = function(condition) {
  if (typeof condition === 'boolean') {
    return {
      _config: function(n) {
          if (condition) n.style.display = 'none';
      }
    }
  } else if(arguments.length == 0){
    return function(e, n) {
      // next didupdate event
      n.style.display = 'none'
    }
  }
}

// life cycle event helpers

mag.addons.onNextUpdate = function(context, callback) {
  context.didupdate = function(event, node) {
    return callback.call(context, event, node)
  }
}

// promises
mag.deferred = Deferred = function() {
  return function Deferred(resolve, reject) {
    Deferred.resolve = resolve
    Deferred.reject = reject
  }
}

mag.addons.when = function(arrayOfPromises, callback) {
    Promise.all(arrayOfPromises).then(callback)
}

mag.addons.requestWithFeedback = function(args) {
  var key = JSON.stringify(args)
  if (!mag.addons.requestWithFeedback.cache[key]) {

    var loaders = document.querySelectorAll(".loader")

    //show icons
    for (var i = 0, loader; loader = loaders[i]; i++) loader.style.display = "block"

    var expire = function(data) {
      delete mag.addons.requestWithFeedback.cache[key]

      if (Object.keys(mag.addons.requestWithFeedback.cache).length == 0) {
          //hide icons
          for (var i = 0, loader; loader = loaders[i]; i++) loader.style.display = "none"
      }
      return data
    }
    mag.addons.requestWithFeedback.cache[key] = mag.request(args).then(expire, function(error) {
      expire(error)
      throw error
    })
  }
  return mag.addons.requestWithFeedback.cache[key]
}
mag.addons.requestWithFeedback.cache = {}

// hookins

mag.hookin('attributes', 'key', function(data) {
    // remove system key from being added to attributes in html
  //  data.value = null
})

mag.hookin('attributes', 'className', function(data) {
  var newClass = data.value
  data.value = data.node.classList + ''
  if (!data.node.classList.contains(newClass)) {
    data.value = data.node.classList.length > 0 ? data.node.classList + ' ' + newClass : newClass
  }
    data.key = 'class'
})
<commit_msg>Update mag.addons.js<commit_after>/*
Mag.JS AddOns v0.10.3
(c) Michael Glazer
https://github.com/magnumjs/mag.js
*/
mag.addons = {};
// helper function for non proxy supported browser i.e. NOT firefox
// state.fom = mag.addons.binds(state)
mag.addons.binds = function(data, attachTo, callback) {
  var oldVal,handler = function(e) {

    var val = e.target.type == 'checkbox' ? e.target.checked : e.target.value
    if (oldVal && val === oldVal) return

    oldVal = val;

    var name = e.target.name
    if (data[name] && data[name].type == 'fun' && typeof data[name] == 'function') {
        data[name](val)
    } else if(name) {
      //necessary ?
      if(data[name] && typeof data[name]._text !=='undefined')
        data[name]._text=val
      else
        data[name]=val
    }

    if(typeof Object.observe !== 'undefined'){
      Object.observe(data, function(changes) {
        // update target with changes
        changes.forEach(function(change) {
          if (change.type == 'update' || change.type == 'add') {
              // update the related dom
            if (e.target.name == change.name && e.target.value!==change.object[change.name])
                e.target.value = change.object[change.name]
          }
        })
      })
    }
    if (callback && typeof callback == 'function') callback()
  }
  var addThis = {}

  var events = ['_onclick', '_onchange', '_oninput']
  for (var k in events) addThis[events[k]] = handler

  addThis['_config'] = function(node, isNew) {

    for (var j in data) {
      var ele = document.querySelector('[name="' + j + '"]')
      if (j && isNew && ele) {
        ele.click()
      } else if(j && ele && ele.value!==data[j]) {
        // checkboxes/select/textarea ?
        ele.value = data[j]
      }
    }
  }
  if (attachTo) mag.addons.merge(addThis, attachTo)

  return addThis
}

// simple bind to keyup event
// mag.addons.change(state, state.form={})
// state.form = mag.addons.change(state)
mag.addons.change=function(data, addTo){
  var src = {
    _onkeyup : function(e){
      data[e.target.name](e.target.value)
    }
  }
  if(addTo){
    return mag.addons.merge(src, addTo)
  }
  return src
}

// BINDS
// this is the context which should be the 'state'
//  mag.addons.bindEvent.call(state, 'input', 'keyup', props.model.name)

mag.addons.bindEvent = function(field, event, model) {
  var src = {}
  src._value = model()
  src['_on' + event] = mag.withProp('value', model)

  mag.addons.merge(src, this[field] = {});
}

// bind methods from a model to a controller instance
//  mag.addons.bindToModel(this, Todo)
mag.addons.bindToModel = function(context, model) {
  for (var meth in model) {
    context[meth] = model[meth].bind(context)
  }
}

//UTILITY
mag.addons.copy =function (o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object") ? mag.addons.copy(v) : v;
   }
   return out;
}

mag.addons.merge = function(source, destination) {
    for (var k in source) destination[k] = source[k]
}

// return object of getter values
mag.addons.getProp = function(data) {
    var newData = {}
    Object.keys(data).forEach(function(k) {
        if (data[k].type == 'fun' && typeof data[k] == 'function') newData[k] = data[k]()
    })
    return newData
}

// show hide

// remove class hide to given context/this
// show(this) inside controller
mag.addons.show = function(condition) {
  if (typeof condition === 'boolean') {
    return {
      _config: function(n) {
        if (!condition) n.style.display = 'none';
        else n.style.display = 'block';
      }
    };
  } else if(typeof condition === 'object') {
    condition.didload = function(e, n) {
      n.classList.remove('hide')
      // both or just if no class hide ..
      //node.style.display = 'none'
    }
  } else if (arguments.length == 0){
    return function(e, n) {
      // next didupdate event
      n.style.display = 'block'
    }
  }
}

mag.addons.hide = function(condition) {
  if (typeof condition === 'boolean') {
    return {
      _config: function(n) {
          if (condition) n.style.display = 'none';
      }
    }
  } else if(arguments.length == 0){
    return function(e, n) {
      // next didupdate event
      n.style.display = 'none'
    }
  }
}

// life cycle event helpers

mag.addons.onNextUpdate = function(context, callback) {
  context.didupdate = function(event, node) {
    return callback.call(context, event, node)
  }
}

// promises
mag.deferred = Deferred = function() {
  return function Deferred(resolve, reject) {
    Deferred.resolve = resolve
    Deferred.reject = reject
  }
}

mag.addons.when = function(arrayOfPromises, callback) {
    Promise.all(arrayOfPromises).then(callback)
}

mag.addons.requestWithFeedback = function(args) {
  var key = JSON.stringify(args)
  if (!mag.addons.requestWithFeedback.cache[key]) {

    var loaders = document.querySelectorAll(".loader")

    //show icons
    for (var i = 0, loader; loader = loaders[i]; i++) loader.style.display = "block"

    var expire = function(data) {
      delete mag.addons.requestWithFeedback.cache[key]

      if (Object.keys(mag.addons.requestWithFeedback.cache).length == 0) {
          //hide icons
          for (var i = 0, loader; loader = loaders[i]; i++) loader.style.display = "none"
      }
      return data
    }
    mag.addons.requestWithFeedback.cache[key] = mag.request(args).then(expire, function(error) {
      expire(error)
      throw error
    })
  }
  return mag.addons.requestWithFeedback.cache[key]
}
mag.addons.requestWithFeedback.cache = {}

// hookins

mag.hookin('attributes', 'key', function(data) {
    // remove system key from being added to attributes in html
  //  data.value = null
})

mag.hookin('attributes', 'className', function(data) {
  var newClass = data.value
  data.value = data.node.classList + ''
  if (!data.node.classList.contains(newClass)) {
    data.value = data.node.classList.length > 0 ? data.node.classList + ' ' + newClass : newClass
  }
    data.key = 'class'
})
<|endoftext|>


<commit_before>import React from 'react';

export default class Field extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      focused: false,
      pristine: true
    };
  }

  static contextTypes = {
    onChange: React.PropTypes.func,
    submitFailures: React.PropTypes.number
  }

  componentDidMount() {
    this.onChange(this.props.value || '');
  }

  componentDidUpdate(p, s, nextContext){
    if(nextContext.submitFailures !== this.context.submitFailures){
      this.onChange(this.state.value);
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.reseting === true) {
      this.onChange(this.props.value || '', true);
    }
  }

  onChange(evt, reseting = false) {
    let value = (evt && evt.target)? evt.target.value : evt;
    this.setState({
      value: value
    });
    this.context.onChange(this.props.name, value, reseting).then(err => {

      this.setState({
        error: err
      });
    });
  }

  onFocus() {
    this.setState({focused: true, pristine: false});
  }

  onBlur() {
    this.setState({focused: false});
  }

  render() {
    let {label, hint, required, className, ...other} = this.props;

    let formGroupClass = 'form-group' + (className ? ' ' + className : '');

    if(this.state.focused){
      formGroupClass += ' active';
    }
    else if(this.state.error && (this.state.pristine === false || this.context.submitFailures)){
      formGroupClass += ' danger';
    }

    if(this.props.inlineLabel){
      formGroupClass += ' inline';
    }

    //Only display the label if it's passed as props
    let _label = label ? {label} : null;
    return (

        {_label}
        {this.renderField(other)}

    );
  }
}
<commit_msg>correct React 15 bug with controlled and uncontrolled input<commit_after>import React from 'react';

export default class Field extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      focused: false,
      value: '',
      pristine: true
    };
  }

  static contextTypes = {
    onChange: React.PropTypes.func,
    submitFailures: React.PropTypes.number
  }

  componentDidMount() {
    this.onChange(this.props.value || '');
  }

  componentDidUpdate(p, s, nextContext){
    if(nextContext.submitFailures !== this.context.submitFailures){
      this.onChange(this.state.value);
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.reseting === true) {
      this.onChange(this.props.value || '', true);
    }
  }

  onChange(evt, reseting = false) {
    let value = (evt && evt.target)? evt.target.value : evt;
    this.setState({
      value: value
    });
    this.context.onChange(this.props.name, value, reseting).then(err => {

      this.setState({
        error: err
      });
    });
  }

  onFocus() {
    this.setState({focused: true, pristine: false});
  }

  onBlur() {
    this.setState({focused: false});
  }

  render() {
    let {label, hint, required, className, ...other} = this.props;

    let formGroupClass = 'form-group' + (className ? ' ' + className : '');

    if(this.state.focused){
      formGroupClass += ' active';
    }
    else if(this.state.error && (this.state.pristine === false || this.context.submitFailures)){
      formGroupClass += ' danger';
    }

    if(this.props.inlineLabel){
      formGroupClass += ' inline';
    }

    //Only display the label if it's passed as props
    let _label = label ? {label} : null;
    return (

        {_label}
        {this.renderField(other)}

    );
  }
}
<|endoftext|>