mirror of
https://github.com/jashkenas/backbone.git
synced 2026-01-24 14:27:56 -05:00
Merge remote branch 'remotes/upstream/master'
This commit is contained in:
56
backbone.js
56
backbone.js
@@ -680,8 +680,8 @@
|
||||
route : function(route, name, callback) {
|
||||
Backbone.history || (Backbone.history = new Backbone.History);
|
||||
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
|
||||
Backbone.history.route(route, _.bind(function(fragment) {
|
||||
var args = this._extractParameters(route, fragment);
|
||||
Backbone.history.route(route, _.bind(function(hash) {
|
||||
var args = this._extractParameters(route, hash);
|
||||
callback.apply(this, args);
|
||||
this.trigger.apply(this, ['route:' + name].concat(args));
|
||||
}, this));
|
||||
@@ -689,8 +689,8 @@
|
||||
|
||||
// Simple proxy to `Backbone.history` to save a fragment into the history,
|
||||
// without triggering routes.
|
||||
saveLocation : function(fragment) {
|
||||
Backbone.history.saveLocation(fragment);
|
||||
saveLocation : function(hash) {
|
||||
Backbone.history.saveLocation(hash);
|
||||
},
|
||||
|
||||
// Bind all defined routes to `Backbone.history`. We have to reverse the
|
||||
@@ -708,7 +708,7 @@
|
||||
},
|
||||
|
||||
// Convert a route string into a regular expression, suitable for matching
|
||||
// against the current location fragment.
|
||||
// against the current location hash.
|
||||
_routeToRegExp : function(route) {
|
||||
route = route.replace(escapeRegExp, "\\$&")
|
||||
.replace(namedParam, "([^\/]*)")
|
||||
@@ -718,8 +718,8 @@
|
||||
|
||||
// Given a route, and a URL fragment that it matches, return the array of
|
||||
// extracted parameters.
|
||||
_extractParameters : function(route, fragment) {
|
||||
return route.exec(fragment).slice(1);
|
||||
_extractParameters : function(route, hash) {
|
||||
return route.exec(hash).slice(1);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -731,7 +731,6 @@
|
||||
// browser does not support `onhashchange`, falls back to polling.
|
||||
Backbone.History = function() {
|
||||
this.handlers = [];
|
||||
this.fragment = this.getFragment();
|
||||
_.bindAll(this, 'checkUrl');
|
||||
};
|
||||
|
||||
@@ -752,7 +751,7 @@
|
||||
interval: 50,
|
||||
|
||||
// Get the cross-browser normalized URL fragment.
|
||||
getFragment : function(loc) {
|
||||
getHash : function(loc) {
|
||||
return (loc || window.location).hash.replace(hashStrip, '');
|
||||
},
|
||||
|
||||
@@ -760,16 +759,19 @@
|
||||
// an existing route, and `false` otherwise.
|
||||
start : function() {
|
||||
if (historyStarted) throw new Error("Backbone.history has already been started");
|
||||
var hash = this.getHash();
|
||||
var docMode = document.documentMode;
|
||||
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
|
||||
if (oldIE) {
|
||||
this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
|
||||
this.saveLocation(hash);
|
||||
}
|
||||
if ('onhashchange' in window && !oldIE) {
|
||||
$(window).bind('hashchange', this.checkUrl);
|
||||
} else {
|
||||
setInterval(this.checkUrl, this.interval);
|
||||
}
|
||||
this.hash = hash;
|
||||
historyStarted = true;
|
||||
return this.loadUrl();
|
||||
},
|
||||
@@ -783,15 +785,11 @@
|
||||
// Checks the current URL to see if it has changed, and if it has,
|
||||
// calls `loadUrl`, normalizing across the hidden iframe.
|
||||
checkUrl : function() {
|
||||
var current = this.getFragment();
|
||||
if (current == this.fragment && this.iframe) {
|
||||
current = this.getFragment(this.iframe.location);
|
||||
}
|
||||
if (current == this.fragment ||
|
||||
current == decodeURIComponent(this.fragment)) return false;
|
||||
if (this.iframe) {
|
||||
window.location.hash = this.iframe.location.hash = current;
|
||||
}
|
||||
var hash = this.getHash();
|
||||
if (hash == this.hash && this.iframe) hash = this.getHash(this.iframe.location);
|
||||
if (hash == this.hash || hash == decodeURIComponent(this.hash)) return false;
|
||||
if (this.iframe) this.saveLocation(hash);
|
||||
this.hash = hash;
|
||||
this.loadUrl();
|
||||
},
|
||||
|
||||
@@ -799,10 +797,10 @@
|
||||
// match, returns `true`. If no defined routes matches the fragment,
|
||||
// returns `false`.
|
||||
loadUrl : function() {
|
||||
var fragment = this.fragment = this.getFragment();
|
||||
var hash = this.hash;
|
||||
var matched = _.any(this.handlers, function(handler) {
|
||||
if (handler.route.test(fragment)) {
|
||||
handler.callback(fragment);
|
||||
if (handler.route.test(hash)) {
|
||||
handler.callback(hash);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -812,13 +810,13 @@
|
||||
// Save a fragment into the hash history. You are responsible for properly
|
||||
// URL-encoding the fragment in advance. This does not trigger
|
||||
// a `hashchange` event.
|
||||
saveLocation : function(fragment) {
|
||||
fragment = (fragment || '').replace(hashStrip, '');
|
||||
if (this.fragment == fragment) return;
|
||||
window.location.hash = this.fragment = fragment;
|
||||
if (this.iframe && (fragment != this.getFragment(this.iframe.location))) {
|
||||
saveLocation : function(hash) {
|
||||
hash = (hash || '').replace(hashStrip, '');
|
||||
if (this.hash == hash) return;
|
||||
window.location.hash = this.hash = hash;
|
||||
if (this.iframe && (hash != this.getHash(this.iframe.location))) {
|
||||
this.iframe.document.open().close();
|
||||
this.iframe.location.hash = fragment;
|
||||
this.iframe.location.hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,7 +843,7 @@
|
||||
};
|
||||
|
||||
// Cached regex to split keys for `delegate`.
|
||||
var eventSplitter = /^(\w+)\s*(.*)$/;
|
||||
var eventSplitter = /^(\S+)\s*(.*)$/;
|
||||
|
||||
// List of view options to be merged as properties.
|
||||
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
|
||||
@@ -992,7 +990,6 @@
|
||||
// Default JSON-request options.
|
||||
var params = _.extend({
|
||||
type: type,
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
processData: false
|
||||
}, options);
|
||||
@@ -1004,6 +1001,7 @@
|
||||
|
||||
// Ensure that we have the appropriate request data.
|
||||
if (!params.data && model && (method == 'create' || method == 'update')) {
|
||||
params.contentType = 'application/json';
|
||||
params.data = JSON.stringify(model.toJSON());
|
||||
}
|
||||
|
||||
|
||||
BIN
docs/images/bittorrent.jpg
Normal file
BIN
docs/images/bittorrent.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
BIN
docs/images/cloudapp.png
Normal file
BIN
docs/images/cloudapp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
89
index.html
89
index.html
@@ -280,9 +280,11 @@
|
||||
<li>– <a href="#examples-documentcloud">DocumentCloud</a></li>
|
||||
<li>– <a href="#examples-basecamp">Basecamp Mobile</a></li>
|
||||
<li>– <a href="#examples-flow">Flow</a></li>
|
||||
<li>– <a href="#examples-tilemill">TileMill</a></li>
|
||||
<li>– <a href="#examples-cloudapp">CloudApp</a></li>
|
||||
<li>– <a href="#examples-soundcloud">Mobile SoundCloud</a></li>
|
||||
<li>– <a href="#examples-tilemill">TileMill</a></li>
|
||||
<li>- <a href="#examples-instagreat">Insta-great!</a></li>
|
||||
<li>- <a href="#examples-bittorrent">BitTorrent</a></li>
|
||||
<li>- <a href="#examples-quietwrite">QuietWrite</a></li>
|
||||
<li>- <a href="#examples-tzigla">Tzigla</a></li>
|
||||
<li>- <a href="#examples-substance">Substance</a></li>
|
||||
@@ -1984,21 +1986,21 @@ var model = localBackbone.Model.extend(...);
|
||||
<img src="docs/images/flow.png" alt="Flow" class="example_image" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="examples-tilemill">
|
||||
Our fellow
|
||||
<a href="http://www.newschallenge.org/">Knight Foundation News Challenge</a>
|
||||
winners, <a href="http://mapbox.com/">MapBox</a>, created an open-source
|
||||
map design studio with Backbone.js:
|
||||
<a href="http://mapbox.github.com/tilemill/">TileMill</a>.
|
||||
TileMill lets you manage map layers based on shapefiles and rasters, and
|
||||
edit their appearance directly in the browser with the
|
||||
<a href="https://github.com/mapbox/carto">Carto styling language</a>.
|
||||
|
||||
<p id="examples-cloudapp">
|
||||
<a href="http://getcloudapp.com">CloudApp</a> is simple file and link
|
||||
sharing for the Mac. Backbone.js powers the web tools
|
||||
which consume the <a href="http://developer.getcloudapp.com">documented API</a>
|
||||
to manage Drops. Data is either pulled manually or pushed by
|
||||
<a href="http://pusher.com">Pusher</a> and fed to
|
||||
<a href="http://github.com/janl/mustache.js">Mustache</a> templates for
|
||||
rendering. Check out the <a href="http://cloudapp.github.com/engine">annotated source code</a>
|
||||
to see the magic.
|
||||
</p>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="http://mapbox.github.com/tilemill/">
|
||||
<img src="docs/images/tilemill.png" alt="TileMill" class="example_image" />
|
||||
<a href="http://getcloudapp.com">
|
||||
<img src="docs/images/cloudapp.png" alt="CloudApp" class="example_image" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -2027,6 +2029,23 @@ var model = localBackbone.Model.extend(...);
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="examples-tilemill">
|
||||
Our fellow
|
||||
<a href="http://www.newschallenge.org/">Knight Foundation News Challenge</a>
|
||||
winners, <a href="http://mapbox.com/">MapBox</a>, created an open-source
|
||||
map design studio with Backbone.js:
|
||||
<a href="http://mapbox.github.com/tilemill/">TileMill</a>.
|
||||
TileMill lets you manage map layers based on shapefiles and rasters, and
|
||||
edit their appearance directly in the browser with the
|
||||
<a href="https://github.com/mapbox/carto">Carto styling language</a>.
|
||||
</p>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="http://mapbox.github.com/tilemill/">
|
||||
<img src="docs/images/tilemill.png" alt="TileMill" class="example_image" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="examples-instagreat">
|
||||
<a href="http://twitter.com/elliottkember">Elliott Kember</a> and
|
||||
<a href="http://twitter.com/dizzyup">Hector Simpson</a> built
|
||||
@@ -2043,6 +2062,22 @@ var model = localBackbone.Model.extend(...);
|
||||
<img src="docs/images/instagreat.png" alt="instagre.at" class="example_image" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="examples-bittorrent">
|
||||
<a href="http://www.bittorrent.com">BitTorrent</a> used Backbone to
|
||||
completely rework an existing Win32 UI. Models normalize access to the
|
||||
client's data and views rely heavily on the <tt>change</tt> events to keep
|
||||
the UI state current. Using Backbone and SCSS,
|
||||
<a href="http://www.bittorrent.com/chrysalis/">our new design</a> and UX
|
||||
prototypes are considerably easier to iterate, test and work with than
|
||||
the original Win32 UI.
|
||||
</p>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="http://www.bittorrent.com/chrysalis/">
|
||||
<img src="docs/images/bittorrent.jpg" alt="BitTorrent" class="example_image" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p id="examples-quietwrite">
|
||||
<a href="http://www.twitter.com/jamesjyu">James Yu</a> used Backbone.js to
|
||||
@@ -2182,6 +2217,34 @@ Inbox.messages.fetch();
|
||||
represents a logical chunk of UI, responsible for the contents of a single
|
||||
DOM element.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Comparing the overall structure of Backbone to a server-side MVC framework
|
||||
like <b>Rails</b>, the pieces line up like so:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>Backbone.Model</b> – Like a Rails model minus the class
|
||||
methods. Wraps a row of data in business logic.
|
||||
</li>
|
||||
<li>
|
||||
<b>Backbone.Collection</b> – A group of models on the client-side,
|
||||
with sorting/filtering/aggregation logic.
|
||||
</li>
|
||||
<li>
|
||||
<b>Backbone.Controller</b> – Rails <tt>routes.rb</tt> + Rails controller
|
||||
actions. Maps URLs to functions.
|
||||
</li>
|
||||
<li>
|
||||
<b>Backbone.View</b> – A logical, re-usable piece of UI. Often,
|
||||
but not always, associated with a model.
|
||||
</li>
|
||||
<li>
|
||||
<b>Client-side Templates</b> – Rails <tt>.html.erb</tt> views,
|
||||
rendering a chunk of HTML.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p id="FAQ-this">
|
||||
<b class="header">Binding "this"</b>
|
||||
|
||||
25
test/view.js
25
test/view.js
@@ -16,8 +16,8 @@ $(document).ready(function() {
|
||||
|
||||
test("View: jQuery", function() {
|
||||
view.el = document.body;
|
||||
equals(view.$('#qunit-header a').get(0).innerHTML, ' Backbone Test Suite');
|
||||
equals(view.$('#qunit-header a').get(1).innerHTML, 'Backbone Speed Suite');
|
||||
ok(view.$('#qunit-header a').get(0).innerHTML.match(/Backbone Test Suite/));
|
||||
ok(view.$('#qunit-header a').get(1).innerHTML.match(/Backbone Speed Suite/));
|
||||
});
|
||||
|
||||
test("View: make", function() {
|
||||
@@ -113,4 +113,25 @@ $(document).ready(function() {
|
||||
$("body").trigger("click");
|
||||
equals(5, count);
|
||||
});
|
||||
|
||||
test("View: custom events, with namespaces", function() {
|
||||
var count = 0;
|
||||
var ViewClass = Backbone.View.extend({
|
||||
el: $('body'),
|
||||
events: {
|
||||
"fake$event.namespaced": "run"
|
||||
},
|
||||
run: function() {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
var view = new ViewClass;
|
||||
$('body').trigger('fake$event').trigger('fake$event');
|
||||
equals(count, 2);
|
||||
$('body').unbind('.namespaced');
|
||||
$('body').trigger('fake$event');
|
||||
equals(count, 2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user