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 };