<commit_before>/*
* Copyright 2010-2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** Called automatically by JsDoc Toolkit. */
function publish(symbolSet) {
	publish.conf = {  // trailing slash expected for dirs
		ext:         ".html",
		outDir:      JSDOC.opt.d || SYS.pwd+"../out/jsdoc/",
		templatesDir: JSDOC.opt.t || SYS.pwd+"../templates/jsdoc/",
		staticDir:   "static/",
		symbolsDir:  "",
		srcDir:      "src/",
		cssDir:      "css/",
		imagesDir:   "images/",
		jsDir:       "javascript/",
		templateName: "BBTest",
		templateVersion: "0.1"
	};

	// is source output is suppressed, just display the links to the source file
	if (JSDOC.opt.s && defined(Link) && Link.prototype._makeSrcLink) {
		Link.prototype._makeSrcLink = function(srcFilePath) {
			return "<"+srcFilePath+">";
		}
	}

	// create the folders and subfolders to hold the output
	IO.mkPath((publish.conf.outDir+publish.conf.cssDir));
	IO.mkPath((publish.conf.outDir+publish.conf.imagesDir));
	IO.mkPath((publish.conf.outDir+publish.conf.jsDir));
	IO.mkPath((publish.conf.outDir+publish.conf.srcDir));

	// used to allow Link to check the details of things being linked to
	Link.symbolSet = symbolSet;

	// create the required templates
	try {
		var classTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"class.tmpl");
		var tocTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"toc.tmpl");
		var featureListTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"featureList.tmpl");
	}
	catch(e) {
		print("Couldn't create the required templates: "+e);
		quit();
	}

	// some ustility filters
	function hasNoParent($) {return ($.memberOf == "")}
	function isaFile($) {return ($.is("FILE"))}
	function isaClass($) {return ($.is("CONSTRUCTOR") || $.isNamespace) && !($.alias == "_global_")}

	// get an array version of the symbolset, useful for filtering
	var symbols = symbolSet.toArray();

	/* create the hilited source code files
	var files = JSDOC.opt.srcFiles;
 	for (var i = 0, l = files.length; i < l; i++) {
 		var file = files[i];
 		var srcDir = publish.conf.outDir +publish.conf.srcDir;
		makeSrcFile(file, srcDir);
 	}
	*/

 	// get a list of all the classes in the symbolset
 	var classes = symbols.filter(isaClass).sort(makeSortby("name"));

	// create a filemap in which outfiles must be to be named uniquely, ignoring case
	if (JSDOC.opt.u) {
		var filemapCounts = {};
		Link.filemap = {};
		for (var i = 0, l = classes.length; i < l; i++) {
			var lcAlias = classes[i].alias.toLowerCase();

			if (!filemapCounts[lcAlias]) filemapCounts[lcAlias] = 1;
			else filemapCounts[lcAlias]++;

			Link.filemap[classes[i].alias] =
				(filemapCounts[lcAlias] > 1)?
				lcAlias+"_"+filemapCounts[lcAlias] : lcAlias;
		}
	}

	// create each of the class pages
	for (var i = 0, l = classes.length; i < l; i++) {
		var symbol = classes[i];

		//Set up support strings and tags for namespaces and children
		resolveSupport(symbol);

		symbol.events = symbol.getEvents();   // 1 order matters
		symbol.methods = symbol.getMethods(); // 2

		Link.currentSymbol= symbol;
		var output = "";
		output = classTemplate.process(symbol);

		IO.saveFile(publish.conf.outDir+"/"+publish.conf.symbolsDir, ((JSDOC.opt.u)? Link.filemap[symbol.alias] : symbol.alias) + publish.conf.ext, output);
	}

	// Generate the toc page
	Link.base = "";
	function hasTOC($) {return ($.toc)}
	var classes = classes.filter(hasTOC).sort(makeTocSort());

	publish.classesIndex = tocTemplate.process(classes);
	IO.saveFile(publish.conf.outDir, "toc"+publish.conf.ext, publish.classesIndex);

	// Create the feature list page
	classes = symbols.filter(isaClass).sort(makeSortby("alias"));
	var featureList = featureListTemplate.process(classes);
	IO.saveFile(publish.conf.outDir, "list"+publish.conf.ext, featureList);
	featureListTemplate = featureList = classes = null;

	// COPY FILES
	//CSS files
	var cssFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.cssDir);
	for(var i = 0;i<cssFiles.length;i++){
		IO.copyFile(cssFiles[i], publish.conf.outDir+"/"+publish.conf.cssDir);
	}
	//Static files
	var staticFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.staticDir);
	for(var i = 0;i<staticFiles.length;i++){
		IO.copyFile(staticFiles[i], publish.conf.outDir);
	}
	//Image Files
	var imageFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.imagesDir);
	for(var i = 0;i<imageFiles.length;i++){
		IO.copyFile(imageFiles[i], publish.conf.outDir+"/"+publish.conf.imagesDir);
	}
	//JS Files
	var jsFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.jsDir);
	for(var i = 0;i<jsFiles.length;i++){
		IO.copyFile(jsFiles[i], publish.conf.outDir+"/"+publish.conf.jsDir);
	}
}

/** Just the first sentence (up to a full stop). Should not break on dotted variable names. */
function summarize(desc) {
	if (typeof desc != "undefined")
		return desc.match(/([\w\W]+?\.)[^a-z0-9_$]/i)? RegExp.$1 : desc;
}

/** Make a symbol sorter by some attribute. */
function makeSortby(attribute) {
	return function(a, b) {
		try{
		if (a[attribute] != undefined && b[attribute] != undefined) {
			a = a[attribute].toLowerCase();
			b = b[attribute].toLowerCase();
			if (a  b) return 1;
			return 0;
		}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

function makeSpecialSortby(attribute1,attribute2) {
	return function(a, b) {
		try{
		if (a[attribute1] != undefined && a[attribute1][attribute2] != undefined  && b[attribute1] != undefined && b[attribute1][attribute2] != undefined ) {
			a = a[attribute1][attribute2].toLowerCase();
			b = b[attribute1][attribute2].toLowerCase();
			if (a  b) return 1;
			return 0;
		}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

function makeTocSort() {
	return function(a, b) {
		try{
			var baseAttribute = "toc";
			var subAttribute1 = "type";
			var subAttribute2 = "desc";
			if (a[baseAttribute] != undefined &&
			    a[baseAttribute][subAttribute1] != undefined  &&
			    a[baseAttribute][subAttribute2] != undefined  &&
			    b[baseAttribute] != undefined &&
			    a[baseAttribute][subAttribute1] != undefined  &&
			    b[baseAttribute][subAttribute2] != undefined ) {
				a = a[baseAttribute][subAttribute1].toLowerCase() + a[baseAttribute][subAttribute2].toLowerCase();
				b = b[baseAttribute][subAttribute1].toLowerCase() + b[baseAttribute][subAttribute2].toLowerCase();
				//print("Comparing " + a + " and " +b + " and result is " + (a>b?1:(a<b?-1:0)) );
				if (a  b) return 1;
				return 0;
			}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

/** Pull in the contents of an external file at the given path. */
function include(path) {
	var path = publish.conf.templatesDir+path;
	return IO.readFile(path);
}

/** Turn a raw source file into a code-hilited page in the docs. */
function makeSrcFile(path, srcDir, name) {
	if (JSDOC.opt.s) return;

	if (!name) {
		name = path.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
		name = name.replace(/\:/g, "_");
	}

	var src = {path: path, name:name, charset: IO.encoding, hilited: ""};

	if (defined(JSDOC.PluginManager)) {
		JSDOC.PluginManager.run("onPublishSrc", src);
	}

	if (src.hilited) {
		IO.saveFile(srcDir, name+publish.conf.ext, src.hilited);
	}
}

/** Build output for displaying function parameters. */
function makeSignature(params) {
	if (!params) return "()";
	var signature = "("
	+
	params.filter(
		function($) {
			return $.name.indexOf(".") == -1; // don't show config params in signature
		}
	).map(
		function($) {
			if($.isOptional){
				return "["+$.name + ": " + $.type+"]";
			}else{
				return $.name + " : " + $.type;
			}
		}
	).join(", ")
	+
	")";
	return signature;
}

/** Find symbol {@link ...} strings in text and turn into html links */
function resolveLinks(str, from) {
	str = str.replace(/\{@link ([^} ]+) ?\}/gi,
		function(match, symbolName) {
			return new Link().toSymbol(symbolName);
		}
	);

	str = str.replace(/\{@image ([^} ]+) ?\}/gi,
		function(match, symbolName) {

			//Find filename independant of slashes used
			var fileName = symbolName.substring(symbolName.lastIndexOf("\\")+1);
			var fileName = symbolName.substring(symbolName.lastIndexOf("/")+1);
			//Try catch to ignore missing files
			//Use for loop to try all source locations
			try{
				for(var i=0;i<JSDOC.opt._.length;i++){
					IO.copyFile(JSDOC.opt._[i]+"/"+symbolName, publish.conf.outDir+"/"+publish.conf.imagesDir);
				}
			}catch(e){}
			return "<image src=\""+publish.conf.imagesDir + fileName+"\">";
		}
	);

	//str = str.replace(/\{@learns ([^} ]+) ?\}/gi,
	//	function(match, symbolName) {
	//
	//		var linkAddress; //have to retrieve this somehow
	//		var linkName; //have to retrieve this somehow
	//
	//		return "" + linkName +"\"";
	//	}
	//);

	return str;
}

//BlackBerry Support Strings
var BB50String = "BlackBerry OS 5.0";
var BB50PString = "BlackBerry OS 5.0+";
var BB60String = "BlackBerry OS 6.0";
var BB60PString = "BlackBerry OS 6.0+";
var PB10String = "BlackBerry PlayBook";

var BB50Tag = "bb5.0";
var BB50PTag = "bb5.0|bb6.0";
var BB60Tag = "bb6.0";
var BB60PTag = "bb6.0";
var PB10Tag = "|pb1.0";
var commonTag = "|common";

var tableYes = "Y	";
var tableNo =  " 	";

function resolveSupport(symbol){
	if(symbol && symbol.comment){
		print("Resolving support for "+ symbol.alias);

		symbol.supportStrings = [];
		symbol.supportTag = "";
		symbol.supportTable ="";

	    //BlackBerry Support Tags
	    var BB50 = symbol.comment.getTag("BB50").length;
	    var BB50P = symbol.comment.getTag("BB50+").length;
	    var BB60 = symbol.comment.getTag("BB60").length;
	    var BB60P = symbol.comment.getTag("BB60+").length;
	    var PB10 = symbol.comment.getTag("PB10").length;

	    var children = [].concat(symbol.methods,symbol.properties,symbol.events);
		for (var i = 0, l = children.length; i < l; i++) {
			var object = children[i];
			if(object && object.comment){
				resolveSupport(object);

				BB50 |= object.comment.getTag("BB50").length;
			    BB50P |= object.comment.getTag("BB50+").length;
			    BB60 |= object.comment.getTag("BB60").length;
			    BB60P |= object.comment.getTag("BB60+").length;
			    PB10 |= object.comment.getTag("PB10").length;
			}
		}

	    //Check if a plus applies, otherwise set the supports for inidividual tags
	    if(BB50P){
	    	symbol.supportTag = BB50PTag;
	        symbol.supportStrings.push(BB50PString);
	        symbol.supportTable = tableYes+"\n"+tableYes;
	    }else if(BB60P){
	        symbol.supportTag = BB60PTag;
	        symbol.supportStrings.push(BB60PString);
	        symbol.supportTable = tableNo+"\n"+tableYes;
	    }else if(BB50){
	    	symbol.supportTag = BB50Tag;
	        symbol.supportStrings.push(BB50String);
	        symbol.supportTable = tableYes+"\n"+tableNo;
	    }else if(BB60){
	    	symbol.supportTag = BB60Tag;
	        symbol.supportStrings.push(BB60String);
	        symbol.supportTable = tableNo+"\n"+tableYes;
		}else{
			symbol.supportTable = tableNo + "\n" + tableNo;
		}

	    if(PB10){
	    	symbol.supportTag += PB10Tag;
	        symbol.supportStrings.push(PB10String);
	        if(BB50P){
	        	symbol.supportTag += commonTag;
	        }
	        symbol.supportTable += "\n"+tableYes;
	    }else{
	    	symbol.supportTable += "\n"+tableNo;
	    }
	}
}<commit_msg>Removal of unnecessary commented code<commit_after>/*
* Copyright 2010-2011 Research In Motion Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/** Called automatically by JsDoc Toolkit. */
function publish(symbolSet) {
	publish.conf = {  // trailing slash expected for dirs
		ext:         ".html",
		outDir:      JSDOC.opt.d || SYS.pwd+"../out/jsdoc/",
		templatesDir: JSDOC.opt.t || SYS.pwd+"../templates/jsdoc/",
		staticDir:   "static/",
		symbolsDir:  "",
		srcDir:      "src/",
		cssDir:      "css/",
		imagesDir:   "images/",
		jsDir:       "javascript/",
		templateName: "BBTest",
		templateVersion: "0.1"
	};

	// is source output is suppressed, just display the links to the source file
	if (JSDOC.opt.s && defined(Link) && Link.prototype._makeSrcLink) {
		Link.prototype._makeSrcLink = function(srcFilePath) {
			return "<"+srcFilePath+">";
		}
	}

	// create the folders and subfolders to hold the output
	IO.mkPath((publish.conf.outDir+publish.conf.cssDir));
	IO.mkPath((publish.conf.outDir+publish.conf.imagesDir));
	IO.mkPath((publish.conf.outDir+publish.conf.jsDir));
	IO.mkPath((publish.conf.outDir+publish.conf.srcDir));

	// used to allow Link to check the details of things being linked to
	Link.symbolSet = symbolSet;

	// create the required templates
	try {
		var classTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"class.tmpl");
		var tocTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"toc.tmpl");
		var featureListTemplate = new JSDOC.JsPlate(publish.conf.templatesDir+"featureList.tmpl");
	}
	catch(e) {
		print("Couldn't create the required templates: "+e);
		quit();
	}

	// some ustility filters
	function hasNoParent($) {return ($.memberOf == "")}
	function isaFile($) {return ($.is("FILE"))}
	function isaClass($) {return ($.is("CONSTRUCTOR") || $.isNamespace) && !($.alias == "_global_")}

	// get an array version of the symbolset, useful for filtering
	var symbols = symbolSet.toArray();

	/* create the hilited source code files
	var files = JSDOC.opt.srcFiles;
 	for (var i = 0, l = files.length; i < l; i++) {
 		var file = files[i];
 		var srcDir = publish.conf.outDir +publish.conf.srcDir;
		makeSrcFile(file, srcDir);
 	}
	*/

 	// get a list of all the classes in the symbolset
 	var classes = symbols.filter(isaClass).sort(makeSortby("name"));

	// create a filemap in which outfiles must be to be named uniquely, ignoring case
	if (JSDOC.opt.u) {
		var filemapCounts = {};
		Link.filemap = {};
		for (var i = 0, l = classes.length; i < l; i++) {
			var lcAlias = classes[i].alias.toLowerCase();

			if (!filemapCounts[lcAlias]) filemapCounts[lcAlias] = 1;
			else filemapCounts[lcAlias]++;

			Link.filemap[classes[i].alias] =
				(filemapCounts[lcAlias] > 1)?
				lcAlias+"_"+filemapCounts[lcAlias] : lcAlias;
		}
	}

	// create each of the class pages
	for (var i = 0, l = classes.length; i < l; i++) {
		var symbol = classes[i];

		//Set up support strings and tags for namespaces and children
		resolveSupport(symbol);

		symbol.events = symbol.getEvents();   // 1 order matters
		symbol.methods = symbol.getMethods(); // 2

		Link.currentSymbol= symbol;
		var output = "";
		output = classTemplate.process(symbol);

		IO.saveFile(publish.conf.outDir+"/"+publish.conf.symbolsDir, ((JSDOC.opt.u)? Link.filemap[symbol.alias] : symbol.alias) + publish.conf.ext, output);
	}

	// Generate the toc page
	Link.base = "";
	function hasTOC($) {return ($.toc)}
	var classes = classes.filter(hasTOC).sort(makeTocSort());

	publish.classesIndex = tocTemplate.process(classes);
	IO.saveFile(publish.conf.outDir, "toc"+publish.conf.ext, publish.classesIndex);

	// Create the feature list page
	classes = symbols.filter(isaClass).sort(makeSortby("alias"));
	var featureList = featureListTemplate.process(classes);
	IO.saveFile(publish.conf.outDir, "list"+publish.conf.ext, featureList);
	featureListTemplate = featureList = classes = null;

	// COPY FILES
	//CSS files
	var cssFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.cssDir);
	for(var i = 0;i<cssFiles.length;i++){
		IO.copyFile(cssFiles[i], publish.conf.outDir+"/"+publish.conf.cssDir);
	}
	//Static files
	var staticFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.staticDir);
	for(var i = 0;i<staticFiles.length;i++){
		IO.copyFile(staticFiles[i], publish.conf.outDir);
	}
	//Image Files
	var imageFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.imagesDir);
	for(var i = 0;i<imageFiles.length;i++){
		IO.copyFile(imageFiles[i], publish.conf.outDir+"/"+publish.conf.imagesDir);
	}
	//JS Files
	var jsFiles = IO.ls(publish.conf.templatesDir+"/"+publish.conf.jsDir);
	for(var i = 0;i<jsFiles.length;i++){
		IO.copyFile(jsFiles[i], publish.conf.outDir+"/"+publish.conf.jsDir);
	}
}

/** Just the first sentence (up to a full stop). Should not break on dotted variable names. */
function summarize(desc) {
	if (typeof desc != "undefined")
		return desc.match(/([\w\W]+?\.)[^a-z0-9_$]/i)? RegExp.$1 : desc;
}

/** Make a symbol sorter by some attribute. */
function makeSortby(attribute) {
	return function(a, b) {
		try{
		if (a[attribute] != undefined && b[attribute] != undefined) {
			a = a[attribute].toLowerCase();
			b = b[attribute].toLowerCase();
			if (a  b) return 1;
			return 0;
		}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

function makeSpecialSortby(attribute1,attribute2) {
	return function(a, b) {
		try{
		if (a[attribute1] != undefined && a[attribute1][attribute2] != undefined  && b[attribute1] != undefined && b[attribute1][attribute2] != undefined ) {
			a = a[attribute1][attribute2].toLowerCase();
			b = b[attribute1][attribute2].toLowerCase();
			if (a  b) return 1;
			return 0;
		}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

function makeTocSort() {
	return function(a, b) {
		try{
			var baseAttribute = "toc";
			var subAttribute1 = "type";
			var subAttribute2 = "desc";
			if (a[baseAttribute] != undefined &&
			    a[baseAttribute][subAttribute1] != undefined  &&
			    a[baseAttribute][subAttribute2] != undefined  &&
			    b[baseAttribute] != undefined &&
			    a[baseAttribute][subAttribute1] != undefined  &&
			    b[baseAttribute][subAttribute2] != undefined ) {
				a = a[baseAttribute][subAttribute1].toLowerCase() + a[baseAttribute][subAttribute2].toLowerCase();
				b = b[baseAttribute][subAttribute1].toLowerCase() + b[baseAttribute][subAttribute2].toLowerCase();
				//print("Comparing " + a + " and " +b + " and result is " + (a>b?1:(a<b?-1:0)) );
				if (a  b) return 1;
				return 0;
			}
		}catch(e){
			print("Couldn't sort by attribute "+attribute+": "+e);
		}
	}
}

/** Pull in the contents of an external file at the given path. */
function include(path) {
	var path = publish.conf.templatesDir+path;
	return IO.readFile(path);
}

/** Turn a raw source file into a code-hilited page in the docs. */
function makeSrcFile(path, srcDir, name) {
	if (JSDOC.opt.s) return;

	if (!name) {
		name = path.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
		name = name.replace(/\:/g, "_");
	}

	var src = {path: path, name:name, charset: IO.encoding, hilited: ""};

	if (defined(JSDOC.PluginManager)) {
		JSDOC.PluginManager.run("onPublishSrc", src);
	}

	if (src.hilited) {
		IO.saveFile(srcDir, name+publish.conf.ext, src.hilited);
	}
}

/** Build output for displaying function parameters. */
function makeSignature(params) {
	if (!params) return "()";
	var signature = "("
	+
	params.filter(
		function($) {
			return $.name.indexOf(".") == -1; // don't show config params in signature
		}
	).map(
		function($) {
			if($.isOptional){
				return "["+$.name + ": " + $.type+"]";
			}else{
				return $.name + " : " + $.type;
			}
		}
	).join(", ")
	+
	")";
	return signature;
}

/** Find symbol {@link ...} strings in text and turn into html links */
function resolveLinks(str, from) {
	str = str.replace(/\{@link ([^} ]+) ?\}/gi,
		function(match, symbolName) {
			return new Link().toSymbol(symbolName);
		}
	);

	str = str.replace(/\{@image ([^} ]+) ?\}/gi,
		function(match, symbolName) {

			//Find filename independant of slashes used
			var fileName = symbolName.substring(symbolName.lastIndexOf("\\")+1);
			var fileName = symbolName.substring(symbolName.lastIndexOf("/")+1);
			//Try catch to ignore missing files
			//Use for loop to try all source locations
			try{
				for(var i=0;i<JSDOC.opt._.length;i++){
					IO.copyFile(JSDOC.opt._[i]+"/"+symbolName, publish.conf.outDir+"/"+publish.conf.imagesDir);
				}
			}catch(e){}
			return "<image src=\""+publish.conf.imagesDir + fileName+"\">";
		}
	);

	return str;
}

//BlackBerry Support Strings
var BB50String = "BlackBerry OS 5.0";
var BB50PString = "BlackBerry OS 5.0+";
var BB60String = "BlackBerry OS 6.0";
var BB60PString = "BlackBerry OS 6.0+";
var PB10String = "BlackBerry PlayBook";

var BB50Tag = "bb5.0";
var BB50PTag = "bb5.0|bb6.0";
var BB60Tag = "bb6.0";
var BB60PTag = "bb6.0";
var PB10Tag = "|pb1.0";
var commonTag = "|common";

var tableYes = "Y	";
var tableNo =  " 	";

function resolveSupport(symbol){
	if(symbol && symbol.comment){
		print("Resolving support for "+ symbol.alias);

		symbol.supportStrings = [];
		symbol.supportTag = "";
		symbol.supportTable ="";

	    //BlackBerry Support Tags
	    var BB50 = symbol.comment.getTag("BB50").length;
	    var BB50P = symbol.comment.getTag("BB50+").length;
	    var BB60 = symbol.comment.getTag("BB60").length;
	    var BB60P = symbol.comment.getTag("BB60+").length;
	    var PB10 = symbol.comment.getTag("PB10").length;

	    var children = [].concat(symbol.methods,symbol.properties,symbol.events);
		for (var i = 0, l = children.length; i < l; i++) {
			var object = children[i];
			if(object && object.comment){
				resolveSupport(object);

				BB50 |= object.comment.getTag("BB50").length;
			    BB50P |= object.comment.getTag("BB50+").length;
			    BB60 |= object.comment.getTag("BB60").length;
			    BB60P |= object.comment.getTag("BB60+").length;
			    PB10 |= object.comment.getTag("PB10").length;
			}
		}

	    //Check if a plus applies, otherwise set the supports for inidividual tags
	    if(BB50P){
	    	symbol.supportTag = BB50PTag;
	        symbol.supportStrings.push(BB50PString);
	        symbol.supportTable = tableYes+"\n"+tableYes;
	    }else if(BB60P){
	        symbol.supportTag = BB60PTag;
	        symbol.supportStrings.push(BB60PString);
	        symbol.supportTable = tableNo+"\n"+tableYes;
	    }else if(BB50){
	    	symbol.supportTag = BB50Tag;
	        symbol.supportStrings.push(BB50String);
	        symbol.supportTable = tableYes+"\n"+tableNo;
	    }else if(BB60){
	    	symbol.supportTag = BB60Tag;
	        symbol.supportStrings.push(BB60String);
	        symbol.supportTable = tableNo+"\n"+tableYes;
		}else{
			symbol.supportTable = tableNo + "\n" + tableNo;
		}

	    if(PB10){
	    	symbol.supportTag += PB10Tag;
	        symbol.supportStrings.push(PB10String);
	        if(BB50P){
	        	symbol.supportTag += commonTag;
	        }
	        symbol.supportTable += "\n"+tableYes;
	    }else{
	    	symbol.supportTable += "\n"+tableNo;
	    }
	}
}<|endoftext|>


<commit_before>/**
 * Copyright 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var LoginPageView = GoldstoneBaseView.extend({

    defaults: {},

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.el = options.el;
        this.render();
        this.addHandlers();
    },

    addHandlers: function() {
        var self = this;

        $('.login-form').on('submit', function(e) {
            e.preventDefault();
            self.trimInputField('[name="username"]');
            self.submitRequest($(this).serialize());
        });
    },

    trimInputField: function(selector) {
        var trimmedContent = $(selector).val().trim();
        $(selector).val(trimmedContent);
    },

    submitRequest: function(input) {
        var self = this;

        // Upon clicking the submit button, the serialized user input is sent
        // via $.post to check the credentials. If successful, invoke "done"
        // if not, invoke "fail"

        $.post('/accounts/login', input, function() {})
            .done(function(success) {

                // store the auth token
                self.storeAuthToken(success.auth_token);

                // and add a message to the top of the screen that logs what
                // is returned from the call
                goldstone.raiseInfo('Authorization Successful, redirecting to goldstone', true);
                self.redirectPostSuccessfulAuth();
            })
            .fail(function(fail) {
                // and add a message to the top of the screen that logs what
                // is returned from the call

                try {
                    goldstone.raiseInfo(fail.responseJSON.non_field_errors[0], true);
                } catch (e) {
                    goldstone.raiseInfo(fail.responseText, true);
                    console.log(e);
                }

            });
    },

    storeAuthToken: function(token) {
        localStorage.setItem('userToken', token);
    },

    redirectPostSuccessfulAuth: function() {

        // if there was a previously visited page that
        // had redirected to the login page due to lack
        // of credentials, redirect back to that page
        if (location.hash && location.hash.length > 0) {
            locationhref = '/' + location.hash.slice(1);
        } else {

            // or just redirect to /discover
            locationhref = '/';
        }

        location.href = locationhref;

    },

    render: function() {
        this.$el.html(this.template());
        return this;
    },

    template: _.template('' +
        '
' +
        '
' +
        '
' +
        '<form class="login-form">' +
        '
Please sign in
' +
        'Username' +
        '<input name="username" type="text" class="form-control" placeholder="Username" required autofocus>' +
        'Password' +
        '<input name="password" type="password" class="form-control" placeholder="Password" required>
' +
        '<button name="submit" class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>' +
        '</form>' +
        '
Forgot username or password?
' +
        '
' +
        '
' +
        '
'
    )

});
<commit_msg>Remove 'previously visited' via url hash support on loginPageView. Can be replaced with a variable stored in app {}<commit_after>/**
 * Copyright 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var LoginPageView = GoldstoneBaseView.extend({

    defaults: {},

    initialize: function(options) {
        this.options = options || {};
        this.defaults = _.clone(this.defaults);
        this.el = options.el;
        this.render();
        this.addHandlers();
    },

    addHandlers: function() {
        var self = this;

        $('.login-form').on('submit', function(e) {
            e.preventDefault();
            self.trimInputField('[name="username"]');
            self.submitRequest($(this).serialize());
        });
    },

    trimInputField: function(selector) {
        var trimmedContent = $(selector).val().trim();
        $(selector).val(trimmedContent);
    },

    submitRequest: function(input) {
        var self = this;

        // Upon clicking the submit button, the serialized user input is sent
        // via $.post to check the credentials. If successful, invoke "done"
        // if not, invoke "fail"

        $.post('/accounts/login', input, function() {})
            .done(function(success) {

                // store the auth token
                self.storeAuthToken(success.auth_token);
                self.redirectPostSuccessfulAuth();
            })
            .fail(function(fail) {
                // and add a message to the top of the screen that logs what
                // is returned from the call

                try {
                    goldstone.raiseInfo(fail.responseJSON.non_field_errors[0]);
                } catch (e) {
                    goldstone.raiseInfo(fail.responseText);
                    console.log(e);
                }

            });
    },

    storeAuthToken: function(token) {
        localStorage.setItem('userToken', token);
    },

    redirectPostSuccessfulAuth: function() {
        location.href = '#';
    },

    render: function() {
        this.$el.html(this.template());
        return this;
    },

    template: _.template('' +
        '
' +
        '
' +
        '
' +
        '<form class="login-form">' +
        '
Please sign in
' +
        'Username' +
        '<input name="username" type="text" class="form-control" placeholder="Username" required autofocus>' +
        'Password' +
        '<input name="password" type="password" class="form-control" placeholder="Password" required>
' +
        '<button name="submit" class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>' +
        '</form>' +
        '
Forgot username or password?
' +
        '
' +
        '
' +
        '
'
    )

});
<|endoftext|>


<commit_before>tags_order_pre = ['unit', 'type', 'target_type'];
tags_order_post = ['server', 'plugin'];
function display_tag(tag_name, tag_value) {
    // at some point it'll probably make sense to make the background color the inverse of the foreground color
    // cause there'll be so many foreground colors that are not always visible on any particular background.
    if (tag_value instanceof Array) {
        // v[0] -> the tag 'value' ("sumseries (x values)")
        // v [1] -> list of differentiators
        differentiators = tag_value[1];
        tag_value = tag_value[0];
        body = "
";
        // show "2x this-value-appears-twice, [..]"
        var differentiators_counted = {};
        differentiators.forEach(function (target, i) {
            differentiators_counted[target] = differentiators_counted[target] + 1 || 1;
        });
        for (var diff in differentiators_counted) {
            body += "
" + differentiators_counted[diff] + "x " + diff + "
";
        }
        body += "
";

        //$(document).ready(function() {
            // for some reason "body": "" results in an empty body, so do it via data-content attribute.
            //$('#' + label_id).popover({"html": true, "placement": "bottom", "trigger": "hover"});
        //});
        // leverage jquery to do html entity encoding, so we can put html code in the data attribute.
        title = jQuery('
').text('' + tag_name + ': ' + tag_value).html();
        label = "" + tag_value + "";
    } else {
        title = jQuery('
').text('' + tag_name + ': ' + tag_value).html();
        label = "" + tag_value + "";
    }
    return label;
}

function display_tag_for_legend(tag_name) {
    title = jQuery('
').text('A tag with key ' + tag_name + '').html();
    label = "" + tag_name + "";
    return label;
}

function display_word(word) {
    // word can be anything. a misc. string, a tag, a <tag>:, !<tag>=, etc
    // if it matches on a tag, display it correctly for that tag (colored label, key = parsed out tag, value the pattern)
    // otherwise generic label
    orig = word;
    if (word.charAt(0) == '!') {
        word = word.slice(1);
    }
    word = word.split('=')[0].split(':')[0];
    if (word in colormap) {
        return display_tag(word, orig);
    }
    return "" + orig + "";
}
// build a label list (colored or regular) for patterns which may contain tags
function generate_pattern_display(patterns) {
    var string = '';
    patterns.forEach(function(word) {string += display_word(word) });
    return string;
}
// generate label list for tags k/v, ordered by moving tags based on keys in pre post lists to begin/end.
function generate_title_from_dict(tags_dict, order_pre, order_post) {
    var title = '';
    var keys = Object.keys(tags_dict);
    keys.sort();
    order_pre.forEach(function(tag) {if(tag in tags_dict) {title += display_tag(tag, tags_dict[tag]); }});
    keys.forEach(function (tag) { tag_v = tags_dict[tag]; if($.inArray(tag, order_pre) < 0 && $.inArray(tag, order_post) < 0) {title += display_tag(tag,tag_v); }});
    order_post.forEach(function(tag) {if(tag in tags_dict) {title += display_tag(tag, tags_dict[tag]); }});
    return title;
}
function generate_tag_legend_display(tags_list) {
    var title = 'Tag legend: ';
    tags_list.sort();
    tags_order_pre.forEach(function(tag) {if($.inArray(tag, tags_list) >= 0) {title += display_tag_for_legend(tag); }});
    tags_list.forEach(function(tag) {if($.inArray(tag, tags_order_pre) < 0 && $.inArray(tag, tags_order_post) < 0) {title += display_tag_for_legend(tag); }});
    tags_order_post.forEach(function(tag) {if($.inArray(tag, tags_list) >= 0) {title += display_tag_for_legend(tag); }});
    return title;
}
function get_graph_name(key, graph_data) {
    // set graph_name; with each tag in its own color. this way it's very clear how it's related to the query (esp. the group by)
    // remove all constants that are already printed in vtitle..
    filtered_constants = [];
    $.map(graph_data["constants_all"], function (tag_v, tag_k) {
        if (jQuery.inArray(tag_k, ["stat", "type", "unit", "target_type"]) == -1) {
            filtered_constants[tag_k] = tag_v;
        }
    });

    var graph_name = generate_title_from_dict(filtered_constants, tags_order_pre, tags_order_post);
    // note graph_name can be empty
    return graph_name;
}
// http://stackoverflow.com/questions/280634/endswith-in-javascript
function endsWith(str, suffix) {
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function get_vtitle(graph_data) {
    //automatically generate vtitle, if possible
    var vtitle = "";
    var target_type = "";
    if ('stat' in graph_data['constants_all']) {
        vtitle += display_tag('stat', graph_data['constants_all']['stat']);
    }
    if ('type' in graph_data['constants_all']) {
        vtitle += display_tag('type', graph_data['constants_all']['type']);
    }
    if ('target_type' in graph_data['constants_all']) {
        target_type = graph_data['constants_all']['target_type'];
    }
    if ('unit' in graph_data['constants_all']) {
        if (target_type == 'counter') {
            vtitle += 'total' + display_tag('unit', graph_data['constants_all']['unit']);
        } else {
            vtitle += display_tag('unit', graph_data['constants_all']['unit']);
        }
    }
    // gauge_pct etc
    if (endsWith(target_type, '_pct')) {
        vtitle += ' %';
    }
    if (vtitle != "") {
        if (target_type == 'count') {
            vtitle += "/" + count_interval;
        }
    }
    if ('target_type' in graph_data['constants_all']) {
        vtitle += "(" + display_tag('target_type', graph_data['constants_all']['target_type']) + ")";
    }
    return vtitle;
}
function create_colormap(tags) {
    var colormap = new Array();
    var static_colors = {
        "n1": "fff",
        "n2": "eee",
        "n3": "ddd",
        "n4": "ccc",
        "n5": "bbb",
        "n6": "aaa",
        "n7": "999",
        "n8": "888",
        "n9": "777"
    }
    tags.forEach(function(tag){
        colormap[tag] = (Math.round(crc32(tag)/256)).toString(16);
    });
    $.extend(colormap, static_colors);
    return colormap;
}
function get_inspect_url(data, name) {
    var q;
    if($.isArray(data['id'])) {
        q = data['id'].join(',');
    } else {
        q = data['id'];
    }
    return "" + name + "";
}

function update_dash_entry(key) {
    var entry_input = document.getElementById('query_' + key);

    var entry_fs_link = document.getElementById('link_fullscreen_' + key);
    entry_fs_link.href = '/index/' + encodeURIComponent(entry_input.value);

    $("#viewport_" + key).load("/graphs_minimal/" + encodeURIComponent(entry_input.value));
}
function update_graphs(query, graphs) {
    var query = $(query)[0].value;
    $.post('/graphs/', {query:query}, function(data) {
        $(graphs).html(data);
    });
}

<commit_msg>avoid low-contrast tag colors<commit_after>tags_order_pre = ['unit', 'type', 'target_type'];
tags_order_post = ['server', 'plugin'];
function display_tag(tag_name, tag_value) {
    // at some point it'll probably make sense to make the background color the inverse of the foreground color
    // cause there'll be so many foreground colors that are not always visible on any particular background.
    if (tag_value instanceof Array) {
        // v[0] -> the tag 'value' ("sumseries (x values)")
        // v [1] -> list of differentiators
        differentiators = tag_value[1];
        tag_value = tag_value[0];
        body = "
";
        // show "2x this-value-appears-twice, [..]"
        var differentiators_counted = {};
        differentiators.forEach(function (target, i) {
            differentiators_counted[target] = differentiators_counted[target] + 1 || 1;
        });
        for (var diff in differentiators_counted) {
            body += "
" + differentiators_counted[diff] + "x " + diff + "
";
        }
        body += "
";

        //$(document).ready(function() {
            // for some reason "body": "" results in an empty body, so do it via data-content attribute.
            //$('#' + label_id).popover({"html": true, "placement": "bottom", "trigger": "hover"});
        //});
        // leverage jquery to do html entity encoding, so we can put html code in the data attribute.
        title = jQuery('
').text('' + tag_name + ': ' + tag_value).html();
        label = "" + tag_value + "";
    } else {
        title = jQuery('
').text('' + tag_name + ': ' + tag_value).html();
        label = "" + tag_value + "";
    }
    return label;
}

function display_tag_for_legend(tag_name) {
    title = jQuery('
').text('A tag with key ' + tag_name + '').html();
    label = "" + tag_name + "";
    return label;
}

function display_word(word) {
    // word can be anything. a misc. string, a tag, a <tag>:, !<tag>=, etc
    // if it matches on a tag, display it correctly for that tag (colored label, key = parsed out tag, value the pattern)
    // otherwise generic label
    orig = word;
    if (word.charAt(0) == '!') {
        word = word.slice(1);
    }
    word = word.split('=')[0].split(':')[0];
    if (word in colormap) {
        return display_tag(word, orig);
    }
    return "" + orig + "";
}
// build a label list (colored or regular) for patterns which may contain tags
function generate_pattern_display(patterns) {
    var string = '';
    patterns.forEach(function(word) {string += display_word(word) });
    return string;
}
// generate label list for tags k/v, ordered by moving tags based on keys in pre post lists to begin/end.
function generate_title_from_dict(tags_dict, order_pre, order_post) {
    var title = '';
    var keys = Object.keys(tags_dict);
    keys.sort();
    order_pre.forEach(function(tag) {if(tag in tags_dict) {title += display_tag(tag, tags_dict[tag]); }});
    keys.forEach(function (tag) { tag_v = tags_dict[tag]; if($.inArray(tag, order_pre) < 0 && $.inArray(tag, order_post) < 0) {title += display_tag(tag,tag_v); }});
    order_post.forEach(function(tag) {if(tag in tags_dict) {title += display_tag(tag, tags_dict[tag]); }});
    return title;
}
function generate_tag_legend_display(tags_list) {
    var title = 'Tag legend: ';
    tags_list.sort();
    tags_order_pre.forEach(function(tag) {if($.inArray(tag, tags_list) >= 0) {title += display_tag_for_legend(tag); }});
    tags_list.forEach(function(tag) {if($.inArray(tag, tags_order_pre) < 0 && $.inArray(tag, tags_order_post) < 0) {title += display_tag_for_legend(tag); }});
    tags_order_post.forEach(function(tag) {if($.inArray(tag, tags_list) >= 0) {title += display_tag_for_legend(tag); }});
    return title;
}
function get_graph_name(key, graph_data) {
    // set graph_name; with each tag in its own color. this way it's very clear how it's related to the query (esp. the group by)
    // remove all constants that are already printed in vtitle..
    filtered_constants = [];
    $.map(graph_data["constants_all"], function (tag_v, tag_k) {
        if (jQuery.inArray(tag_k, ["stat", "type", "unit", "target_type"]) == -1) {
            filtered_constants[tag_k] = tag_v;
        }
    });

    var graph_name = generate_title_from_dict(filtered_constants, tags_order_pre, tags_order_post);
    // note graph_name can be empty
    return graph_name;
}
// http://stackoverflow.com/questions/280634/endswith-in-javascript
function endsWith(str, suffix) {
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function get_vtitle(graph_data) {
    //automatically generate vtitle, if possible
    var vtitle = "";
    var target_type = "";
    if ('stat' in graph_data['constants_all']) {
        vtitle += display_tag('stat', graph_data['constants_all']['stat']);
    }
    if ('type' in graph_data['constants_all']) {
        vtitle += display_tag('type', graph_data['constants_all']['type']);
    }
    if ('target_type' in graph_data['constants_all']) {
        target_type = graph_data['constants_all']['target_type'];
    }
    if ('unit' in graph_data['constants_all']) {
        if (target_type == 'counter') {
            vtitle += 'total' + display_tag('unit', graph_data['constants_all']['unit']);
        } else {
            vtitle += display_tag('unit', graph_data['constants_all']['unit']);
        }
    }
    // gauge_pct etc
    if (endsWith(target_type, '_pct')) {
        vtitle += ' %';
    }
    if (vtitle != "") {
        if (target_type == 'count') {
            vtitle += "/" + count_interval;
        }
    }
    if ('target_type' in graph_data['constants_all']) {
        vtitle += "(" + display_tag('target_type', graph_data['constants_all']['target_type']) + ")";
    }
    return vtitle;
}
function create_colormap(tags) {
    var colormap = new Array();
    var static_colors = {
        "n1": "fff",
        "n2": "eee",
        "n3": "ddd",
        "n4": "ccc",
        "n5": "bbb",
        "n6": "aaa",
        "n7": "999",
        "n8": "888",
        "n9": "777"
    }
    tags.forEach(function(tag){
        r = 0;
        g = 0;
        compute_of = tag
        while (r < 50 && g < 50) {
            color = Math.round(crc32(compute_of)/256).toString(16);
            r = color.substring(0,2);
            g = color.substring(2,4);
            console.log(tag + "-" + compute_of + "-> color" + color);
            compute_of += "a";
        }
        colormap[tag] = color;
    });
    $.extend(colormap, static_colors);
    return colormap;
}
function get_inspect_url(data, name) {
    var q;
    if($.isArray(data['id'])) {
        q = data['id'].join(',');
    } else {
        q = data['id'];
    }
    return "" + name + "";
}

function update_dash_entry(key) {
    var entry_input = document.getElementById('query_' + key);

    var entry_fs_link = document.getElementById('link_fullscreen_' + key);
    entry_fs_link.href = '/index/' + encodeURIComponent(entry_input.value);

    $("#viewport_" + key).load("/graphs_minimal/" + encodeURIComponent(entry_input.value));
}
function update_graphs(query, graphs) {
    var query = $(query)[0].value;
    $.post('/graphs/', {query:query}, function(data) {
        $(graphs).html(data);
    });
}

<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { CSSTransitionGroup } from 'react-transition-group';
import './Cell.css';

class Cell extends Component {
  color() {
    if (this.props.value === 1) {
      return 'blue';
    } else if (this.props.value === 2) {
      return 'black';
    } else {
      return 'red';
    }
  }

  render() {
    const { onClickCell, value, cycle } = this.props;

    return (
      <CSSTransitionGroup
        component="div"
        className="Cell"
        onClick={onClickCell}
        transitionName="Cell-flip"
        transitionEnterTimeout={500}
        transitionLeaveTimeout={500}
      >

      </CSSTransitionGroup>
    );
  }
}

export default Cell;

//

//   {cycle}
//

<commit_msg>Add cheat mode numbers back to cells, fix mismatch in animation times<commit_after>import React, { Component } from 'react';
import { CSSTransitionGroup } from 'react-transition-group';
import './Cell.css';

class Cell extends Component {
  color() {
    if (this.props.value === 1) {
      return 'blue';
    } else if (this.props.value === 2) {
      return 'black';
    } else {
      return 'red';
    }
  }

  render() {
    const { onClickCell, value, cycle } = this.props;

    return (
      <CSSTransitionGroup
        component="div"
        className="Cell"
        onClick={onClickCell}
        transitionName="Cell-flip"
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300}
      >

              {cycle}

      </CSSTransitionGroup>
    );
  }
}

export default Cell;
<|endoftext|>


<commit_before>/** ****************************************************************************
*   @license                                                                   *
*   Copyright 2017 random-parts. All Rights Reserved.                          *
*                                                                              *
*   Licensed under the Apache License, Version 2.0 (the "License");            *
*   you may not use this file except in compliance with the License.           *
*   You may obtain a copy of the License at                                    *
*                                                                              *
*       http://www.apache.org/licenses/LICENSE-2.0                             *
*                                                                              *
*   Unless required by applicable law or agreed to in writing, software        *
*   distributed under the License is distributed on an "AS IS" BASIS,          *
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
*   See the License for the specific language governing permissions and        *
*   limitations under the License.                                             *
***************************************************************************** */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*       _____               ___               _      ___  _____   _____        *
*      |_   _|__ __ _ _ __ / __|_ __  ___ _ _| |_ __| _ \/ __\ \ / / _ \       *
*        | |/ -_) _` | '  \\__ \ '_ \/ _ \ '_|  _(_-<   /\__ \\ V /|  _/       *
*        |_|\___\__,_|_|_|_|___/ .__/\___/_|  \__/__/_|_\|___/ \_/ |_|         *
*                              |_|                                             *
*                                                                              *
*        Team Sports RSVP - Team schedule management spreadsheet add-on        *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * @file Contains the controller methods used to orchestrate script actions
 * @license Apache License, Version 2.0
 * @version 1.2.16
 */

/**
 * ---
 * Script Configuration Object for debugging. Sets the proper Spreadsheeet Object
 * depending on if the script is run as an add-on[!debug]
 * or from the script-editor[debug]
 *
 * @namespace Config
 * @property {Boolean} debug
 * @property {String} debug_ss_id - spreadsheet id for testing
 * @property {Spreadsheet} spreadsheet - `debug:` SpreadsheetByID()

 * `!debug:` ActiveSpreadsheet()
 * @property {Form} team_form - `debug:` openByUrl()
 `!debug:` getActiveForm()
 */
var Config = {
  debug: false,
  debug_ss_id: "SHEET_ID",
  spreadsheet: function () {
    if (this.debug) { return SpreadsheetApp.openById(this.debug_ss_id) }
     else { return SpreadsheetApp.getActiveSpreadsheet() }
  },
  storage: function () {
    if (this.debug) { return PropertiesService.getScriptProperties() }
     else { return PropertiesService.getDocumentProperties() }
  },
  team_form: function () {
      var form_url = this.spreadsheet().getFormUrl();
      return (form_url != null) ? FormApp.openByUrl(form_url) : null;
  }
}

/**
 * ---
 * onInstall() event that runs once. Runs the first time the add-on is installed.
 * It sets-up the triggers then runs onOpen to build the menu.
 *
 * @param {EventObject} e
 */
function onInstall (e) {
  // Install the script project triggers
  onUpdateTriggers();

  // Run to get the Add-on Menu
  onOpen(e);
}

/**
 * ---
 * onOpen() event runs the start-up processes, i.e add-on menu build,
 * when the speadsheet is opened.
 *
 * @param {EventObject} e
 */
function onOpen (e) {
  var ss = Config.spreadsheet();
  // Build the Add-On Menu
  ui().menu(e);
  SpreadsheetApp.flush();

  ss.setActiveSheet(ss.getSheets()[0]);
}

/**
 * ---
 * Auto runs the daily tasks. Set to update the schedule and then
 * process any emails that should go out for the day.
 */
function onDailyTrigger () {
  var ss = Config.spreadsheet();
  // Create a team form if one is not attached
  if (ss.getFormUrl() == null) { onCreateForm() }

  // Update the form confirmation message
  formService().updateConfirmation();

  // Update the displayed schedule
  scheduleService().update();

  // Run email task
  emailService().sendMail("today");
}

/**
 * ---
 * Form submit event triggers the app to update the displayed season schedule
 *
 * @param {EventObject} e
 */
function onFormSubmit (e) {
  formService().handleResponse(e);
}

/**
 * ---
 * Sheet edit event within the web schedule range triggers an update to
 * the displayed schedule when it pull changes in the schedule from the website
 *
 * @requires Installed onEdit() Trigger
 * @param {EventObject} e
 */
function onSheetEdit (e) {
  var ss = e.source;
  var range = e.range;
  var change_cell_row = e.range.rowStart;
  var change_cell_col = e.range.columnStart;
  var data_range = ss.getRangeByName("webSchedule");

  // Check if the edits are with the web data/schedule values
  if ((data_range.getRow() <= change_cell_row == change_cell_row <= data_range.getLastRow()) &&
      (data_range.getColumn() <= change_cell_col == change_cell_col <= data_range.getLastColumn())) {
    // Run the schedule update
    scheduleService().update();
  }
}

/**
 * ---
 * Creates the sheet(s) needed to run a new season. Can be run from the
 * Add-on Menu.
 *
 */
function onNewSeason () {
  var ss = Config.spreadsheet();
  var sheet_name = ui().prompt.sheetname;
  var ss_triggers = ScriptApp.getUserTriggers(ss);
  var active_sheet;
   // Install add-on triggers for this spreadsheet
   if (ss_triggers.length == 0) { onUpdateTriggers() }
   // Create a template sheet if one does not exist
   if (template(ss).getTemplate() == null) { sheetService().create.template() }

  // Create a season sheet
  sheetService().create.season(sheet_name);
  // Give it time to do what it needs to do
  Utilities.sleep(250);

  // Set the new sheet as the ActiveSheet
  active_sheet = ss.getSheets()[0];
  active_sheet.showSheet();
  ss.setActiveSheet(active_sheet);

  SpreadsheetApp.flush();
  Utilities.sleep(250);

  // Copy the returning squad mates to the new season sheet
  teamService().squad.setReturning();

  SpreadsheetApp.flush();
  Utilities.sleep(250);

  // Set the new web/online schedule link & team name
  onWeblinkUpdate();

  // Create a team form if one is not attached
  if (ss.getFormUrl() == null) { onCreateForm() }

  // Set the sheet edit protection & data validation
  sheetService().update.protection();
  sheetService().update.validation();

  // Let the user know the set-up is finished
  ui().alert.done();
}

/*******************************************************************************
*                       Menu Options - Advanced Features                       *
*******************************************************************************/

/**
 * --- Menu Option: "Extra Options Help" */
function onOptionsHelp () { ui().modal.optionsHelp() }

/**
 * --- Menu Option: "Re-install Triggers" */
function onUpdateTriggers () { utils().script.install.triggers() }

/**
 * --- Menu Option: "Remove Triggers" */
function onRemoveTriggers () { utils().script.clean.triggers() }

/**
 * --- Menu Option: "Remove Conditional Formatting" */
function onRemoveCondFormatting () { sheetService().clear.conditionalFormatting() }

/**
 * --- Menu Option: "Remove Dependencies [soft un-install]" */
function onRemoveDependencies () {
  utils().script.clean.triggers();
  settings().clearAll();
  formService().removeForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);

  sheetService().remove.sheet(settings().sheet.template_name);
}

/**
 * --- Menu Option: "Restore Data Validation" */
function onUpdateDataValidation () { sheetService().update.validation() }

/**
 * --- Menu Option: "Restore NamedRanges" */
function onUpdateNamedRange () { sheetService().update.namedRanges() }

/**
 * --- Menu Option: "Restore Conditional Formatting" */
function onUpdateCondFormatting () { sheetService().update.conditionalFormatting() }

/**
 * ---
 * Add-on Menu option to manually run the schedule update.
 */
function onRunSchedule () {
  scheduleService().update();
}

/**
 * ---
 * Add-on Menu option to manually send debt emails
 */
function onRunDebtEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty) {
     emailService().sendMail("other", "debt");
  }
}

/**
 * ---
 * Add-on Menu option to manually send emails for the next gameday
 */
function onRunNextEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty && ui().confirm.sendemail() == true) {
     emailService().sendMail("next");
  }
}

/**
 * ---
 * Add-on Menu option to manually send `returning next season` emails
 */
function onRunReturningEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty) {
     emailService().sendMail("other", "returning");
  }
}

/**
 * ---
 * Prompts the user to enter the link for the online schedule,
 * sets it in the sheet, and sets the team name in the spreadsheet and
 * within the sheet's attached Form from the resulting schedule.
 *
 */
function onWeblinkUpdate () {
  var name_prompt = ui().confirm.teamname;
  var isConfirmed = ui().confirm.clearall;

  // Prompt to clear schedule range if it is not blank
  if (!scheduleService().isScheduleBlank()) {
    if (!isConfirmed()) { return }

    sheetService().clear.scheduleRanges();
    // Reset data validation and protections
    sheetService().update.protection();
    sheetService().update.validation();
  }

  // Prompt and set the web link
  scheduleService().setWebLink(ui().prompt.weblink());
  teamService().updateName(name_prompt);

  SpreadsheetApp.flush();
  Utilities.sleep(500);

  // Update the form title to match the team name
  formService().updateTitle(team().name);

  // Update displayed schedule
  onRunSchedule();
}

/**
 * ---
 * Detaches the active form from the spreadsheet before
 * creating a new form and attaching it.
 */
function onResetForm () {
  // Detach the attached form and clear the response sheet
  formService().removeForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);
  // Create Team Form
  onCreateForm();
}

/**
 * ---
 * Creating a new form and attaches it to the spreadsheet
 */
 function onCreateForm () {
  // Create Team Form
  formService().createForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);
  // Update the form confirmation message
  formService().updateConfirmation();
}<commit_msg>add send log email onDailyTrigger()<commit_after>/** ****************************************************************************
*   @license                                                                   *
*   Copyright 2017 random-parts. All Rights Reserved.                          *
*                                                                              *
*   Licensed under the Apache License, Version 2.0 (the "License");            *
*   you may not use this file except in compliance with the License.           *
*   You may obtain a copy of the License at                                    *
*                                                                              *
*       http://www.apache.org/licenses/LICENSE-2.0                             *
*                                                                              *
*   Unless required by applicable law or agreed to in writing, software        *
*   distributed under the License is distributed on an "AS IS" BASIS,          *
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
*   See the License for the specific language governing permissions and        *
*   limitations under the License.                                             *
***************************************************************************** */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*       _____               ___               _      ___  _____   _____        *
*      |_   _|__ __ _ _ __ / __|_ __  ___ _ _| |_ __| _ \/ __\ \ / / _ \       *
*        | |/ -_) _` | '  \\__ \ '_ \/ _ \ '_|  _(_-<   /\__ \\ V /|  _/       *
*        |_|\___\__,_|_|_|_|___/ .__/\___/_|  \__/__/_|_\|___/ \_/ |_|         *
*                              |_|                                             *
*                                                                              *
*        Team Sports RSVP - Team schedule management spreadsheet add-on        *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/**
 * @file Contains the controller methods used to orchestrate script actions
 * @license Apache License, Version 2.0
 * @version 1.2.16
 */

/**
 * ---
 * Script Configuration Object for debugging. Sets the proper Spreadsheeet Object
 * depending on if the script is run as an add-on[!debug]
 * or from the script-editor[debug]
 *
 * @namespace Config
 * @property {Boolean} debug
 * @property {String} debug_ss_id - spreadsheet id for testing
 * @property {Spreadsheet} spreadsheet - `debug:` SpreadsheetByID()

 * `!debug:` ActiveSpreadsheet()
 * @property {Form} team_form - `debug:` openByUrl()
 `!debug:` getActiveForm()
 */
var Config = {
  debug: false,
  debug_ss_id: "SHEET_ID",
  spreadsheet: function () {
    if (this.debug) { return SpreadsheetApp.openById(this.debug_ss_id) }
     else { return SpreadsheetApp.getActiveSpreadsheet() }
  },
  storage: function () {
    if (this.debug) { return PropertiesService.getScriptProperties() }
     else { return PropertiesService.getDocumentProperties() }
  },
  team_form: function () {
      var form_url = this.spreadsheet().getFormUrl();
      return (form_url != null) ? FormApp.openByUrl(form_url) : null;
  }
}

/**
 * ---
 * onInstall() event that runs once. Runs the first time the add-on is installed.
 * It sets-up the triggers then runs onOpen to build the menu.
 *
 * @param {EventObject} e
 */
function onInstall (e) {
  // Install the script project triggers
  onUpdateTriggers();

  // Run to get the Add-on Menu
  onOpen(e);
}

/**
 * ---
 * onOpen() event runs the start-up processes, i.e add-on menu build,
 * when the speadsheet is opened.
 *
 * @param {EventObject} e
 */
function onOpen (e) {
  var ss = Config.spreadsheet();
  // Build the Add-On Menu
  ui().menu(e);
  SpreadsheetApp.flush();

  ss.setActiveSheet(ss.getSheets()[0]);
}

/**
 * ---
 * Auto runs the daily tasks. Set to update the schedule and then
 * process any emails that should go out for the day.
 */
function onDailyTrigger () {
  var ss = Config.spreadsheet();
  // Create a team form if one is not attached
  if (ss.getFormUrl() == null) { onCreateForm() }

  // Update the form confirmation message
  formService().updateConfirmation();

  // Update the displayed schedule
  scheduleService().update();

  // Run email task
  emailService().sendMail("today");

  SpreadsheetApp.flush();

  // Send daily email log
  emailService().sendMail("log");

  // Clear the email log
  storage().clear("email");
}

/**
 * ---
 * Form submit event triggers the app to update the displayed season schedule
 *
 * @param {EventObject} e
 */
function onFormSubmit (e) {
  formService().handleResponse(e);
}

/**
 * ---
 * Sheet edit event within the web schedule range triggers an update to
 * the displayed schedule when it pull changes in the schedule from the website
 *
 * @requires Installed onEdit() Trigger
 * @param {EventObject} e
 */
function onSheetEdit (e) {
  var ss = e.source;
  var range = e.range;
  var change_cell_row = e.range.rowStart;
  var change_cell_col = e.range.columnStart;
  var data_range = ss.getRangeByName("webSchedule");

  // Check if the edits are with the web data/schedule values
  if ((data_range.getRow() <= change_cell_row == change_cell_row <= data_range.getLastRow()) &&
      (data_range.getColumn() <= change_cell_col == change_cell_col <= data_range.getLastColumn())) {
    // Run the schedule update
    scheduleService().update();
  }
}

/**
 * ---
 * Creates the sheet(s) needed to run a new season. Can be run from the
 * Add-on Menu.
 *
 */
function onNewSeason () {
  var ss = Config.spreadsheet();
  var sheet_name = ui().prompt.sheetname;
  var ss_triggers = ScriptApp.getUserTriggers(ss);
  var active_sheet;
   // Install add-on triggers for this spreadsheet
   if (ss_triggers.length == 0) { onUpdateTriggers() }
   // Create a template sheet if one does not exist
   if (template(ss).getTemplate() == null) { sheetService().create.template() }

  // Create a season sheet
  sheetService().create.season(sheet_name);
  // Give it time to do what it needs to do
  Utilities.sleep(250);

  // Set the new sheet as the ActiveSheet
  active_sheet = ss.getSheets()[0];
  active_sheet.showSheet();
  ss.setActiveSheet(active_sheet);

  SpreadsheetApp.flush();
  Utilities.sleep(250);

  // Copy the returning squad mates to the new season sheet
  teamService().squad.setReturning();

  SpreadsheetApp.flush();
  Utilities.sleep(250);

  // Set the new web/online schedule link & team name
  onWeblinkUpdate();

  // Create a team form if one is not attached
  if (ss.getFormUrl() == null) { onCreateForm() }

  // Set the sheet edit protection & data validation
  sheetService().update.protection();
  sheetService().update.validation();

  // Let the user know the set-up is finished
  ui().alert.done();
}

/*******************************************************************************
*                       Menu Options - Advanced Features                       *
*******************************************************************************/

/**
 * --- Menu Option: "Extra Options Help" */
function onOptionsHelp () { ui().modal.optionsHelp() }

/**
 * --- Menu Option: "Re-install Triggers" */
function onUpdateTriggers () { utils().script.install.triggers() }

/**
 * --- Menu Option: "Remove Triggers" */
function onRemoveTriggers () { utils().script.clean.triggers() }

/**
 * --- Menu Option: "Remove Conditional Formatting" */
function onRemoveCondFormatting () { sheetService().clear.conditionalFormatting() }

/**
 * --- Menu Option: "Remove Dependencies [soft un-install]" */
function onRemoveDependencies () {
  utils().script.clean.triggers();
  settings().clearAll();
  formService().removeForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);

  sheetService().remove.sheet(settings().sheet.template_name);
}

/**
 * --- Menu Option: "Restore Data Validation" */
function onUpdateDataValidation () { sheetService().update.validation() }

/**
 * --- Menu Option: "Restore NamedRanges" */
function onUpdateNamedRange () { sheetService().update.namedRanges() }

/**
 * --- Menu Option: "Restore Conditional Formatting" */
function onUpdateCondFormatting () { sheetService().update.conditionalFormatting() }

/**
 * ---
 * Add-on Menu option to manually run the schedule update.
 */
function onRunSchedule () {
  scheduleService().update();
}

/**
 * ---
 * Add-on Menu option to manually send debt emails
 */
function onRunDebtEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty) {
     emailService().sendMail("other", "debt");
  }
}

/**
 * ---
 * Add-on Menu option to manually send emails for the next gameday
 */
function onRunNextEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty && ui().confirm.sendemail() == true) {
     emailService().sendMail("next");
  }
}

/**
 * ---
 * Add-on Menu option to manually send `returning next season` emails
 */
function onRunReturningEmail () {
  var ss = Config.spreadsheet();
  var isEmpty = ss.getRangeByName("squadEmail").isBlank();

  if (!isEmpty) {
     emailService().sendMail("other", "returning");
  }
}

/**
 * ---
 * Prompts the user to enter the link for the online schedule,
 * sets it in the sheet, and sets the team name in the spreadsheet and
 * within the sheet's attached Form from the resulting schedule.
 *
 */
function onWeblinkUpdate () {
  var name_prompt = ui().confirm.teamname;
  var isConfirmed = ui().confirm.clearall;

  // Prompt to clear schedule range if it is not blank
  if (!scheduleService().isScheduleBlank()) {
    if (!isConfirmed()) { return }

    sheetService().clear.scheduleRanges();
    // Reset data validation and protections
    sheetService().update.protection();
    sheetService().update.validation();
  }

  // Prompt and set the web link
  scheduleService().setWebLink(ui().prompt.weblink());
  teamService().updateName(name_prompt);

  SpreadsheetApp.flush();
  Utilities.sleep(500);

  // Update the form title to match the team name
  formService().updateTitle(team().name);

  // Update displayed schedule
  onRunSchedule();
}

/**
 * ---
 * Detaches the active form from the spreadsheet before
 * creating a new form and attaching it.
 */
function onResetForm () {
  // Detach the attached form and clear the response sheet
  formService().removeForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);
  // Create Team Form
  onCreateForm();
}

/**
 * ---
 * Creating a new form and attaches it to the spreadsheet
 */
 function onCreateForm () {
  // Create Team Form
  formService().createForm();

  SpreadsheetApp.flush();
  Utilities.sleep(500);
  // Update the form confirmation message
  formService().updateConfirmation();
}<|endoftext|>


<commit_before>import EventEmitter from 'events';
import Immutable, {Map} from 'immutable';

/**
 * Copyright (c) 2016, Nitrogen Labs, Inc.
 * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
 */

class Flux extends EventEmitter {
  /**
   * Create a new instance of Flux.  Note that the Flux object
   * is a Singleton pattern, so only one should ever exist.
   *
   * @constructor
   * @this {Flux}
   * @param {object} options  Over-rides for the default set of options
   */
  constructor(options = {}) {
    super();
    this.options = options;

    // Create a hash of all the stores - used for registration / deregistration
    this._storeClasses = Map();
    this._store = Map();
    this.debug = false;
  }

  off(event, listener) {
    this.removeListener(event, listener);
  }

  /**
   * Dispatches an action to all stores
   *
   * @param {...Objects} actions to dispatch to all the stores
   */
  dispatch(...actions) {
    if(!Array.isArray(actions)) {
      return;
    }

    // Loop through actions
    actions.map(a => {
      if(typeof a.type !== 'string') {
        return;
      }

      const {type, ...data} = a;
      const oldState = Map(this._store);

      // When an action comes in, it must be completely handled by all stores
      this._storeClasses.map(storeClass => {
        const name = storeClass.name;
        const state = this._store.get(name) || Immutable.fromJS(storeClass.initialState()) || Map();
        this._store = this._store.set(name, storeClass.onAction(type, data, state) || state);
        return storeClass.setState(this._store.get(name));
      });

      if(!this._store.equals(oldState)) {
        if(this.debug) {
          console.group(`%c FLUX ACTION: ${type}`, 'font-weight:700');
          console.log('%c Action: ', 'color: #00C4FF', a);
          console.log('%c Last State: ', 'color: #959595', oldState.toJS());
          console.log('%c New State: ', 'color: #00d484', this._store.toJS());
          console.groupEnd();
        }
      }

      this.emit(type, data);
    });
  }

  /**
   * Enables the console debugger
   */
  enableDebugger() {
    this.debug = true;
  }

  /**
   * Gets the current state object
   *
   * @param {string} [name] (optional) The name of the store for just that object, otherwise it will return all store
   *   objects.
   * @returns {Map} the state object
   */
  getStore(name = '') {
    let store;

    if(name !== '') {
      store = this._store.get(name);
    } else {
      store = this._store;
    }

    return store || Map();
  }

  /**
   * Registers a new Store with Flux
   *
   * @param {Class} StoreClass A unique name for the Store
   */
  registerStore(StoreClass) {
    const name = StoreClass.name.toLowerCase();

    if(!this._storeClasses.has(name)) {
      // Create store object
      const store = new StoreClass();
      this._storeClasses = this._storeClasses.set(name, store);

      // Get default values
      const state = this._store.get(name) || Immutable.fromJS(store.initialState()) || Map();
      this._store = this._store.set(name, state);
    }

    return this._storeClasses.get(name);
  }

  /**
   * De-registers a named store from Flux
   *
   * @param {string} name The name of the store
   */
  deregisterStore(name = '') {
    name = name.toLowerCase();
    this._storeClasses.delete(name);
    this._store = this._store.delete(name);
  }

  /**
   * Gets a store object that is registered with Flux
   *
   * @param {string} name The name of the store
   * @returns {Store} the store object
   */
  getClass(name = '') {
    name = name.toLowerCase();
    return this._storeClasses.get(name);
  }

  /**
   * Saves data to the sessionStore
   *
   * @param {string} key The name of the store
   * @param {string|object|array|Immutable} value The name of the store
   */
  setSessionData(key, value) {
    if(Immutable.Iterable.isIterable(value)) {
      value = value.toJS();
    }

    sessionStorage.setItem(key, JSON.stringify(value));
  }

  /**
   * Gets data from
   *
   * @param {string} name The name of the store
   * @returns {Immutable} the data object associated with the key
   */
  getSessionData(key) {
    return Immutable.fromJS(JSON.parse(sessionStorage.getItem(key) || '""'));
  }

  /**
   * Removes a key from sessionStorage
   *
   * @param {string} key Key associated with the data to remove
   */
  delSessionData(key) {
    sessionStorage.removeItem(key);
  }

  /**
   * Saves data to localStore
   *
   * @param {string} name The name of the store
   * @param {string|object|array|Immutable} value The name of the store
   */
  setLocalData(key, value) {
    if(Immutable.Iterable.isIterable(value)) {
      value = value.toJS();
    }

    localStorage.setItem(key, JSON.stringify(value));
  }

  /**
   * Gets a store that is registered with Flux
   *
   * @param {string} name The name of the store
   * @returns {Immutable} the data object associated with the key
   */
  getLocalData(key) {
    return Immutable.fromJS(JSON.parse(localStorage.getItem(key) || '""'));
  }

  /**
   * Removes a key from localStorage
   *
   * @param {string} key Key associated with the data to remove
   */
  delLocalData(key) {
    localStorage.removeItem(key);
  }
}

const flux = new Flux();
export default flux;
<commit_msg>get store now accepts array and string<commit_after>import EventEmitter from 'events';
import Immutable, {Map} from 'immutable';

/**
 * Copyright (c) 2016, Nitrogen Labs, Inc.
 * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
 */

class Flux extends EventEmitter {
  /**
   * Create a new instance of Flux.  Note that the Flux object
   * is a Singleton pattern, so only one should ever exist.
   *
   * @constructor
   * @this {Flux}
   */
  constructor() {
    super();

    // Create a hash of all the stores - used for registration / de-registration
    this._storeClasses = Map();
    this._store = Map();
    this.debug = false;
  }

  off(event, listener) {
    this.removeListener(event, listener);
  }

  /**
   * Dispatches an action to all stores
   *
   * @param {...Objects} actions to dispatch to all the stores
   */
  dispatch(...actions) {
    if(!Array.isArray(actions)) {
      return;
    }

    // Loop through actions
    actions.map(a => {
      if(typeof a.type !== 'string') {
        return;
      }

      const {type, ...data} = a;
      const oldState = this._store;

      // When an action comes in, it must be completely handled by all stores
      this._storeClasses.map(storeClass => {
        const name = storeClass.name;
        const state = this._store.get(name) || Immutable.fromJS(storeClass.initialState()) || Map();
        this._store = this._store.set(name, storeClass.onAction(type, data, state) || state);
        return storeClass.setState(this._store.get(name));
      });

      if(this.debug) {
        const actionObj = Immutable.fromJS(a).toJS();

        console.group(`%c FLUX ACTION: ${type}`, 'font-weight:700');
        console.log('%c Action: ', 'color: #00C4FF', actionObj);
        console.log('%c Last State: ', 'color: #959595', oldState.toJS());
        console.log('%c New State: ', 'color: #00d484', this._store.toJS());
        console.groupEnd();
      }

      this.emit(type, data);
    });
  }

  /**
   * Enables the console debugger
   */
  enableDebugger() {
    this.debug = true;
  }

  /**
   * Gets the current state object
   *
   * @param {string} [name] (optional) The name of the store for just that object, otherwise it will return all store
   *   objects.
   * @returns {Map} the state object
   */
  getStore(name = '') {
    let store;

    if(Array.isArray(name)) {
      store = this._store.getIn(name);
    }
    else if(name !== '') {
      store = this._store.get(name);
    } else {
      store = this._store;
    }

    return store || Map();
  }

  /**
   * Registers a new Store with Flux
   *
   * @param {Class} StoreClass A unique name for the Store
   */
  registerStore(StoreClass) {
    const name = StoreClass.name.toLowerCase();

    if(!this._storeClasses.has(name)) {
      // Create store object
      const store = new StoreClass();
      this._storeClasses = this._storeClasses.set(name, store);

      // Get default values
      const state = this._store.get(name) || Immutable.fromJS(store.initialState()) || Map();
      this._store = this._store.set(name, state);
    }

    return this._storeClasses.get(name);
  }

  /**
   * De-registers a named store from Flux
   *
   * @param {string} name The name of the store
   */
  deregisterStore(name = '') {
    name = name.toLowerCase();
    this._storeClasses.delete(name);
    this._store = this._store.delete(name);
  }

  /**
   * Gets a store object that is registered with Flux
   *
   * @param {string} name The name of the store
   * @returns {Store} the store object
   */
  getClass(name = '') {
    name = name.toLowerCase();
    return this._storeClasses.get(name);
  }

  /**
   * Saves data to the sessionStore
   *
   * @param {string} key Key to store data
   * @param {string|object|array|Immutable} value Data to store.
   */
  setSessionData(key, value) {
    if(Immutable.Iterable.isIterable(value)) {
      value = value.toJS();
    }

    sessionStorage.setItem(key, JSON.stringify(value));
  }

  /**
   * Gets data from
   *
   * @param {string} key The key for data
   * @returns {Immutable} the data object associated with the key
   */
  getSessionData(key) {
    return Immutable.fromJS(JSON.parse(sessionStorage.getItem(key) || '""'));
  }

  /**
   * Removes a key from sessionStorage
   *
   * @param {string} key Key associated with the data to remove
   */
  delSessionData(key) {
    sessionStorage.removeItem(key);
  }

  /**
   * Saves data to localStore
   *
   * @param {string} key Key to store data
   * @param {string|object|array|Immutable} value Data to store.
   */
  setLocalData(key, value) {
    if(Immutable.Iterable.isIterable(value)) {
      value = value.toJS();
    }

    localStorage.setItem(key, JSON.stringify(value));
  }

  /**
   * Gets a store that is registered with Flux
   *
   * @param {string} key The key for data
   * @returns {Immutable} the data object associated with the key
   */
  getLocalData(key) {
    return Immutable.fromJS(JSON.parse(localStorage.getItem(key) || '""'));
  }

  /**
   * Removes a key from localStorage
   *
   * @param {string} key Key associated with the data to remove
   */
  delLocalData(key) {
    localStorage.removeItem(key);
  }
}

const flux = new Flux();
export default flux;
<|endoftext|>


<commit_before>/* @flow */

import * as React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { SchemaModel, Schema } from 'rsuite-schema';
import classNames from 'classnames';

import { getUnhandledProps, prefix } from './utils';
import { defaultClassPrefix } from './utils/prefix';

type Props = {
  className?: string,
  layout?: 'horizontal' | 'vertical' | 'inline',
  values?: Object,
  defaultValues?: Object,
  errors?: Object,
  checkDelay?: number,
  checkTrigger?: 'change' | 'blur' | 'none',
  onChange?: (values: Object, event: SyntheticEvent<*>) => void,
  onError?: (errors: Object) => void,
  onCheck?: (errors: Object) => void,
  model: typeof Schema,
  classPrefix: string
};

type State = {
  errors?: Object,
  values?: Object
};

class Form extends React.Component<Props, State> {
  static defaultProps = {
    classPrefix: defaultClassPrefix('form'),
    model: SchemaModel({}),
    layout: 'vertical',
    defaultValues: {},
    checkDelay: 500,
    checkTrigger: 'change'
  };

  static childContextTypes = {
    form: PropTypes.object.isRequired
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      errors: props.errors || {},
      /**
       * 把当前 values 维护到 state 中，主要为 Form 中的 check 方法
       * 默认会设置 props.values ，
       * 如果还是没有的话就默认为 {}
       */
      values: props.values || {}
    };
  }
  getChildContext() {
    const { defaultValues, model, checkTrigger } = this.props;
    const { errors, values } = this.state;
    return {
      form: {
        onFieldChange: this.handleFieldChange,
        onFieldError: this.handleFieldError,
        onFieldSuccess: this.handleFieldSuccess,
        checkTrigger,
        values,
        defaultValues,
        errors,
        model
      }
    };
  }

  componentWillReceiveProps(nextProps: Props) {
    if (!_.isEqual(nextProps.errors, this.props.errors)) {
      this.setState({
        errors: nextProps.errors
      });
    }

    if (!_.isEqual(nextProps.values, this.props.values)) {
      this.setState({
        values: nextProps.values
      });
    }
  }
  /**
   * public APIs
   */
  check = (callback: (errors: Object) => void) => {
    const { values } = this.state;
    const { defaultValues, model, onCheck, onError } = this.props;
    const errors = {};
    let errorCount = 0;

    const nextValues = Object.assign({}, defaultValues, values);
    Object.keys(model.schema).forEach(key => {
      const checkResult = model.checkForField(key, nextValues[key]);

      if (checkResult.hasError === true) {
        errorCount += 1;
        errors[key] = checkResult.errorMessage;
      }
    });

    this.setState({ errors });
    onCheck && onCheck(errors);
    callback && callback(errors);
    if (errorCount > 0) {
      onError && onError(errors);
      return false;
    }

    return true;
  };

  /**
   * public APIs
   */
  cleanErrors(callback: () => void) {
    this.setState({ errors: {} }, callback);
  }

  /**
   * public APIs
   */
  resetErrors(errors: Object = {}, callback: () => void) {
    this.setState({ errors }, callback);
  }

  handleFieldError = (name: string, error: string) => {
    const { onError, onCheck } = this.props;
    const errors = Object.assign({}, this.state.errors, {
      [name]: error
    });

    this.setState({ errors }, () => {
      onError && onError(errors);
      onCheck && onCheck(errors);
    });
  };

  handleFieldSuccess = (name: string) => {
    const { onCheck } = this.props;
    const errors = Object.assign({}, this.state.errors, {
      [name]: null
    });
    this.setState({ errors }, () => {
      onCheck && onCheck(errors);
    });
  };

  handleFieldChange = (name: string, value: any, event: SyntheticEvent<*>) => {
    const { onChange, defaultValues } = this.props;
    const values = Object.assign({}, this.state.values, defaultValues, {
      [name]: value
    });
    onChange && onChange(values, event);
  };

  render() {
    const { layout, classPrefix, className, ...props } = this.props;
    const addPrefix = prefix(classPrefix);
    const clesses = classNames(classPrefix, addPrefix(layout), className);
    const unhandled = getUnhandledProps(Form, props);

    return <form {...unhandled} onSubmit={e => e.preventDefault()} className={clesses} />;
  }
}

export default Form;
<commit_msg>Update Form<commit_after>/* @flow */

import * as React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { SchemaModel, Schema } from 'rsuite-schema';
import classNames from 'classnames';

import { getUnhandledProps, prefix } from './utils';
import { defaultClassPrefix } from './utils/prefix';

type Props = {
  className?: string,
  layout?: 'horizontal' | 'vertical' | 'inline',
  values?: Object,
  defaultValues?: Object,
  errors?: Object,
  checkDelay?: number,
  checkTrigger?: 'change' | 'blur' | 'none',
  onChange?: (values: Object, event: SyntheticEvent<*>) => void,
  onError?: (errors: Object) => void,
  onCheck?: (errors: Object) => void,
  model: typeof Schema,
  classPrefix: string
};

type State = {
  errors?: Object,
  values?: Object
};

class Form extends React.Component<Props, State> {
  static defaultProps = {
    classPrefix: defaultClassPrefix('form'),
    model: SchemaModel({}),
    layout: 'vertical',
    defaultValues: {},
    checkDelay: 500,
    checkTrigger: 'change'
  };

  static childContextTypes = {
    form: PropTypes.object.isRequired
  };

  constructor(props: Props) {
    super(props);
    this.state = {
      errors: props.errors || {},
      /**
       * 把当前 values 维护到 state 中，主要为 Form 中的 check 方法
       * 默认会设置 props.values ，
       * 如果还是没有的话就默认为 {}
       */
      values: props.values || {}
    };
  }
  getChildContext() {
    const { defaultValues, model, checkTrigger } = this.props;
    const { errors, values } = this.state;
    return {
      form: {
        onFieldChange: this.handleFieldChange,
        onFieldError: this.handleFieldError,
        onFieldSuccess: this.handleFieldSuccess,
        checkTrigger,
        values,
        defaultValues,
        errors,
        model
      }
    };
  }

  componentWillReceiveProps(nextProps: Props) {
    if (!_.isEqual(nextProps.errors, this.props.errors)) {
      console.log('componentWillReceiveProps', nextProps.errors);
      this.setState({
        errors: nextProps.errors
      });
    }

    if (!_.isEqual(nextProps.values, this.props.values)) {
      this.setState({
        values: nextProps.values
      });
    }
  }
  /**
   * public APIs
   */
  check = (callback: (errors: Object) => void) => {
    const { values } = this.state;
    const { defaultValues, model, onCheck, onError } = this.props;
    const errors = {};
    let errorCount = 0;

    const nextValues = Object.assign({}, defaultValues, values);
    Object.keys(model.schema).forEach(key => {
      const checkResult = model.checkForField(key, nextValues[key]);

      if (checkResult.hasError === true) {
        errorCount += 1;
        errors[key] = checkResult.errorMessage;
      }
    });

    this.setState({ errors });
    onCheck && onCheck(errors);
    callback && callback(errors);
    if (errorCount > 0) {
      onError && onError(errors);
      return false;
    }

    return true;
  };

  /**
   * public APIs
   */
  cleanErrors(callback: () => void) {
    this.setState({ errors: {} }, callback);
  }

  /**
   * public APIs
   */
  resetErrors(errors: Object = {}, callback: () => void) {
    this.setState({ errors }, callback);
  }

  handleFieldError = (name: string, errorMessage: string) => {
    const { onError, onCheck } = this.props;
    const errors = Object.assign({}, this.state.errors, {
      [name]: errorMessage
    });

    console.log(this.state.errors);

    this.setState({ errors }, () => {
      onError && onError(errors);
      onCheck && onCheck(errors);
    });
  };

  handleFieldSuccess = (name: string) => {
    const { onCheck } = this.props;
    const errors = _.omit(this.state.errors, [name]);
    console.log('handleFieldSuccess', this.state.errors);
    this.setState({ errors }, () => {
      onCheck && onCheck(errors);
    });
  };

  handleFieldChange = (name: string, value: any, event: SyntheticEvent<*>) => {
    const { onChange, defaultValues } = this.props;
    const values = Object.assign({}, this.state.values, defaultValues, {
      [name]: value
    });
    onChange && onChange(values, event);
  };

  render() {
    const { layout, classPrefix, className, ...props } = this.props;
    const addPrefix = prefix(classPrefix);
    const clesses = classNames(classPrefix, addPrefix(layout), className);
    const unhandled = getUnhandledProps(Form, props);

    return <form {...unhandled} onSubmit={e => e.preventDefault()} className={clesses} />;
  }
}

export default Form;
<|endoftext|>


<commit_before>var React = require('react-native');
var GameBoard = require('./GameBoard');
var GameHelpers = require('./GameHelpers');
var Modal = require('./Modal');

var {
  AppRegistry,
  StyleSheet,
  View,
  Animated,
  PanResponder,
  Text,
  ScrollView,
  addons,
  TouchableOpacity
  } = React;

var BOARD_SIZE = 4;

var Game = React.createClass({
  getInitialState: function () {
    return {
      indexes: GameHelpers.getShuffledIndexes(BOARD_SIZE),
    };
  },

  onNewGame: function () {
    this.setState({
      indexes: GameHelpers.getOrderedIndexes(BOARD_SIZE),
    });
  },

  onMoved: function (moveFrom, moveTo) {
    var indexesMatrix = GameHelpers.getMatrixFromIndxes(this.state.indexes, BOARD_SIZE);
    indexesMatrix[moveTo.y][moveTo.x] = indexesMatrix[moveFrom.y][moveFrom.x];
    indexesMatrix[moveFrom.y][moveFrom.x] = null;
    this.setState({
      indexes: GameHelpers.getIndexesFromMatrix(indexesMatrix)
    })
  },

  render() {
    return (
      <View  style={styles.container}>

        <View style={styles.headerArea}>
          <Text style={styles.header}>The fifteen game</Text>
          <Text style={styles.header2}>Slide tiles to put them in order.</Text>
        </View>

        <View style={styles.mainArea}>
          <GameBoard
            boardSize={BOARD_SIZE}
            indexes={this.state.indexes}
            onMoved={this.onMoved}/>
        </View>

        <View style={styles.bottomArea}>
          <TouchableOpacity onPress={this.onNewGame}>
            <Text style={styles.help}>Give up?</Text>
          </TouchableOpacity>
        </View>

        <Modal isOpen={GameHelpers.isWon(this.state.indexes, BOARD_SIZE)}>
          <View style={styles.wonDialog}>
            <Text style={styles.header}>Hooray!</Text>
            <Text style={styles.header2}>Once again?</Text>
            <TouchableOpacity onPress={this.onNewGame}>
              <View style={styles.buttonWrapper}>
                <Text style={styles.button}>Play again</Text>
              </View>
            </TouchableOpacity>
          </View>
        </Modal>

      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F4F4F4',
  },
  headerArea: {
    height: 200,
    alignItems: 'center',
    justifyContent: 'center',
  },
  mainArea: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  bottomArea: {
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  header: {
    fontSize: 25,
    fontWeight: '200',
    textAlign: 'center',
    marginBottom: 15,
  },
  header2: {
    fontSize: 15,
    fontWeight: '200',
    textAlign: 'center',
  },
  help: {
    opacity: 0.7,
  },
  wonDialog: {
    paddingHorizontal: 30,
    paddingVertical: 30,
  },
  buttonWrapper: {
    backgroundColor: '#FF3366',
    height: 55,
    justifyContent: 'center',
        marginTop: 30,
  },
  button: {
    //flex: 1,
    //height: 55,
    fontSize: 15,
    textAlign: 'center',
    color: "#FFFFFF",
  },
});

module.exports = Game;<commit_msg>shuffle game<commit_after>var React = require('react-native');
var GameBoard = require('./GameBoard');
var GameHelpers = require('./GameHelpers');
var Modal = require('./Modal');

var {
  AppRegistry,
  StyleSheet,
  View,
  Animated,
  PanResponder,
  Text,
  ScrollView,
  addons,
  TouchableOpacity
  } = React;

var BOARD_SIZE = 4;

var Game = React.createClass({
  getInitialState: function () {
    return {
      indexes: GameHelpers.getShuffledIndexes(BOARD_SIZE),
    };
  },

  onNewGame: function () {
    this.setState({
      indexes: GameHelpers.getShuffledIndexes(BOARD_SIZE),
    });
  },

  onMoved: function (moveFrom, moveTo) {
    var indexesMatrix = GameHelpers.getMatrixFromIndxes(this.state.indexes, BOARD_SIZE);
    indexesMatrix[moveTo.y][moveTo.x] = indexesMatrix[moveFrom.y][moveFrom.x];
    indexesMatrix[moveFrom.y][moveFrom.x] = null;
    this.setState({
      indexes: GameHelpers.getIndexesFromMatrix(indexesMatrix)
    })
  },

  render() {
    return (
      <View  style={styles.container}>

        <View style={styles.headerArea}>
          <Text style={styles.header}>The fifteen game</Text>
          <Text style={styles.header2}>Slide tiles to put them in order.</Text>
        </View>

        <View style={styles.mainArea}>
          <GameBoard
            boardSize={BOARD_SIZE}
            indexes={this.state.indexes}
            onMoved={this.onMoved}/>
        </View>

        <View style={styles.bottomArea}>
          <TouchableOpacity onPress={this.onNewGame}>
            <Text style={styles.help}>Give up?</Text>
          </TouchableOpacity>
        </View>

        <Modal isOpen={GameHelpers.isWon(this.state.indexes, BOARD_SIZE)}>
          <View style={styles.wonDialog}>
            <Text style={styles.header}>Hooray!</Text>
            <Text style={styles.header2}>Once again?</Text>
            <TouchableOpacity onPress={this.onNewGame}>
              <View style={styles.buttonWrapper}>
                <Text style={styles.button}>Play again</Text>
              </View>
            </TouchableOpacity>
          </View>
        </Modal>

      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F4F4F4',
  },
  headerArea: {
    height: 200,
    alignItems: 'center',
    justifyContent: 'center',
  },
  mainArea: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  bottomArea: {
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
  },
  header: {
    fontSize: 25,
    fontWeight: '200',
    textAlign: 'center',
    marginBottom: 15,
  },
  header2: {
    fontSize: 15,
    fontWeight: '200',
    textAlign: 'center',
  },
  help: {
    opacity: 0.7,
  },
  wonDialog: {
    paddingHorizontal: 30,
    paddingVertical: 30,
  },
  buttonWrapper: {
    backgroundColor: '#FF3366',
    height: 55,
    justifyContent: 'center',
        marginTop: 30,
  },
  button: {
    //flex: 1,
    //height: 55,
    fontSize: 15,
    textAlign: 'center',
    color: "#FFFFFF",
  },
});

module.exports = Game;<|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
    Text,
    View
} from 'react-native'
import { ScrollableTabBar }
       from 'react-native-scrollable-tab-view'
import { computed } from 'mobx'
import { observer } from 'mobx-react/native'

// Importing Pages
import { DiscoverPage } from './screens//DiscoverPage'
import { BarPage } from './screens/BarPage'
import { MenuPage } from './screens/MenuPage'
import { OrderPage } from './screens/OrderPage'

import { handleBackButton } from './components/AndroidBackButton'
import { SideMenu } from './components/sidemenu/SideMenu'
import { ControlPanel } from './components/sidemenu/ControlPanel'
import { TabView } from './components/Tabs'
import { Loader } from './components/Page'

import { store, barStore, tabStore } from '~/src/model/store'
import * as _ from './utils/curry'

const { log, assert } = _.utils('~/Main')

/* Do not allow font scaling */
Text.defaultProps.allowFontScaling = false

const style = {
    title: {
        color: '#E72D6B',
        fontWeight: 'bold'
    }
}

@observer
export class Main extends Component {
    static route = {
        navigationBar: {
            title: 'QDodger',
            backgroundColor: '#fff',
            titleStyle: style.title
        }
    }
    @computed get barSelected () {
        return !barStore.barID || tabStore.currentPage !== 0
    }
    render = () => {
        if (!store.initialized) {
            return <Loader />
        }
        if (!this.barSelected) {
            return <DiscoverPage />
        }
        return (
            <View style={{flex: 1}}>
              <SideMenu content={<ControlPanel />}>
                  <View style={{flex: 1, flexDirection: 'row'}}>
                      {/*
                      <Checkout />
                      <ReceiptModal />
                      */}
                      <TabView>
                          <View tabLabel='Discover' style={{flex: 1}}>
                              <DiscoverPage />
                          </View>
                          <View tabLabel='Bar' style={{flex: 1}}>
                              <BarPage />
                          </View>
                          <View tabLabel='Menu' style={{flex: 1}}>
                              <MenuPage />
                          </View>
                          <View tabLabel='Order' style={{flex: 1}}>
                              <OrderPage />
                          </View>
                      </TabView>
                  </View>
              </SideMenu>
          </View>
      )
    }

    // renderTabBar = () => <DefaultTabBar />
    // renderTabBar = () => <ScrollableTabBar />
}

log('----------------------------------------------------------------')

// Promise.onPossiblyUnhandledRejection(function(error){
//     throw error
// })

// By default the choice is not to propogate errors from Promises...
require('promise/setimmediate/rejection-tracking').enable({
    allRejections: true,
    onUnhandled: (id, error) => {
        console.error(error)
    }
})

export const main = async () => {
    handleBackButton()
    await store.initialize()
}
<commit_msg>small fix main<commit_after>import React, { Component } from 'react'
import {
    Text,
    View
} from 'react-native'
import { ScrollableTabBar }
       from 'react-native-scrollable-tab-view'
import { computed } from 'mobx'
import { observer } from 'mobx-react/native'

// Importing Pages
import { DiscoverPage } from './screens//DiscoverPage'
import { BarPage } from './screens/BarPage'
import { MenuPage } from './screens/MenuPage'
import { OrderPage } from './screens/OrderPage'

import { handleBackButton } from './components/AndroidBackButton'
import { SideMenu } from './components/sidemenu/SideMenu'
import { ControlPanel } from './components/sidemenu/ControlPanel'
import { TabView } from './components/Tabs'
import { Loader } from './components/Page'

import { store, barStore, tabStore } from './model/store'
import * as _ from './utils/curry'

const { log, assert } = _.utils('~/src/Main')

/* Do not allow font scaling */
Text.defaultProps.allowFontScaling = false

const style = {
    title: {
        color: '#E72D6B',
        fontWeight: 'bold'
    }
}

@observer
export class Main extends Component {
    static route = {
        navigationBar: {
            title: 'QDodger',
            backgroundColor: '#fff',
            titleStyle: style.title
        }
    }
    @computed get barSelected () {
        return !!barStore.barID || tabStore.currentPage !== 0
    }
    render = () => {
        if (!store.initialized) {
            return <Loader />
        }
        if (!this.barSelected) {
            return <DiscoverPage />
        }
        return (
            <View style={{flex: 1}}>
              <SideMenu content={<ControlPanel />}>
                  <View style={{flex: 1, flexDirection: 'row'}}>
                      {/*
                      <Checkout />
                      <ReceiptModal />
                      */}
                      <TabView>
                          <View tabLabel='Discover' style={{flex: 1}}>
                              <DiscoverPage />
                          </View>
                          <View tabLabel='Bar' style={{flex: 1}}>
                              <BarPage />
                          </View>
                          <View tabLabel='Menu' style={{flex: 1}}>
                              <MenuPage />
                          </View>
                          <View tabLabel='Order' style={{flex: 1}}>
                              <OrderPage />
                          </View>
                      </TabView>
                  </View>
              </SideMenu>
          </View>
      )
    }

    // renderTabBar = () => <DefaultTabBar />
    // renderTabBar = () => <ScrollableTabBar />
}

log('----------------------------------------------------------------')

// Promise.onPossiblyUnhandledRejection(function(error){
//     throw error
// })

// By default the choice is not to propogate errors from Promises...
require('promise/setimmediate/rejection-tracking').enable({
    allRejections: true,
    onUnhandled: (id, error) => {
        console.error(error)
    }
})

export const main = async () => {
    handleBackButton()
    await store.initialize()
}
<|endoftext|>