1 /** 2 * Emily.js - http://flams.github.com/emily/ 3 * Copyright(c) 2012-2014 Olivier Scherrer <pode.fr@gmail.com> 4 * MIT Licensed 5 */ 6 "use strict"; 7 8 var Observable = require("./Observable"), 9 Store = require("./Store"), 10 Tools = require("./Tools"); 11 12 /** 13 * @class 14 * Routing allows for navigating in an application by defining routes. 15 */ 16 module.exports = function RouterConstructor() { 17 18 /** 19 * The routes observable (the applications use it) 20 * @private 21 */ 22 var _routes = new Observable(), 23 24 /** 25 * The events observable (used by Routing) 26 * @private 27 */ 28 _events = new Observable(), 29 30 /** 31 * The routing history 32 * @private 33 */ 34 _history = new Store([]), 35 36 /** 37 * For navigating through the history, remembers the current position 38 * @private 39 */ 40 _currentPos = -1, 41 42 /** 43 * The depth of the history 44 * @private 45 */ 46 _maxHistory = 10; 47 48 /** 49 * Only for debugging 50 * @private 51 */ 52 this.getRoutesObservable = function getRoutesObservable() { 53 return _routes; 54 }; 55 56 /** 57 * Only for debugging 58 * @private 59 */ 60 this.getEventsObservable = function getEventsObservable() { 61 return _events; 62 }; 63 64 /** 65 * Set the maximum length of history 66 * As the user navigates through the application, the 67 * routeur keeps track of the history. Set the depth of the history 68 * depending on your need and the amount of memory that you can allocate it 69 * @param {Number} maxHistory the depth of history 70 * @returns {Boolean} true if maxHistory is equal or greater than 0 71 */ 72 this.setMaxHistory = function setMaxHistory(maxHistory) { 73 if (maxHistory >= 0) { 74 _maxHistory = maxHistory; 75 return true; 76 } else { 77 return false; 78 } 79 80 }; 81 82 /** 83 * Get the current max history setting 84 * @returns {Number} the depth of history 85 */ 86 this.getMaxHistory = function getMaxHistory() { 87 return _maxHistory; 88 }; 89 90 /** 91 * Set a new route 92 * @param {String} route the name of the route 93 * @param {Function} func the function to be execute when navigating to the route 94 * @param {Object} scope the scope in which to execute the function 95 * @returns a handle to remove the route 96 */ 97 this.set = function set() { 98 return _routes.watch.apply(_routes, arguments); 99 }; 100 101 /** 102 * Remove a route 103 * @param {Object} handle the handle provided by the set method 104 * @returns true if successfully removed 105 */ 106 this.unset = function unset(handle) { 107 return _routes.unwatch(handle); 108 }; 109 110 /** 111 * Navigate to a route 112 * @param {String} route the route to navigate to 113 * @param {*} *params 114 * @returns 115 */ 116 this.navigate = function get(route, params) { 117 if (this.load.apply(this, arguments)) { 118 // Before adding a new route to the history, we must clear the forward history 119 _history.proxy("splice", _currentPos +1, _history.count()); 120 _history.proxy("push", Tools.toArray(arguments)); 121 this.ensureMaxHistory(_history); 122 _currentPos = _history.count() -1; 123 return true; 124 } else { 125 return false; 126 } 127 128 }; 129 130 /** 131 * Ensure that history doesn't grow bigger than the max history setting 132 * @param {Store} history the history store 133 * @private 134 */ 135 this.ensureMaxHistory = function ensureMaxHistory(history) { 136 var count = history.count(), 137 max = this.getMaxHistory(), 138 excess = count - max; 139 140 if (excess > 0) { 141 history.proxy("splice", 0, excess); 142 } 143 }; 144 145 /** 146 * Actually loads the route 147 * @private 148 */ 149 this.load = function load() { 150 var copy = Tools.toArray(arguments); 151 152 if (_routes.notify.apply(_routes, copy)) { 153 copy.unshift("route"); 154 _events.notify.apply(_events, copy); 155 return true; 156 } else { 157 return false; 158 } 159 }; 160 161 /** 162 * Watch for route changes 163 * @param {Function} func the func to execute when the route changes 164 * @param {Object} scope the scope in which to execute the function 165 * @returns {Object} the handle to unwatch for route changes 166 */ 167 this.watch = function watch(func, scope) { 168 return _events.watch("route", func, scope); 169 }; 170 171 /** 172 * Unwatch routes changes 173 * @param {Object} handle the handle was returned by the watch function 174 * @returns true if unwatch 175 */ 176 this.unwatch = function unwatch(handle) { 177 return _events.unwatch(handle); 178 }; 179 180 /** 181 * Get the history store, for debugging only 182 * @private 183 */ 184 this.getHistoryStore = function getHistoryStore() { 185 return _history; 186 }; 187 188 /** 189 * Get the current length of history 190 * @returns {Number} the length of history 191 */ 192 this.getHistoryCount = function getHistoryCount() { 193 return _history.count(); 194 }; 195 196 /** 197 * Flush the entire history 198 */ 199 this.clearHistory = function clearHistory() { 200 _history.reset([]); 201 }; 202 203 /** 204 * Go back and forth in the history 205 * @param {Number} nb the amount of history to rewind/forward 206 * @returns true if history exists 207 */ 208 this.go = function go(nb) { 209 var history = _history.get(_currentPos + nb); 210 if (history) { 211 _currentPos += nb; 212 this.load.apply(this, history); 213 return true; 214 } else { 215 return false; 216 } 217 }; 218 219 /** 220 * Go back in the history, short for go(-1) 221 * @returns 222 */ 223 this.back = function back() { 224 return this.go(-1); 225 }; 226 227 /** 228 * Go forward in the history, short for go(1) 229 * @returns 230 */ 231 this.forward = function forward() { 232 return this.go(1); 233 }; 234 235 };