ember-routemanager 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ember-routemanager.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Hedtek Ltd. and Gordon L. Hempton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Ember::Routemanager
2
+
3
+ Ember routemanager packaged for the rails asset pipeline.
4
+
5
+ Original routemanager package is here: https://github.com/ghempton/ember-routemanager
6
+
7
+ Commit d6bf04c289f34caeb63f70a0666b241f1638776e
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'ember-routemanager'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install ember-routemanager
22
+
23
+ ## Usage
24
+
25
+ Add the gem to your application and then add the line
26
+ //= require ember-routemanager
27
+ to your applicaton's javascript manifest
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ember-routemanager/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David Workman", "Hedtek Ltd."]
6
+ gem.email = ["gems@hedtek.com"]
7
+ gem.description = %q{Ember routemanager for rails asset pipeline}
8
+ gem.summary = %q{Ember routemanager for rails asset pipeline}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "ember-routemanager"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Ember::Routemanager::VERSION
17
+ end
@@ -0,0 +1,5 @@
1
+ module Ember
2
+ module Routemanager
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ require "ember-routemanager/version"
2
+
3
+ module EmberRoutemanager
4
+ class Engine < Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,553 @@
1
+
2
+ (function(exports) {
3
+ var get = Ember.get, set = Ember.set;
4
+
5
+ /**
6
+ Whether the browser supports HTML5 history.
7
+ */
8
+ var supportsHistory = !!(window.history && window.history.pushState);
9
+
10
+ /**
11
+ Whether the browser supports the hashchange event.
12
+ */
13
+ var supportsHashChange = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7);
14
+
15
+ /**
16
+ @class
17
+ Ember.RouteManager manages the browser location and changes states accordingly
18
+ to the current location. The location can be programmatically set as follows:
19
+
20
+ routeManager.set('location', 'notes/edit/4');
21
+
22
+ Ember.RouteManager also supports HTML5 history, which uses a '/' instead of a
23
+ '#' in the URLs, so that all your website's URLs are consistent.
24
+ */
25
+ Ember.RouteManager = Ember.StateManager.extend({
26
+
27
+ /**
28
+ Set this property to true if you want to use HTML5 history, if available on
29
+ the browser, instead of the location hash.
30
+
31
+ HTML 5 history uses the history.pushState method and the window's popstate
32
+ event.
33
+
34
+ By default it is false, so your URLs will look like:
35
+
36
+ http://domain.tld/my_app#notes/edit/4
37
+
38
+ If set to true and the browser supports pushState(), your URLs will look
39
+ like:
40
+
41
+ http://domain.tld/my_app/notes/edit/4
42
+
43
+ You will also need to make sure that baseURI is properly configured, as
44
+ well as your server so that your routes are properly pointing to your
45
+ SproutCore application.
46
+
47
+ @see http://dev.w3.org/html5/spec/history.html#the-history-interface
48
+ @property
49
+ @type {Boolean}
50
+ */
51
+ wantsHistory: false,
52
+
53
+ /**
54
+ A read-only boolean indicating whether or not HTML5 history is used. Based
55
+ on the value of wantsHistory and the browser's support for pushState.
56
+
57
+ @see wantsHistory
58
+ @property
59
+ @type {Boolean}
60
+ */
61
+ usesHistory: null,
62
+
63
+ /**
64
+ The base URI used to resolve routes (which are relative URLs). Only used
65
+ when usesHistory is equal to true.
66
+
67
+ The build tools automatically configure this value if you have the
68
+ html5_history option activated in the Buildfile:
69
+
70
+ config :my_app, :html5_history => true
71
+
72
+ Alternatively, it uses by default the value of the href attribute of the
73
+ <base> tag of the HTML document. For example:
74
+
75
+ <base href="http://domain.tld/my_app">
76
+
77
+ The value can also be customized before or during the exectution of the
78
+ main() method.
79
+
80
+ @see http://www.w3.org/TR/html5/semantics.html#the-base-element
81
+ @property
82
+ @type {String}
83
+ */
84
+ baseURI: document.baseURI,
85
+
86
+ /** @private
87
+ A boolean value indicating whether or not the ping method has been called
88
+ to setup the Ember.routes.
89
+
90
+ @property
91
+ @type {Boolean}
92
+ */
93
+ _didSetup: false,
94
+
95
+ /** @private
96
+ Internal representation of the current location hash.
97
+
98
+ @property
99
+ @type {String}
100
+ */
101
+ _location: null,
102
+
103
+ /** @private
104
+ Internal method used to extract and merge the parameters of a URL.
105
+
106
+ @returns {Hash}
107
+ */
108
+ _extractParametersAndRoute: function(obj) {
109
+ var params = {}, route = obj.route || '', separator, parts, i, len, crumbs, key;
110
+ separator = (route.indexOf('?') < 0 && route.indexOf('&') >= 0) ? '&' : '?';
111
+ parts = route.split(separator);
112
+ route = parts[0];
113
+ if(parts.length === 1) {
114
+ parts = [];
115
+ } else if(parts.length === 2) {
116
+ parts = parts[1].split('&');
117
+ } else if(parts.length > 2) {
118
+ parts.shift();
119
+ }
120
+
121
+ // extract the parameters from the route string
122
+ len = parts.length;
123
+ for( i = 0; i < len; ++i) {
124
+ crumbs = parts[i].split('=');
125
+ params[crumbs[0]] = crumbs[1];
126
+ }
127
+
128
+ // overlay any parameter passed in obj
129
+ for(key in obj) {
130
+ if(obj.hasOwnProperty(key) && key !== 'route') {
131
+ params[key] = '' + obj[key];
132
+ }
133
+ }
134
+
135
+ // build the route
136
+ parts = [];
137
+ for(key in params) {
138
+ parts.push([key, params[key]].join('='));
139
+ }
140
+ params.params = separator + parts.join('&');
141
+ params.route = route;
142
+
143
+ return params;
144
+ },
145
+
146
+ /**
147
+ The current location hash. It is the part in the browser's location after
148
+ the '#' mark.
149
+
150
+ @property
151
+ @type {String}
152
+ */
153
+ location: Ember.computed(function(key, value) {
154
+ this._skipRoute = false;
155
+ return this._extractLocation(key, value);
156
+ }).property(),
157
+
158
+ _extractLocation: function(key, value) {
159
+ var crumbs, encodedValue;
160
+
161
+ if(value !== undefined) {
162
+ if(value === null) {
163
+ value = '';
164
+ }
165
+
166
+ if( typeof (value) === 'object') {
167
+ crumbs = this._extractParametersAndRoute(value);
168
+ value = crumbs.route + crumbs.params;
169
+ }
170
+
171
+ if(!this._skipPush && (!Ember.empty(value) || (this._location && this._location !== value))) {
172
+ encodedValue = encodeURI(value);
173
+
174
+ if(this.usesHistory) {
175
+ if(encodedValue.length > 0) {
176
+ encodedValue = '/' + encodedValue;
177
+ }
178
+ window.history.pushState(null, null, get(this, 'baseURI') + encodedValue);
179
+ } else if(encodedValue.length > 0 || window.location.hash.length > 0) {
180
+ window.location.hash = encodedValue;
181
+ }
182
+ }
183
+
184
+ this._location = value;
185
+ }
186
+
187
+ return this._location;
188
+ },
189
+
190
+ updateLocation: function(loc) {
191
+ this._skipRoute = true;
192
+ return this._extractLocation('location', loc);
193
+ },
194
+
195
+ /**
196
+ You usually don't need to call this method. It is done automatically after
197
+ the application has been initialized.
198
+
199
+ It registers for the hashchange event if available. If not, it creates a
200
+ timer that looks for location changes every 150ms.
201
+ */
202
+ ping: function() {
203
+ if(!this._didSetup) {
204
+ this._didSetup = true;
205
+ var state;
206
+
207
+ if(get(this, 'wantsHistory') && supportsHistory) {
208
+ this.usesHistory = true;
209
+
210
+ // Move any hash state to url state
211
+ // TODO: Make sure we have a hash before adding slash
212
+ state = window.location.hash.slice(1);
213
+ if(state.length > 0) {
214
+ state = '/' + state;
215
+ window.history.replaceState(null, null, get(this, 'baseURI') + state);
216
+ }
217
+
218
+ this.popState();
219
+ this.popState = jQuery.proxy(this.popState, this);
220
+ jQuery(window).bind('popstate', this.popState);
221
+
222
+ } else {
223
+ this.usesHistory = false;
224
+
225
+ if(get(this, 'wantsHistory')) {
226
+ // Move any url state to hash
227
+ var base = get(this, 'baseURI');
228
+ var loc = (base.charAt(0) === '/') ? document.location.pathname : document.location.href.replace(document.location.hash, '');
229
+ state = loc.slice(base.length + 1);
230
+ if(state.length > 0) {
231
+ window.location.href = base + '#' + state;
232
+ }
233
+ }
234
+
235
+ if(supportsHashChange) {
236
+ this.hashChange();
237
+ this.hashChange = jQuery.proxy(this.hashChange, this);
238
+ jQuery(window).bind('hashchange', this.hashChange);
239
+
240
+ } else {
241
+ // we don't use a Ember.Timer because we don't want
242
+ // a run loop to be triggered at each ping
243
+ var invokeHashChange = function() {
244
+ this.hashChange();
245
+ this._timerId = setTimeout(invokeHashChange, 100);
246
+ };
247
+
248
+ invokeHashChange();
249
+ }
250
+ }
251
+ }
252
+ },
253
+
254
+ destroy: function() {
255
+ if(this._didSetup) {
256
+ if(get(this, 'wantsHistory') && supportsHistory) {
257
+ jQuery(window).unbind('popstate', this.popState);
258
+ } else {
259
+ if(supportsHashChange) {
260
+ jQuery(window).unbind('hashchange', this.hashChange);
261
+ } else {
262
+ clearTimeout(this._timerId);
263
+ }
264
+ }
265
+ }
266
+ this._super();
267
+ },
268
+
269
+ /**
270
+ Ember.RouteManager currently automatically starts listening
271
+ for browser location changes when created.
272
+ */
273
+ init: function() {
274
+ this._super();
275
+ if(!this._didSetup) {
276
+ this.ping();
277
+ }
278
+ },
279
+
280
+ /**
281
+ Observer of the 'location' property that calls the correct route handler
282
+ when the location changes.
283
+ */
284
+ locationDidChange: Ember.observer(function() {
285
+ this.trigger();
286
+ }, 'location'),
287
+
288
+ /**
289
+ Triggers a route even if already in that route (does change the location, if
290
+ it is not already changed, as well).
291
+
292
+ If the location is not the same as the supplied location, this simply lets
293
+ "location" handle it (which ends up coming back to here).
294
+ */
295
+ trigger: function() {
296
+ var location = get(this, 'location'), params, route;
297
+ params = this._extractParametersAndRoute({
298
+ route: location
299
+ });
300
+ location = params.route;
301
+ delete params.route;
302
+ delete params.params;
303
+
304
+ var result = this.getState(location, params);
305
+ if(result) {
306
+ set(this, 'params', result.params);
307
+
308
+ // We switch states in two phases. The point of this is to handle
309
+ // parameter-only location changes. This will correspond to the same
310
+ // state path in the manager, but states with parts with changed
311
+ // parameters should be re-entered:
312
+
313
+ // 1. We go to the earliest clean state. This prevents
314
+ // unnecessary transitions.
315
+ if(result.cleanStates.length > 0) {
316
+ var cleanState = result.cleanStates.join('.');
317
+ this.goToState(cleanState);
318
+ }
319
+ // 2. We transition to the dirty state. This forces dirty
320
+ // states to be transitioned.
321
+ if(result.dirtyStates.length > 0) {
322
+ var dirtyState = result.cleanStates.concat(result.dirtyStates).join('.');
323
+ this.goToState(dirtyState);
324
+ }
325
+ } else {
326
+ var states = get(this, 'states');
327
+ if(states && get(states, "404")) {
328
+ this.goToState("404");
329
+ }
330
+ }
331
+ },
332
+
333
+ getState: function(route, params) {
334
+ var parts = route.split('/');
335
+ parts = parts.filter(function(part) {
336
+ return part !== '';
337
+ });
338
+
339
+ return this._findState(parts, this, [], [], params, false);
340
+ },
341
+
342
+ /** @private
343
+ Recursive helper that the state and the params if a match is found
344
+ */
345
+ _findState: function(parts, state, cleanStates, dirtyStates, params) {
346
+ parts = Ember.copy(parts);
347
+
348
+ var hasChildren = false, name, states, childState;
349
+ // sort desc based on priority
350
+ states = [];
351
+ for(name in state.states) {
352
+ // 404 state is special and not matched
353
+ childState = state.states[name];
354
+ if(name == "404" || !Ember.State.detect(childState) && !( childState instanceof Ember.State)) {
355
+ continue;
356
+ }
357
+ states.push({
358
+ name: name,
359
+ state: childState
360
+ });
361
+ }
362
+ states = states.sort(function(a, b) {
363
+ return (b.state.get('priority') || 0) - (a.state.get('priority') || 0);
364
+ });
365
+
366
+ for(var i = 0; i < states.length; i++) {
367
+ name = states[i].name;
368
+ childState = states[i].state;
369
+ if(!( childState instanceof Ember.State)) {
370
+ continue;
371
+ }
372
+ hasChildren = true;
373
+
374
+ var result = this._matchState(parts, childState, params);
375
+ if(!result) {
376
+ continue;
377
+ }
378
+
379
+ var newParams = Ember.copy(params);
380
+ jQuery.extend(newParams, result.params);
381
+
382
+ var dirty = dirtyStates.length > 0 || result.dirty;
383
+ var newCleanStates = cleanStates;
384
+ var newDirtyStates = dirtyStates;
385
+ if(dirty) {
386
+ newDirtyStates = Ember.copy(newDirtyStates);
387
+ newDirtyStates.push(name);
388
+ } else {
389
+ newCleanStates = Ember.copy(newCleanStates);
390
+ newCleanStates.push(name);
391
+ }
392
+ result = this._findState(result.parts, childState, newCleanStates, newDirtyStates, newParams);
393
+ if(result) {
394
+ return result;
395
+ }
396
+ }
397
+
398
+ if(!hasChildren && parts.length === 0) {
399
+ return {
400
+ state: state,
401
+ params: params,
402
+ cleanStates: cleanStates,
403
+ dirtyStates: dirtyStates
404
+ };
405
+ }
406
+ return null;
407
+ },
408
+
409
+ /** @private
410
+ Check if a state accepts the parts with the params
411
+
412
+ Returns the remaining parts as well as merged params if
413
+ the state accepts.
414
+
415
+ Will also set the dirty flag if the route is the same but
416
+ the parameters have changed
417
+ */
418
+ _matchState: function(parts, state, params) {
419
+ parts = Ember.copy(parts);
420
+ params = Ember.copy(params);
421
+ var dirty = false;
422
+ var route = get(state, 'route');
423
+ if(route) {
424
+ var partDefinitions;
425
+ // route could be either a string or regex
426
+ if( typeof route == "string") {
427
+ partDefinitions = route.split('/');
428
+ } else if( route instanceof RegExp) {
429
+ partDefinitions = [route];
430
+ } else {
431
+ ember_assert("route must be either a string or regexp", false);
432
+ }
433
+
434
+ for(var i = 0; i < partDefinitions.length; i++) {
435
+ if(parts.length === 0) {
436
+ return false;
437
+ }
438
+ var part = parts.shift();
439
+ var partDefinition = partDefinitions[i];
440
+ var partParams = this._matchPart(partDefinition, part);
441
+ if(!partParams) {
442
+ return false;
443
+ }
444
+
445
+ var oldParams = this.get('params') || {};
446
+ for(var param in partParams) {
447
+ dirty = dirty || (oldParams[param] != partParams[param]);
448
+ }
449
+
450
+ jQuery.extend(params, partParams);
451
+ }
452
+ }
453
+
454
+ if(Ember.typeOf(state.willAccept) == 'function') {
455
+ if(!state.willAccept(params)) {
456
+ return false;
457
+ }
458
+ }
459
+
460
+ return {
461
+ parts: parts,
462
+ params: params,
463
+ dirty: dirty
464
+ };
465
+ },
466
+
467
+ /** @private
468
+ Returns params if the part matches the partDefinition
469
+ */
470
+ _matchPart: function(partDefinition, part) {
471
+ // Handle string parts
472
+ if( typeof partDefinition == "string") {
473
+
474
+ switch (partDefinition.slice(0, 1)) {
475
+ // 1. dynamic routes
476
+ case ':':
477
+ var name = partDefinition.slice(1, partDefinition.length);
478
+ var params = {};
479
+ params[name] = part;
480
+ return params;
481
+
482
+ // 2. wildcard routes
483
+ case '*':
484
+ return {};
485
+
486
+ // 3. static routes
487
+ default:
488
+ if(partDefinition == part)
489
+ return {};
490
+ break;
491
+ }
492
+
493
+ return false;
494
+ }
495
+
496
+ // Handle RegExp parts
497
+ return partDefinition.test(part) ? {} : false;
498
+ },
499
+
500
+ /**
501
+ Event handler for the hashchange event. Called automatically by the browser
502
+ if it supports the hashchange event, or by our timer if not.
503
+ */
504
+ hashChange: function(event) {
505
+ var loc = window.location.hash;
506
+ var routes = this;
507
+
508
+ // Remove the '#' prefix
509
+ loc = (loc && loc.length > 0) ? loc.slice(1, loc.length) : '';
510
+
511
+ if(!jQuery.browser.mozilla) {
512
+ // because of bug https://bugzilla.mozilla.org/show_bug.cgi?id=483304
513
+ loc = decodeURI(loc);
514
+ }
515
+
516
+ if(get(routes, 'location') !== loc && !routes._skipRoute) {
517
+ Ember.run.once(function() {
518
+ routes._skipPush = true;
519
+ set(routes, 'location', loc);
520
+ routes._skipPush = false;
521
+ });
522
+
523
+ }
524
+ routes._skipRoute = false;
525
+ },
526
+
527
+ popState: function(event) {
528
+ var routes = this;
529
+ var base = get(routes, 'baseURI'), loc = (base.charAt(0) === '/') ? document.location.pathname : document.location.href;
530
+
531
+ if(loc.slice(0, base.length) === base) {
532
+ // Remove the base prefix and the extra '/'
533
+ loc = loc.slice(base.length + 1, loc.length);
534
+
535
+ if(get(routes, 'location') !== loc && !routes._skipRoute) {
536
+ Ember.run.once(function() {
537
+ routes._skipPush = true;
538
+ set(routes, 'location', loc);
539
+ routes._skipPush = false;
540
+ });
541
+
542
+ }
543
+ }
544
+ routes._skipRoute = false;
545
+ }
546
+
547
+ });
548
+
549
+ })({});
550
+
551
+
552
+ (function(exports) {
553
+ })({});
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ember-routemanager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Workman
9
+ - Hedtek Ltd.
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-03-26 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Ember routemanager for rails asset pipeline
16
+ email:
17
+ - gems@hedtek.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - LICENSE
25
+ - README.md
26
+ - Rakefile
27
+ - ember-routemanager.gemspec
28
+ - lib/ember-routemanager.rb
29
+ - lib/ember-routemanager/version.rb
30
+ - vendor/assets/javascripts/ember-routemanager.js
31
+ homepage: ''
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.8.16
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Ember routemanager for rails asset pipeline
55
+ test_files: []