1 /*!
  2  *  mindmaps - a HTML5 powered mind mapping application
  3  *  Copyright (C) 2011  David Richard
  4  *
  5  *  This program is free software: you can redistribute it and/or modify
  6  *  it under the terms of the GNU Affero General Public License as
  7  *  published by the Free Software Foundation, either version 3 of the
  8  *  License, or (at your option) any later version.
  9  *
 10  *  This program is distributed in the hope that it will be useful,
 11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  *  GNU Affero General Public License for more details.
 14  *
 15  *  You should have received a copy of the GNU Affero General Public License
 16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 17  */
 18 
 19 /*
 20  * Make sure this is the first file to be referenced in index.hml.
 21  */
 22 
 23 // Use ECMA5 strict mode. see:
 24 // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
 25 "use strict";
 26 
 27 /**
 28  * @namespace Application wide namespace for mindmaps.
 29  */
 30 var mindmaps = mindmaps || {};
 31 
 32 /**
 33  * Start up. This function is executed when the DOM is loaded.
 34  */
 35 $(function() {
 36 	// take car of old browsers
 37 	createECMA5Shims();
 38 	createHTML5Shims();
 39 
 40 	setupConsole();
 41 	trackErrors();
 42 
 43 	// create a new app controller and go
 44 	var appController = new mindmaps.ApplicationController();
 45 	appController.go();
 46 });
 47 
 48 function trackErrors() {
 49 	window.onerror = function(msg, url, line) {
 50 		if (!window._gaq) {
 51 			return;
 52 		}
 53 
 54 		// Track JS errors in GA.
 55 		_gaq.push([ '_trackEvent', 'Error Log', msg, url + '_' + line ]);
 56 
 57 		return false; // false prevents default error handling.
 58 	};
 59 }
 60 
 61 // TODO make non global
 62 /**
 63  * Initialize the console object.
 64  */
 65 function setupConsole() {
 66 	var noOp = function() {
 67 	};
 68 
 69 	// provide console object and dummy functions if not built-in
 70 	var console = window.console || {};
 71 	console.log = console.log || noOp;
 72 	console.info = console.info || noOp;
 73 	console.debug = console.debug || noOp;
 74 	console.warn = console.warn || noOp;
 75 	console.error = console.error || noOp;
 76 
 77 	// turn all console.xx calls into no-ops when in production mode except
 78 	// for errors, do an alert.
 79 	if (!mindmaps.DEBUG) {
 80 		console.debug = noOp;
 81 		console.info = noOp;
 82 		console.log = noOp;
 83 		console.warn = noOp;
 84 		console.error = function(s) {
 85 			window.alert("Error: " + s);
 86 		};
 87 	}
 88 }
 89 
 90 /**
 91  * Creates ECMA5 shims if the browser does not implement them.
 92  */
 93 function createECMA5Shims() {
 94 	// from: https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
 95 
 96 	// ES-5 15.3.4.5
 97 	// http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
 98 	if (!Function.prototype.bind) {
 99 		var slice = Array.prototype.slice;
100 		Function.prototype.bind = function bind(that) { // .length is 1
101 			var target = this;
102 			if (typeof target.apply !== "function"
103 					|| typeof target.call !== "function")
104 				return new TypeError();
105 			var args = slice.call(arguments);
106 
107 			function bound() {
108 
109 				if (this instanceof bound) {
110 
111 					var self = Object.create(target.prototype);
112 					target.apply(self, args.concat(slice.call(arguments)));
113 					return self;
114 				} else {
115 					return target.call.apply(target, args.concat(slice
116 							.call(arguments)));
117 				}
118 
119 			}
120 			bound.length = (typeof target === "function" ? Math.max(
121 					target.length - args.length, 0) : 0);
122 			return bound;
123 		};
124 	}
125 
126 	// ES5 15.4.3.2
127 	if (!Array.isArray) {
128 		Array.isArray = function isArray(obj) {
129 			return Object.prototype.toString.call(obj) === "[object Array]";
130 		};
131 	}
132 
133 	// ES5 15.4.4.18
134 	if (!Array.prototype.forEach) {
135 		Array.prototype.forEach = function forEach(block, thisObject) {
136 			var len = +this.length;
137 			for ( var i = 0; i < len; i++) {
138 				if (i in this) {
139 					block.call(thisObject, this[i], i, this);
140 				}
141 			}
142 		};
143 	}
144 
145 	// ES5 15.4.4.19
146 	// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
147 	if (!Array.prototype.map) {
148 		Array.prototype.map = function map(fun /* , thisp */) {
149 			var len = +this.length;
150 			if (typeof fun !== "function")
151 				throw new TypeError();
152 
153 			var res = new Array(len);
154 			var thisp = arguments[1];
155 			for ( var i = 0; i < len; i++) {
156 				if (i in this)
157 					res[i] = fun.call(thisp, this[i], i, this);
158 			}
159 
160 			return res;
161 		};
162 	}
163 
164 	// ES5 15.4.4.20
165 	if (!Array.prototype.filter) {
166 		Array.prototype.filter = function filter(block /* , thisp */) {
167 			var values = [];
168 			var thisp = arguments[1];
169 			for ( var i = 0; i < this.length; i++)
170 				if (block.call(thisp, this[i]))
171 					values.push(this[i]);
172 			return values;
173 		};
174 	}
175 
176 	// ES5 15.4.4.16
177 	if (!Array.prototype.every) {
178 		Array.prototype.every = function every(block /* , thisp */) {
179 			var thisp = arguments[1];
180 			for ( var i = 0; i < this.length; i++)
181 				if (!block.call(thisp, this[i]))
182 					return false;
183 			return true;
184 		};
185 	}
186 
187 	// ES5 15.4.4.17
188 	if (!Array.prototype.some) {
189 		Array.prototype.some = function some(block /* , thisp */) {
190 			var thisp = arguments[1];
191 			for ( var i = 0; i < this.length; i++)
192 				if (block.call(thisp, this[i]))
193 					return true;
194 			return false;
195 		};
196 	}
197 
198 	// ES5 15.4.4.21
199 	// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
200 	if (!Array.prototype.reduce) {
201 		Array.prototype.reduce = function reduce(fun /* , initial */) {
202 			var len = +this.length;
203 			if (typeof fun !== "function")
204 				throw new TypeError();
205 
206 			// no value to return if no initial value and an empty array
207 			if (len === 0 && arguments.length === 1)
208 				throw new TypeError();
209 
210 			var i = 0;
211 			if (arguments.length >= 2) {
212 				var rv = arguments[1];
213 			} else {
214 				do {
215 					if (i in this) {
216 						rv = this[i++];
217 						break;
218 					}
219 
220 					// if array contains no values, no initial value to return
221 					if (++i >= len)
222 						throw new TypeError();
223 				} while (true);
224 			}
225 
226 			for (; i < len; i++) {
227 				if (i in this)
228 					rv = fun.call(null, rv, this[i], i, this);
229 			}
230 
231 			return rv;
232 		};
233 	}
234 
235 	// ES5 15.4.4.22
236 	// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
237 	if (!Array.prototype.reduceRight) {
238 		Array.prototype.reduceRight = function reduceRight(fun /* , initial */) {
239 			var len = +this.length;
240 			if (typeof fun !== "function")
241 				throw new TypeError();
242 
243 			// no value to return if no initial value, empty array
244 			if (len === 0 && arguments.length === 1)
245 				throw new TypeError();
246 
247 			var rv, i = len - 1;
248 			if (arguments.length >= 2) {
249 				rv = arguments[1];
250 			} else {
251 				do {
252 					if (i in this) {
253 						rv = this[i--];
254 						break;
255 					}
256 
257 					// if array contains no values, no initial value to return
258 					if (--i < 0)
259 						throw new TypeError();
260 				} while (true);
261 			}
262 
263 			for (; i >= 0; i--) {
264 				if (i in this)
265 					rv = fun.call(null, rv, this[i], i, this);
266 			}
267 
268 			return rv;
269 		};
270 	}
271 
272 	// ES5 15.4.4.14
273 	if (!Array.prototype.indexOf) {
274 		Array.prototype.indexOf = function indexOf(value /* , fromIndex */) {
275 			var length = this.length;
276 			if (!length)
277 				return -1;
278 			var i = arguments[1] || 0;
279 			if (i >= length)
280 				return -1;
281 			if (i < 0)
282 				i += length;
283 			for (; i < length; i++) {
284 				if (!(i in this))
285 					continue;
286 				if (value === this[i])
287 					return i;
288 			}
289 			return -1;
290 		};
291 	}
292 
293 	// ES5 15.4.4.15
294 	if (!Array.prototype.lastIndexOf) {
295 		Array.prototype.lastIndexOf = function lastIndexOf(value) {
296 			var length = this.length;
297 			if (!length)
298 				return -1;
299 			var i = arguments[1] || length;
300 			if (i < 0)
301 				i += length;
302 			i = Math.min(i, length - 1);
303 			for (; i >= 0; i--) {
304 				if (!(i in this))
305 					continue;
306 				if (value === this[i])
307 					return i;
308 			}
309 			return -1;
310 		};
311 	}
312 
313 	// ES5 15.9.4.4
314 	if (!Date.now) {
315 		Date.now = function now() {
316 			return new Date().getTime();
317 		};
318 	}
319 }
320 
321 /**
322  * Create shims for HTML5 functionality if not supported by browser.
323  */
324 function createHTML5Shims() {
325 	// localstorage dummy (does nothing)
326 	if (typeof window.localStorage == 'undefined') {
327 		window.localStorage = {
328 			getItem : function() {
329 				return null;
330 			},
331 			setItem : function() {
332 			},
333 			clear : function() {
334 			},
335 			removeItem : function() {
336 			},
337 			length : 0,
338 			key : function() {
339 				return null;
340 			}
341 		};
342 	}
343 }