├── .gitignore
├── .jshintrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── dist
├── backbone.intercept.js
├── backbone.intercept.min.js
└── backbone.intercept.min.js.map
├── gruntfile.js
├── package.json
├── src
├── backbone.intercept.js
└── wrapper.js
└── test
├── .jshintrc
├── setup
├── helpers.js
└── node.js
├── spec-runner.html
└── unit
├── clicks.js
├── forms.js
├── navigate.js
├── root-element.js
├── start.js
└── stop.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | # Users Environment Variables
28 | .lock-wscript
29 |
30 | bower_components
31 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise" : true,
3 | "camelcase" : true,
4 | "curly" : true,
5 | "eqeqeq" : true,
6 | "es3" : true,
7 | "forin" : false,
8 | "immed" : true,
9 | "indent" : 2,
10 | "latedef" : true,
11 | "newcap" : true,
12 | "noarg" : true,
13 | "noempty" : true,
14 | "nonbsp" : true,
15 | "nonew" : true,
16 | "plusplus" : false,
17 | "quotmark" : "single",
18 | "undef" : true,
19 | "unused" : true,
20 | "strict" : false,
21 | "trailing" : true,
22 | "maxparams" : 4,
23 | "maxdepth" : 2,
24 |
25 | "asi" : false,
26 | "boss" : false,
27 | "debug" : false,
28 | "eqnull" : false,
29 | "esnext" : false,
30 | "evil" : false,
31 | "expr" : true,
32 | "funcscope" : false,
33 | "globalstrict" : false,
34 | "iterator" : false,
35 | "lastsemic" : false,
36 | "laxbreak" : false,
37 | "laxcomma" : false,
38 | "loopfunc" : false,
39 | "maxerr" : 50,
40 | "multistr" : false,
41 | "notypeof" : false,
42 | "proto" : false,
43 | "scripturl" : false,
44 | "smarttabs" : false,
45 | "shadow" : false,
46 | "sub" : false,
47 | "supernew" : false,
48 | "validthis" : false,
49 | "noyield" : false,
50 |
51 | "browser" : true,
52 | "couch" : false,
53 | "devel" : false,
54 | "dojo" : false,
55 | "jquery" : false,
56 | "mootools" : false,
57 | "node" : false,
58 | "nonstandard" : false,
59 | "prototypejs" : false,
60 | "rhino" : false,
61 | "worker" : false,
62 | "wsh" : false,
63 | "yui" : false,
64 | "globals": {
65 | "Backbone": true,
66 | "_": true,
67 | "$": true,
68 | "require": true,
69 | "module": true,
70 | "define": true,
71 | "exports": true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | before_script:
3 | - npm install -g grunt-cli
4 | node_js:
5 | - "0.10"
6 | - "0.12"
7 | sudo: false
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### [0.4.4](https://github.com/jmeas/backbone.intercept/releases/tag/0.4.4)
2 |
3 | - Adds control + click bypass
4 |
5 | ### [0.4.3](https://github.com/jmeas/backbone.intercept/releases/tag/0.4.3)
6 |
7 | - Updated dependencies to support the latest Backbone.
8 |
9 | ### [0.4.2](https://github.com/jmeas/backbone.intercept/releases/tag/0.4.2)
10 |
11 | - Updated Bower to support the latest Backbone, too!
12 |
13 | ### [0.4.1](https://github.com/jmeas/backbone.intercept/releases/tag/0.4.1)
14 |
15 | - Updated dependencies to support the latest Backbone.
16 |
17 | ### [0.4.0](https://github.com/jmeas/backbone.intercept/releases/tag/0.4.0)
18 |
19 | - Updated dependencies to support the latest Backbone and Underscore.
20 |
21 | ### [0.3.3](https://github.com/jmeas/backbone.intercept/releases/tag/v0.3.3)
22 |
23 | - **Bugfix:** Query strings are no longer stripped from intercepted links.
24 |
25 | ### [0.3.2](https://github.com/jmeas/backbone.intercept/releases/tag/v0.3.2)
26 |
27 | - **Bugfix:** Resolves an issue where relative links would be sent to the Router as absolute paths
28 |
29 | ### [0.3.1](https://github.com/jmeas/backbone.intercept/releases/tag/v0.3.1)
30 |
31 | - Corrects Underscore dependency (this library supports every version)
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 James Smith
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > I'm looking for maintainer(s) of this lib. If you're interested, [let me know](https://twitter.com/jmeaspls)! I'd be happy to help you get set up :v:
2 |
3 | # Backbone.Intercept
4 |
5 | [](https://travis-ci.org/jmeas/backbone.intercept)
6 |
7 | Backbone.Intercept intelligently manages link clicks and form submissions within Backbone applications.
8 |
9 | ### About
10 |
11 | The default action of form submissions and link clicks is often undesirable in Backbone applications. One
12 | would usually rather prevent that default behavior, then handle those through Backbone.history and possibly also
13 | in a Router's callback. Backbone doesn't do any of this for you, but Backbone.Intercept does.
14 |
15 | If you're writing `e.preventDefault()` in many of your view's event callbacks – or otherwise handling this problem on a per-view
16 | basis – then Backbone.Intercept might be what you're looking for.
17 |
18 | ```js
19 | // before Backbone.Intercept
20 | var MyView = Backbone.View.extend({
21 | events: {
22 | 'click a': 'onClick',
23 | 'submit form': 'onSubmit'
24 | },
25 |
26 | onClick: function(e) {
27 | e.preventDefault();
28 | myRouter.navigate($(e.currentTarget).attr('href'));
29 | },
30 |
31 | onSubmit: function(e) {
32 | e.preventDefault();
33 | // form submit logic
34 | }
35 | });
36 |
37 | // after Backbone.Intercept
38 | var MyView = Backbone.View.extend({
39 | events: {
40 | 'submit form': 'onSubmit'
41 | },
42 |
43 | onSubmit: function(e) {
44 | // form submit logic
45 | }
46 | });
47 | ```
48 |
49 | ## Installation
50 |
51 | Install through `bower` or `npm`.
52 |
53 | ```js
54 | bower install backbone.intercept
55 | npm install backbone.intercept
56 | ```
57 |
58 | #### Dependencies
59 |
60 | Backbone.Intercept depends on Underscore, Backbone and a jQuery-like API on the `Backbone.$` object.
61 |
62 | ### Table of Contents
63 |
64 | - [Getting Started](#getting-started)
65 | - [Links](#links)
66 | - [Default Behavior](#default-behavior)
67 | - [Navigation](#navigation)
68 | - [Customizing the Behavior Per-Link](#customizing-the-behavior-per-link)
69 | - [Setting Global Link Trigger Behavior](#setting-global-link-trigger-behavior)
70 | - [Forms](#forms)
71 | - [Setting the Root Element of Backbone.Intercept](#setting-the-root-element-of-backboneintercept)
72 | - [When Not to Use Backbone.Intercept](#when-not-to-use-backboneintercept)
73 | - [API](#api)
74 | - [Properties](#properties)
75 | - [`VERSION`](#version)
76 | - [`rootSelector`](#rootselector)
77 | - [`defaults`](#defaults)
78 | - [Methods](#methods)
79 | - [`start()`](#start-options-)
80 | - [`stop()`](#stop)
81 | - [`navigate()`](#navigate-uri-options-)
82 |
83 | ## Getting Started
84 |
85 | Getting started is easy. Simply call `Backbone.Intercept.start()` when your application is started up. If
86 | you're using Marionette, this might look something like
87 |
88 | ```js
89 | app.on('start', Backbone.Intercept.start);
90 | ```
91 |
92 | ### Links
93 |
94 | #### Default Behavior
95 |
96 | In general, links with relative URIs will be intercepted, whereas absolute URIs will be ignored by Backbone.Intercept.
97 | A few examples will best illustrate the default behavior of Intercept.
98 |
99 | ```js
100 | // The following URIs will be intercepted
101 | 'path/to/my-page';
102 | '/absolute/path/to/my-page';
103 | 'www.my-website.com';
104 |
105 | // The following URIs will be ignored by Backbone.Intercept and handled like normal
106 | 'http://www.my-site.com';
107 | '#my-page-fragment';
108 | 'mailto:stacy@email.com';
109 | 'javascript:void';
110 | ```
111 |
112 | #### Navigation
113 |
114 | By default your intercepted links will be sent along to `Backbone.history.navigate` to be processed. You can customize this
115 | by overriding the `navigate` method on Backbone.Intercept. By doing this you could make Intercept work with a Router instead,
116 | or integrate other libraries like [Backbone.Radio](https://github.com/marionettejs/backbone.radio).
117 |
118 | ```js
119 | // Create a Router
120 | var myRouter = new Backbone.Router();
121 |
122 | // Attach it to Intercept
123 | Backbone.Intercept.navigate = function(uri, options) {
124 | myRouter.navigate(uri, options);
125 | };
126 |
127 | // Or use a Backbone.Radio Channel
128 | Backbone.Intercept.navigate = function(uri, options) {
129 | var routerChannel = Backbone.Radio.channel('router');
130 | routerChannel.command('navigate', uri, options);
131 | };
132 | ```
133 |
134 | If you don't want anything to happen when you click links you can specify the `navigate` function as a falsey value,
135 | or an empty function.
136 |
137 | ```js
138 | // This won't cause any navigation to occur when links are clicked
139 | Backbone.Intercept.navigate = undefined;
140 | ```
141 |
142 | #### Customizing the Behavior Per-Link
143 |
144 | This behavior can be changed by setting custom attributes on the element.
145 |
146 | ```html
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | ```
162 |
163 | #### Setting Global Link Trigger Behavior
164 |
165 | You can set the default trigger behavior by specifying it directly on the Backbone.Intercept `defaults` option.
166 |
167 | ```js
168 | // Let's set the trigger setting to false by default
169 | Backbone.Intercept.defaults.trigger = false;
170 | ```
171 |
172 | ### Forms
173 |
174 | Forms are much simpler than links. All forms are intercepted unless the `action` attribute has been specified. And unlike links, there's
175 | no integration of forms with a Router.
176 |
177 | ```html
178 |
179 |
180 |
181 |
182 |
183 | ```
184 |
185 | ### Setting the Root Element of Backbone.Intercept
186 |
187 | Backbone.Intercept will intercept links and forms on the `body` on the page, but this can be customized by setting the
188 | `rootSelector` property.
189 |
190 | ```js
191 | Backbone.Intercept.rootSelector = '.backbone-app';
192 | ```
193 |
194 | This is useful for webapps where Backbone might not be the only library running on the page.
195 |
196 | ### When Not To Use Backbone.Intercept
197 |
198 | Backbone.Intercept works best in an application that is entirely controlled by Backbone. Of course, not
199 | every project is like this. It's not uncommon for there to be Backbone components on a page that is otherwise
200 | not Backbone. In those situations it is likely a better choice to manage link clicks and form
201 | submissions on a per-view basis.
202 |
203 | This library is only meant to act as a proxy between the DOM and your JS, and nothing more, if you want to be able to cancel routes, this library won't help you with that, you'd need to handle that in your router.
204 |
205 | ## API
206 |
207 | ### Properties
208 |
209 | ##### `VERSION`
210 |
211 | The version of Backbone.Intercept.
212 |
213 | ##### `rootSelector`
214 |
215 | A query selector for the root element of Intercept. Defaults to `'body'`.
216 |
217 | ##### `defaults`
218 |
219 | An object for the default values for the router. There are three properties in defaults, `links`, `forms`, and `trigger`,
220 | and all three are `true` out-of-the-box. The first two options determine if Intercept handles links and forms, respectively. The
221 | `trigger` option determines if intercepted links pass `trigger:true` by default.
222 |
223 | The value of the `trigger` or `data-trigger` attribute on the anchor tag itself will always trump the value of the
224 | value of `trigger` in the `defaults` hash.
225 |
226 | ### Methods
227 |
228 | ##### `start( [options] )`
229 |
230 | Starts Backbone.Intercept. You can pass `options` as an argument to override the `defaults` property.
231 |
232 | ```js
233 | // In this app we will only intercept forms
234 | Backbone.Intercept.start({
235 | links: false
236 | });
237 |
238 | // And in this one only links
239 | Backbone.Intercept.start({
240 | forms: false
241 | });
242 | ```
243 |
244 | ##### `stop()`
245 |
246 | Stop intercepting links and forms.
247 |
248 | ##### `navigate( uri, options )`
249 | default: `Backbone.history.navigate`
250 |
251 | A method that's called when links are intercepted. By default it just forwards it along to `Backbone.history.navigate`, but you
252 | can specify a custom method to do whatever you'd like.
253 |
254 | The uri is the value of the link's `href` attribute. `options` are the navigation options, which is just an object
255 | with a `trigger` property.
256 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backbone.intercept",
3 | "main": "dist/backbone.intercept.js",
4 | "version": "0.4.3",
5 | "homepage": "https://github.com/jmeas/backbone.intercept",
6 | "authors": [
7 | "Jmeas "
8 | ],
9 | "description": "Automatically manage link clicks and form submissions within Backbone applications.",
10 | "keywords": [
11 | "backbone",
12 | "marionette",
13 | "link",
14 | "links",
15 | "click",
16 | "router",
17 | "route",
18 | "routes",
19 | "history",
20 | "anchor",
21 | "intercept",
22 | "forms",
23 | "submit",
24 | "interception",
25 | "SPA"
26 | ],
27 | "license": "MIT",
28 | "ignore": [
29 | "**/.*",
30 | "node_modules",
31 | "bower_components",
32 | "test",
33 | "tests"
34 | ],
35 | "dependencies": {
36 | "underscore": ">=1.3.3 <=1.8.3",
37 | "backbone": ">=0.9.9 <=1.3.3"
38 | },
39 | "devDependencies": {
40 | "jquery": ">=1.8 <=3.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/dist/backbone.intercept.js:
--------------------------------------------------------------------------------
1 | // Backbone.Intercept v0.4.4
2 | (function(root, factory) {
3 | if (typeof define === 'function' && define.amd) {
4 | define(['backbone', 'underscore'], function(Backbone, _) {
5 | return factory(Backbone, _);
6 | });
7 | }
8 | else if (typeof exports !== 'undefined') {
9 | var Backbone = require('backbone');
10 | var _ = require('underscore');
11 | module.exports = factory(Backbone, _);
12 | }
13 | else {
14 | factory(root.Backbone, root._);
15 | }
16 | }(this, function(Backbone, _) {
17 | 'use strict';
18 |
19 | Backbone.Intercept = {
20 |
21 | VERSION: '0.4.4',
22 |
23 | rootSelector: 'body',
24 |
25 | defaults: {
26 | trigger : true,
27 | links : true,
28 | forms : true
29 | },
30 |
31 | start: function(options) {
32 | options = _.defaults(options || {}, this.defaults);
33 |
34 | if (options.links) {
35 | this._getRootElement().on('click.backboneIntercept', 'a', _.bind(this._interceptLinks, this));
36 | }
37 | if (options.forms) {
38 | this._getRootElement().on('submit.backboneIntercept', _.bind(this._interceptForms, this));
39 | }
40 | },
41 |
42 | stop: function() {
43 | this._getRootElement().off('.backboneIntercept');
44 | },
45 |
46 | navigate: function(uri, options) {
47 | Backbone.history.navigate(uri, options);
48 | },
49 |
50 | // Creates and caches a jQuery object for the body element
51 | _getRootElement: function() {
52 | if (this._body) { return this._body; }
53 | this._body = Backbone.$(this.rootSelector);
54 | return this._body;
55 | },
56 |
57 | // Prevent the default behavior of submitting the
58 | // form if the action attribute is present and is
59 | // a value
60 | _interceptForms: function(e) {
61 | if (e.target && e.target.action) { return; }
62 | e.preventDefault();
63 | },
64 |
65 | _interceptLinks: function(e) {
66 | // Only intercept left-clicks
67 | if (e.which !== 1) { return; }
68 | var $link = Backbone.$(e.currentTarget);
69 |
70 | // Get the href; stop processing if there isn't one
71 | var href = $link.attr('href');
72 | if (!href) { return; }
73 |
74 | // Determine if we're supposed to bypass the link
75 | // based on its attributes
76 | var bypass = this._getAttr($link, 'bypass');
77 | if (bypass !== undefined && bypass !== 'false') {
78 | return;
79 | }
80 |
81 | // If the Ctrl key is held, we want to open in a new tab.
82 | bypass = e.ctrlKey;
83 | if (bypass) {
84 | return;
85 | }
86 |
87 | // The options we pass along to navigate
88 | var navOptions = {
89 | trigger: this.defaults.trigger
90 | };
91 |
92 | // Determine if it's trigger: false based on the attributes
93 | var trigger = this._getAttr($link, 'trigger');
94 |
95 | if (trigger === 'false') {
96 | navOptions.trigger = false;
97 | } else if (trigger === 'true') {
98 | navOptions.trigger = true;
99 | }
100 |
101 | // Return if the URL is absolute, or if the protocol is mailto or javascript
102 | if (/^#|javascript:|mailto:|(?:\w+:)?\/\//.test(href)) { return; }
103 |
104 | // If we haven't been stopped yet, then we prevent the default action
105 | e.preventDefault();
106 |
107 | // Get the computed pathname of the link, removing
108 | // the leading slash. Regex required for IE8 support
109 | var pathname = $link[0].pathname.replace(/^\//, '') + $link[0].search;
110 |
111 | // Lastly we send off the information to the router
112 | if (!this.navigate) { return; }
113 | this.navigate(pathname, navOptions);
114 | },
115 |
116 | _getAttr: function($el, name) {
117 | var attr = $el.attr(name);
118 | if (attr !== undefined) {
119 | return attr;
120 | }
121 | var data = $el.attr('data-' + name);
122 | if (data !== undefined) {
123 | return data;
124 | }
125 | }
126 | };
127 |
128 |
129 | return Backbone.Intercept;
130 | }));
131 |
--------------------------------------------------------------------------------
/dist/backbone.intercept.min.js:
--------------------------------------------------------------------------------
1 | // Backbone.Intercept v0.4.4
2 |
3 | !function(a,b){if("function"==typeof define&&define.amd)define(["backbone","underscore"],function(a,c){return b(a,c)});else if("undefined"!=typeof exports){var c=require("backbone"),d=require("underscore");module.exports=b(c,d)}else b(a.Backbone,a._)}(this,function(a,b){"use strict";return a.Intercept={VERSION:"0.4.4",rootSelector:"body",defaults:{trigger:!0,links:!0,forms:!0},start:function(a){a=b.defaults(a||{},this.defaults),a.links&&this._getRootElement().on("click.backboneIntercept","a",b.bind(this._interceptLinks,this)),a.forms&&this._getRootElement().on("submit.backboneIntercept",b.bind(this._interceptForms,this))},stop:function(){this._getRootElement().off(".backboneIntercept")},navigate:function(b,c){a.history.navigate(b,c)},_getRootElement:function(){return this._body?this._body:(this._body=a.$(this.rootSelector),this._body)},_interceptForms:function(a){a.target&&a.target.action||a.preventDefault()},_interceptLinks:function(b){if(1===b.which){var c=a.$(b.currentTarget),d=c.attr("href");if(d){var e=this._getAttr(c,"bypass");if((void 0===e||"false"===e)&&!(e=b.ctrlKey)){var f={trigger:this.defaults.trigger},g=this._getAttr(c,"trigger");if("false"===g?f.trigger=!1:"true"===g&&(f.trigger=!0),!/^#|javascript:|mailto:|(?:\w+:)?\/\//.test(d)){b.preventDefault();var h=c[0].pathname.replace(/^\//,"")+c[0].search;this.navigate&&this.navigate(h,f)}}}}},_getAttr:function(a,b){var c=a.attr(b);if(void 0!==c)return c;var d=a.attr("data-"+b);return void 0!==d?d:void 0}},a.Intercept});
4 | //# sourceMappingURL=backbone.intercept.min.js.map
--------------------------------------------------------------------------------
/dist/backbone.intercept.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["backbone.intercept.js"],"names":["root","factory","define","amd","Backbone","_","exports","require","module","this","Intercept","VERSION","rootSelector","defaults","trigger","links","forms","start","options","_getRootElement","on","bind","_interceptLinks","_interceptForms","stop","off","navigate","uri","history","_body","$","e","target","action","preventDefault","which","$link","currentTarget","href","attr","bypass","_getAttr","undefined","ctrlKey","navOptions","test","pathname","replace","search","$el","name","data"],"mappings":";;CACC,SAASA,EAAMC,GACd,GAAsB,kBAAXC,SAAyBA,OAAOC,IACzCD,QAAQ,WAAY,cAAe,SAASE,EAAUC,GACpD,MAAOJ,GAAQG,EAAUC,SAGxB,IAAuB,mBAAZC,SAAyB,CACvC,GAAIF,GAAWG,QAAQ,YACnBF,EAAIE,QAAQ,aAChBC,QAAOF,QAAUL,EAAQG,EAAUC,OAGnCJ,GAAQD,EAAKI,SAAUJ,EAAKK,IAE9BI,KAAM,SAASL,EAAUC,GACzB,YAgHA,OA9GAD,GAASM,WAEPC,QAAS,QAETC,aAAc,OAEdC,UACEC,SAAU,EACVC,OAAU,EACVC,OAAU,GAGZC,MAAO,SAASC,GACdA,EAAUb,EAAEQ,SAASK,MAAeT,KAAKI,UAErCK,EAAQH,OACVN,KAAKU,kBAAkBC,GAAG,0BAA2B,IAAKf,EAAEgB,KAAKZ,KAAKa,gBAAiBb,OAErFS,EAAQF,OACVP,KAAKU,kBAAkBC,GAAG,2BAA4Bf,EAAEgB,KAAKZ,KAAKc,gBAAiBd,QAIvFe,KAAM,WACJf,KAAKU,kBAAkBM,IAAI,uBAG7BC,SAAU,SAASC,EAAKT,GACtBd,EAASwB,QAAQF,SAASC,EAAKT,IAIjCC,gBAAiB,WACf,MAAIV,MAAKoB,MAAgBpB,KAAKoB,OAC9BpB,KAAKoB,MAAQzB,EAAS0B,EAAErB,KAAKG,cACtBH,KAAKoB,QAMdN,gBAAiB,SAASQ,GACpBA,EAAEC,QAAUD,EAAEC,OAAOC,QACzBF,EAAEG,kBAGJZ,gBAAiB,SAASS,GAExB,GAAgB,IAAZA,EAAEI,MAAN,CACA,GAAIC,GAAQhC,EAAS0B,EAAEC,EAAEM,eAGrBC,EAAOF,EAAMG,KAAK,OACtB,IAAKD,EAAL,CAIA,GAAIE,GAAS/B,KAAKgC,SAASL,EAAO,SAClC,SAAeM,KAAXF,GAAmC,UAAXA,MAK5BA,EAAST,EAAEY,SACX,CAKA,GAAIC,IACF9B,QAASL,KAAKI,SAASC,SAIrBA,EAAUL,KAAKgC,SAASL,EAAO,UASnC,IAPgB,UAAZtB,EACF8B,EAAW9B,SAAU,EACA,SAAZA,IACT8B,EAAW9B,SAAU,IAInB,uCAAuC+B,KAAKP,GAAhD,CAGAP,EAAEG,gBAIF,IAAIY,GAAWV,EAAM,GAAGU,SAASC,QAAQ,MAAO,IAAMX,EAAM,GAAGY,MAG1DvC,MAAKiB,UACVjB,KAAKiB,SAASoB,EAAUF,QAG1BH,SAAU,SAASQ,EAAKC,GACtB,GAAIX,GAAOU,EAAIV,KAAKW,EACpB,QAAaR,KAATH,EACF,MAAOA,EAET,IAAIY,GAAOF,EAAIV,KAAK,QAAUW,EAC9B,YAAaR,KAATS,EACKA,MADT,KAOG/C,EAASM","file":"backbone.intercept.min.js"}
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | require('load-grunt-tasks')(grunt);
4 |
5 | // Project configuration.
6 | grunt.initConfig({
7 | pkg: grunt.file.readJSON('package.json'),
8 | meta: {
9 | version: '<%= pkg.version %>',
10 | banner: '// Backbone.Intercept v<%= meta.version %>\n'
11 | },
12 |
13 | preprocess: {
14 | intercept: {
15 | src: 'src/wrapper.js',
16 | dest: 'dist/backbone.intercept.js'
17 | }
18 | },
19 |
20 | template: {
21 | options: {
22 | data: {
23 | version: '<%= meta.version %>'
24 | }
25 | },
26 | intercept: {
27 | src: '<%= preprocess.intercept.dest %>',
28 | dest: '<%= preprocess.intercept.dest %>'
29 | }
30 | },
31 |
32 | concat: {
33 | options: {
34 | banner: '<%= meta.banner %>'
35 | },
36 | intercept: {
37 | src: '<%= preprocess.intercept.dest %>',
38 | dest: '<%= preprocess.intercept.dest %>'
39 | }
40 | },
41 |
42 | uglify: {
43 | options: {
44 | banner: '<%= meta.banner %>'
45 | },
46 | intercept: {
47 | src: '<%= preprocess.intercept.dest %>',
48 | dest: 'dist/backbone.intercept.min.js',
49 | options: {
50 | sourceMap: true
51 | }
52 | }
53 | },
54 |
55 | jshint: {
56 | intercept: {
57 | options: {
58 | jshintrc: '.jshintrc'
59 | },
60 | src: ['src/backbone.intercept.js']
61 | },
62 | tests: {
63 | options: {
64 | jshintrc: 'test/.jshintrc'
65 | },
66 | src: ['test/unit/*.js']
67 | }
68 | },
69 |
70 | mochaTest: {
71 | spec: {
72 | options: {
73 | require: 'test/setup/node.js',
74 | reporter: 'dot',
75 | clearRequireCache: true,
76 | mocha: require('mocha')
77 | },
78 | src: [
79 | 'test/setup/helpers.js',
80 | 'test/unit/*.js'
81 | ]
82 | }
83 | },
84 |
85 | connect: {
86 | options: {
87 | port: 8000,
88 | keepalive: true
89 | },
90 | browser: {}
91 | }
92 | });
93 |
94 | grunt.registerTask('test:browser', 'Test the library in the browser', [
95 | 'jshint',
96 | 'connect'
97 | ]);
98 |
99 | grunt.registerTask('test', 'Test the library', [
100 | 'jshint',
101 | 'mochaTest'
102 | ]);
103 |
104 | grunt.registerTask('build', 'Build the library', [
105 | 'test',
106 | 'preprocess:intercept',
107 | 'template',
108 | 'concat',
109 | 'uglify'
110 | ]);
111 |
112 | grunt.registerTask('default', 'An alias of test', [
113 | 'test'
114 | ]);
115 | };
116 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "backbone.intercept",
3 | "version": "0.4.4",
4 | "description": "Automatically manage link clicks and form submissions within Backbone applications.",
5 | "main": "dist/backbone.intercept.js",
6 | "scripts": {
7 | "test": "grunt"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/jmeas/backbone.intercept.git"
12 | },
13 | "keywords": [
14 | "backbone",
15 | "marionette",
16 | "link",
17 | "links",
18 | "click",
19 | "router",
20 | "route",
21 | "routes",
22 | "history",
23 | "anchor",
24 | "intercept",
25 | "forms",
26 | "submit",
27 | "interception",
28 | "SPA"
29 | ],
30 | "author": "Jmeas",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/jmeas/backbone.intercept/issues"
34 | },
35 | "homepage": "https://github.com/jmeas/backbone.intercept",
36 | "dependencies": {
37 | "backbone": ">=0.9.9 <=1.3.3",
38 | "underscore": ">=1.3.3 <=1.8.3"
39 | },
40 | "devDependencies": {
41 | "chai": "^1.9.1",
42 | "grunt": "^0.4.5",
43 | "grunt-contrib-concat": "^0.5.0",
44 | "grunt-contrib-connect": "^0.9.0",
45 | "grunt-contrib-jshint": "^0.10.0",
46 | "grunt-contrib-uglify": "^0.5.1",
47 | "grunt-mocha-test": "^0.12.0",
48 | "grunt-preprocess": "^4.0.0",
49 | "grunt-template": "^0.2.3",
50 | "jquery": ">=1.8 <=3.0",
51 | "jsdom": "^3.0.0",
52 | "load-grunt-tasks": "^0.6.0",
53 | "mocha": "^1.21.4",
54 | "sinon": "^1.10.3",
55 | "sinon-chai": "^2.5.0"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/backbone.intercept.js:
--------------------------------------------------------------------------------
1 | Backbone.Intercept = {
2 |
3 | VERSION: '<%= version %>',
4 |
5 | rootSelector: 'body',
6 |
7 | defaults: {
8 | trigger : true,
9 | links : true,
10 | forms : true
11 | },
12 |
13 | start: function(options) {
14 | options = _.defaults(options || {}, this.defaults);
15 |
16 | if (options.links) {
17 | this._getRootElement().on('click.backboneIntercept', 'a', _.bind(this._interceptLinks, this));
18 | }
19 | if (options.forms) {
20 | this._getRootElement().on('submit.backboneIntercept', _.bind(this._interceptForms, this));
21 | }
22 | },
23 |
24 | stop: function() {
25 | this._getRootElement().off('.backboneIntercept');
26 | },
27 |
28 | navigate: function(uri, options) {
29 | Backbone.history.navigate(uri, options);
30 | },
31 |
32 | // Creates and caches a jQuery object for the body element
33 | _getRootElement: function() {
34 | if (this._body) { return this._body; }
35 | this._body = Backbone.$(this.rootSelector);
36 | return this._body;
37 | },
38 |
39 | // Prevent the default behavior of submitting the
40 | // form if the action attribute is present and is
41 | // a value
42 | _interceptForms: function(e) {
43 | if (e.target && e.target.action) { return; }
44 | e.preventDefault();
45 | },
46 |
47 | _interceptLinks: function(e) {
48 | // Only intercept left-clicks
49 | if (e.which !== 1) { return; }
50 | var $link = Backbone.$(e.currentTarget);
51 |
52 | // Get the href; stop processing if there isn't one
53 | var href = $link.attr('href');
54 | if (!href) { return; }
55 |
56 | // Determine if we're supposed to bypass the link
57 | // based on its attributes
58 | var bypass = this._getAttr($link, 'bypass');
59 | if (bypass !== undefined && bypass !== 'false') {
60 | return;
61 | }
62 |
63 | // If the Ctrl key is held, we want to open in a new tab.
64 | bypass = e.ctrlKey;
65 | if (bypass) {
66 | return;
67 | }
68 |
69 | // The options we pass along to navigate
70 | var navOptions = {
71 | trigger: this.defaults.trigger
72 | };
73 |
74 | // Determine if it's trigger: false based on the attributes
75 | var trigger = this._getAttr($link, 'trigger');
76 |
77 | if (trigger === 'false') {
78 | navOptions.trigger = false;
79 | } else if (trigger === 'true') {
80 | navOptions.trigger = true;
81 | }
82 |
83 | // Return if the URL is absolute, or if the protocol is mailto or javascript
84 | if (/^#|javascript:|mailto:|(?:\w+:)?\/\//.test(href)) { return; }
85 |
86 | // If we haven't been stopped yet, then we prevent the default action
87 | e.preventDefault();
88 |
89 | // Get the computed pathname of the link, removing
90 | // the leading slash. Regex required for IE8 support
91 | var pathname = $link[0].pathname.replace(/^\//, '') + $link[0].search;
92 |
93 | // Lastly we send off the information to the router
94 | if (!this.navigate) { return; }
95 | this.navigate(pathname, navOptions);
96 | },
97 |
98 | _getAttr: function($el, name) {
99 | var attr = $el.attr(name);
100 | if (attr !== undefined) {
101 | return attr;
102 | }
103 | var data = $el.attr('data-' + name);
104 | if (data !== undefined) {
105 | return data;
106 | }
107 | }
108 | };
109 |
--------------------------------------------------------------------------------
/src/wrapper.js:
--------------------------------------------------------------------------------
1 | (function(root, factory) {
2 | if (typeof define === 'function' && define.amd) {
3 | define(['backbone', 'underscore'], function(Backbone, _) {
4 | return factory(Backbone, _);
5 | });
6 | }
7 | else if (typeof exports !== 'undefined') {
8 | var Backbone = require('backbone');
9 | var _ = require('underscore');
10 | module.exports = factory(Backbone, _);
11 | }
12 | else {
13 | factory(root.Backbone, root._);
14 | }
15 | }(this, function(Backbone, _) {
16 | 'use strict';
17 |
18 | // @include backbone.intercept.js
19 |
20 | return Backbone.Intercept;
21 | }));
22 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "bitwise" : true,
3 | "camelcase" : true,
4 | "curly" : true,
5 | "eqeqeq" : true,
6 | "es3" : true,
7 | "forin" : true,
8 | "immed" : true,
9 | "indent" : 2,
10 | "latedef" : true,
11 | "newcap" : true,
12 | "noarg" : true,
13 | "noempty" : true,
14 | "nonbsp" : true,
15 | "nonew" : true,
16 | "plusplus" : true,
17 | "undef" : true,
18 | "unused" : true,
19 | "strict" : false,
20 | "trailing" : true,
21 |
22 | "asi" : false,
23 | "boss" : false,
24 | "debug" : false,
25 | "eqnull" : false,
26 | "esnext" : false,
27 | "evil" : false,
28 | "expr" : true,
29 | "funcscope" : false,
30 | "globalstrict" : false,
31 | "iterator" : false,
32 | "lastsemic" : false,
33 | "laxbreak" : false,
34 | "laxcomma" : false,
35 | "loopfunc" : false,
36 | "maxerr" : 50,
37 | "multistr" : false,
38 | "notypeof" : false,
39 | "proto" : false,
40 | "scripturl" : false,
41 | "smarttabs" : false,
42 | "shadow" : false,
43 | "sub" : false,
44 | "supernew" : false,
45 | "validthis" : false,
46 | "noyield" : false,
47 |
48 | "browser" : true,
49 | "couch" : false,
50 | "devel" : false,
51 | "dojo" : false,
52 | "jquery" : false,
53 | "mootools" : false,
54 | "node" : false,
55 | "nonstandard" : false,
56 | "prototypejs" : false,
57 | "rhino" : false,
58 | "worker" : false,
59 | "wsh" : false,
60 | "yui" : false,
61 | "globals": {
62 | "Backbone": true,
63 | "_": true,
64 | "$": true,
65 | "require": true,
66 | "module": true,
67 | "define": true,
68 | "exports": true,
69 | "expect": true,
70 | "beforeEach": true,
71 | "afterEach": true,
72 | "describe": true,
73 | "it": true
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/test/setup/helpers.js:
--------------------------------------------------------------------------------
1 | // Adds elements to the document
2 | var setFixtures = function () {
3 | _.each(arguments, function (content) {
4 | helpers.$fixtures.append(content);
5 | });
6 | };
7 |
8 | // Empties our body
9 | var clearFixtures = function () {
10 | helpers.$fixtures.empty();
11 | };
12 |
13 | function setupTestHelpers() {
14 | beforeEach(function() {
15 | this.originalIntercept = _.clone(Backbone.Intercept);
16 | this.sinon = sinon.sandbox.create();
17 | this.setFixtures = setFixtures;
18 | this.clearFixtures = clearFixtures;
19 | global.stub = _.bind(this.sinon.stub, this.sinon);
20 | global.spy = _.bind(this.sinon.spy, this.sinon);
21 | });
22 |
23 | afterEach(function() {
24 | clearFixtures();
25 | this.sinon.restore();
26 | delete global.stub;
27 | delete global.spy;
28 | Backbone.Intercept.stop();
29 | Backbone.Intercept = this.originalIntercept;
30 | });
31 | }
32 |
33 | var helpers = {};
34 |
35 | var node = typeof exports !== 'undefined';
36 | var $ = node ? require('jquery') : $;
37 |
38 | if (node) {
39 | helpers.$fixtures = $('body');
40 | setupTestHelpers();
41 | }
42 |
43 | // when running in browser
44 | else {
45 | this.global = window;
46 | mocha.setup('bdd');
47 |
48 | window.expect = chai.expect;
49 | window.sinon = sinon;
50 |
51 | onload = function() {
52 | mocha.checkLeaks();
53 | mocha.globals(['stub', 'spy']);
54 | mocha.run();
55 | helpers.$fixtures = $('#fixtures');
56 | setupTestHelpers();
57 | };
58 | }
59 |
--------------------------------------------------------------------------------
/test/setup/node.js:
--------------------------------------------------------------------------------
1 | // Create our JSDom document
2 | global.jsdom = require('jsdom').jsdom;
3 | global.document = jsdom(
4 | '',
5 | { url: 'http://0.0.0.0:8000/test/spec-runner.html' }
6 | );
7 | global.window = global.document.parentWindow;
8 | global.navigator = window.navigator = {
9 | userAgent: 'NodeJS JSDom',
10 | appVersion: ''
11 | };
12 |
13 | var sinon = require('sinon');
14 | var chai = require('chai');
15 | var sinonChai = require('sinon-chai');
16 |
17 | global.$ = require('jquery');
18 | global._ = require('underscore');
19 | global.Backbone = require('backbone');
20 | global.Backbone.$ = global.$;
21 |
22 | chai.use(sinonChai);
23 |
24 | global.expect = chai.expect;
25 | global.sinon = sinon;
26 |
27 | require('../../src/backbone.intercept');
28 |
--------------------------------------------------------------------------------
/test/spec-runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Backbone.Intercept Tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/unit/clicks.js:
--------------------------------------------------------------------------------
1 | describe('When clicking a link', function() {
2 | beforeEach(function() {
3 | Backbone.Intercept.start();
4 | });
5 |
6 | describe('with a left click', function() {
7 | beforeEach(function() {
8 | this.leftClick = $.Event('click');
9 | this.leftClick.which = 1;
10 | this.leftClick.preventDefault();
11 | this.sinon.stub(this.leftClick, 'preventDefault');
12 | });
13 |
14 | describe('and the link is relative, without a leading slash', function() {
15 | beforeEach(function() {
16 | var $link = $('');
17 | this.setFixtures($link);
18 | this.sinon.stub(Backbone.Intercept, 'navigate');
19 | $link.trigger(this.leftClick);
20 | });
21 |
22 | it('should intercept the link', function() {
23 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
24 | });
25 |
26 | it('should pass trigger:true to the share method', function() {
27 | expect(Backbone.Intercept.navigate)
28 | .to.have.been.calledOnce
29 | .and.calledWithExactly('test/path/to/my-thing', {trigger:true});
30 | });
31 | });
32 |
33 | describe('and the link is relative, with a leading slash', function() {
34 | beforeEach(function() {
35 | var $link = $('');
36 | this.setFixtures($link);
37 | this.sinon.stub(Backbone.Intercept, 'navigate');
38 | $link.trigger(this.leftClick);
39 | });
40 |
41 | it('should intercept the link', function() {
42 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
43 | });
44 |
45 | it('should pass trigger:true to the share method', function() {
46 | expect(Backbone.Intercept.navigate)
47 | .to.have.been.calledOnce
48 | .and.calledWithExactly('path/to/my-thing', {trigger:true});
49 | });
50 | });
51 |
52 | describe('and the link starts with www', function() {
53 | beforeEach(function() {
54 | var $link = $('');
55 | this.setFixtures($link);
56 | $link.trigger(this.leftClick);
57 | });
58 |
59 | it('should intercept the link', function() {
60 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
61 | });
62 | });
63 |
64 | describe('and the link is a fragment (it starts with #)', function() {
65 | beforeEach(function() {
66 | var $link = $('');
67 | this.setFixtures($link);
68 | $link.trigger(this.leftClick);
69 | });
70 |
71 | it('should not intercept the link', function() {
72 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
73 | });
74 | });
75 |
76 | describe('and the link is absolute, starting with http://', function() {
77 | beforeEach(function() {
78 | var $link = $('');
79 | this.setFixtures($link);
80 | $link.trigger(this.leftClick);
81 | });
82 |
83 | it('should not intercept the link', function() {
84 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
85 | });
86 | });
87 |
88 | describe('and the link is absolute, starting with https://', function() {
89 | beforeEach(function() {
90 | var $link = $('');
91 | this.setFixtures($link);
92 | $link.trigger(this.leftClick);
93 | });
94 |
95 | it('should not intercept the link', function() {
96 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
97 | });
98 | });
99 |
100 | describe('and the link is using the javascript: protocol', function() {
101 | beforeEach(function() {
102 | var $link = $('');
103 | this.setFixtures($link);
104 | $link.trigger(this.leftClick);
105 | });
106 |
107 | it('should not intercept the link', function() {
108 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
109 | });
110 | });
111 |
112 | describe('and the link is using the mailto: protocol', function() {
113 | beforeEach(function() {
114 | var $link = $('');
115 | this.setFixtures($link);
116 | $link.trigger(this.leftClick);
117 | });
118 |
119 | it('should not intercept the link', function() {
120 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
121 | });
122 | });
123 |
124 | describe('and the link has no href attribute', function() {
125 | beforeEach(function() {
126 | var $link = $('');
127 | this.setFixtures($link);
128 | $link.trigger(this.leftClick);
129 | });
130 |
131 | it('should not intercept the link', function() {
132 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
133 | });
134 | });
135 |
136 | describe('and the "data-bypass" attribute exists on the link', function() {
137 | beforeEach(function() {
138 | var $link = $('');
139 | this.setFixtures($link);
140 | $link.trigger(this.leftClick);
141 | });
142 |
143 | it('should not intercept the link', function() {
144 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
145 | });
146 | });
147 |
148 | describe('and the "data-bypass" attribute is "true" on the link', function() {
149 | beforeEach(function() {
150 | var $link = $('');
151 | this.setFixtures($link);
152 | $link.trigger(this.leftClick);
153 | });
154 |
155 | it('should not intercept the link', function() {
156 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
157 | });
158 | });
159 |
160 | describe('and the "data-bypass" attribute is "false" on the link', function() {
161 | beforeEach(function() {
162 | var $link = $('');
163 | this.setFixtures($link);
164 | $link.trigger(this.leftClick);
165 | });
166 |
167 | it('should intercept the link', function() {
168 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
169 | });
170 | });
171 |
172 | describe('and the "bypass" attribute exists on the link', function() {
173 | beforeEach(function() {
174 | var $link = $('');
175 | this.setFixtures($link);
176 | $link.trigger(this.leftClick);
177 | });
178 |
179 | it('should not intercept the link', function() {
180 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
181 | });
182 | });
183 |
184 | describe('and the "bypass" attribute is "true" on the link', function() {
185 | beforeEach(function() {
186 | var $link = $('');
187 | this.setFixtures($link);
188 | $link.trigger(this.leftClick);
189 | });
190 |
191 | it('should not intercept the link', function() {
192 | expect(this.leftClick.preventDefault).to.not.have.been.calledOnce;
193 | });
194 | });
195 |
196 | describe('and the "bypass" attribute is "false" on the link', function() {
197 | beforeEach(function() {
198 | var $link = $('');
199 | this.setFixtures($link);
200 | $link.trigger(this.leftClick);
201 | });
202 |
203 | it('should intercept the link', function() {
204 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
205 | });
206 | });
207 |
208 | describe('and the "trigger" attribute is undefined on the link', function() {
209 | beforeEach(function() {
210 | var $link = $('');
211 | this.setFixtures($link);
212 | this.sinon.stub(Backbone.Intercept, 'navigate');
213 | $link.trigger(this.leftClick);
214 | });
215 |
216 | it('should intercept the link', function() {
217 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
218 | });
219 |
220 | it('should pass trigger:true to the share method', function() {
221 | expect(Backbone.Intercept.navigate)
222 | .to.have.been.calledOnce
223 | .and.calledWithExactly('test/link/to/my-page', {trigger:true});
224 | });
225 | });
226 |
227 | describe('and the "trigger" attribute is false on the link', function() {
228 | beforeEach(function() {
229 | var $link = $('');
230 | this.setFixtures($link);
231 | this.sinon.stub(Backbone.Intercept, 'navigate');
232 | $link.trigger(this.leftClick);
233 | });
234 |
235 | it('should intercept the link', function() {
236 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
237 | });
238 |
239 | it('should pass trigger:false to the share method', function() {
240 | expect(Backbone.Intercept.navigate)
241 | .to.have.been.calledOnce
242 | .and.calledWithExactly('test/link/to/my-page', {trigger:false});
243 | });
244 | });
245 |
246 | describe('and the "data-trigger" attribute is false on the link', function() {
247 | beforeEach(function() {
248 | var $link = $('');
249 | this.setFixtures($link);
250 | this.sinon.stub(Backbone.Intercept, 'navigate');
251 | $link.trigger(this.leftClick);
252 | });
253 |
254 | it('should intercept the link', function() {
255 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
256 | });
257 |
258 | it('should pass trigger:false to the share method', function() {
259 | expect(Backbone.Intercept.navigate)
260 | .to.have.been.calledOnce
261 | .and.calledWithExactly('test/link/to/my-page', {trigger:false});
262 | });
263 | });
264 |
265 | describe('the link has search parameters', function() {
266 | beforeEach(function() {
267 | var $link = $('');
268 | this.setFixtures($link);
269 | this.sinon.stub(Backbone.Intercept, 'navigate');
270 | $link.trigger(this.leftClick);
271 | });
272 |
273 | it('should intercept the link', function() {
274 | expect(this.leftClick.preventDefault).to.have.been.calledOnce;
275 | });
276 |
277 | it('should have the search parameters inside the link navigated to', function() {
278 | expect(Backbone.Intercept.navigate)
279 | .to.have.been.calledOnce
280 | .and.calledWithExactly('test/path/to/my-thing?page=1&limit=50', {trigger:true});
281 | });
282 | });
283 | });
284 |
285 | describe('with a middle click', function() {
286 | beforeEach(function() {
287 | this.middleClick = $.Event('click');
288 | this.middleClick.which = 2;
289 | this.middleClick.preventDefault();
290 | this.sinon.stub(this.middleClick, 'preventDefault');
291 | });
292 |
293 | describe('and the link is relative, without a leading slash', function() {
294 | beforeEach(function() {
295 | var $link = $('');
296 | this.setFixtures($link);
297 | $link.trigger(this.middleClick);
298 | });
299 |
300 | it('should not intercept the link', function() {
301 | expect(this.middleClick.preventDefault).to.not.have.been.calledOnce;
302 | });
303 | });
304 | });
305 |
306 | describe('with a right click', function() {
307 | beforeEach(function() {
308 | this.rightClick = $.Event('click');
309 | this.rightClick.which = 3;
310 | this.rightClick.preventDefault();
311 | this.sinon.stub(this.rightClick, 'preventDefault');
312 | });
313 |
314 | describe('and the link is relative, without a leading slash', function() {
315 | beforeEach(function() {
316 | var $link = $('');
317 | this.setFixtures($link);
318 | $link.trigger(this.rightClick);
319 | });
320 |
321 | it('should not intercept the link', function() {
322 | expect(this.rightClick.preventDefault).to.not.have.been.calledOnce;
323 | });
324 | });
325 | });
326 | });
327 |
--------------------------------------------------------------------------------
/test/unit/forms.js:
--------------------------------------------------------------------------------
1 | describe('When submitting a form', function() {
2 | beforeEach(function() {
3 | Backbone.Intercept.start();
4 | this.submitEvent = $.Event('submit');
5 | this.submitEvent.preventDefault();
6 | this.sinon.spy(this.submitEvent, 'preventDefault');
7 | });
8 |
9 | describe('and the form does not have an action attribute', function() {
10 | beforeEach(function() {
11 | var $form = $('');
12 | this.setFixtures($form);
13 | $form.trigger(this.submitEvent);
14 | });
15 |
16 | it('should prevent the default action', function() {
17 | expect(this.submitEvent.preventDefault).to.have.been.calledOnce;
18 | });
19 | });
20 |
21 | describe('and the form has a defined action attribute', function() {
22 | beforeEach(function() {
23 | var $form = $('');
24 | this.setFixtures($form);
25 | $form.trigger(this.submitEvent);
26 | });
27 |
28 | it('should not prevent the default action', function() {
29 | expect(this.submitEvent.preventDefault).to.not.have.been.calledOnce;
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/test/unit/navigate.js:
--------------------------------------------------------------------------------
1 | describe('When calling the navigate method', function() {
2 | beforeEach(function() {
3 | this.sinon.stub(Backbone.history, 'navigate');
4 | Backbone.Intercept.navigate('path/to/my-resource', {trigger:true});
5 | });
6 |
7 | it('should pass the URI along to the history navigate method', function() {
8 | expect(Backbone.history.navigate)
9 | .to.have.been.calledOnce
10 | .and.calledWithExactly('path/to/my-resource', {trigger:true});
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/unit/root-element.js:
--------------------------------------------------------------------------------
1 | describe('When setting the root selector before starting Intercept', function() {
2 | beforeEach(function() {
3 | Backbone.Intercept.rootSelector = '.backbone-root';
4 |
5 | // Create a normal link; one that shouldn't be intercepted
6 | this.$normalLink = $('');
7 | this.setFixtures(this.$normalLink);
8 |
9 | // Create the scope of our app
10 | this.$scope = $('');
11 | this.setFixtures(this.$scope);
12 |
13 | // Lastly, insert a link into the scope
14 | this.$scopedLink = $('');
15 | this.$scope.append(this.$scopedLink);
16 |
17 | // Start intercept and click around
18 | Backbone.Intercept.start();
19 |
20 | this.leftClick = $.Event('click');
21 | this.leftClick.which = 1;
22 | this.leftClick.preventDefault();
23 | });
24 |
25 | describe('and clicking a link outside of the scoped element', function() {
26 | beforeEach(function() {
27 | this.$normalLink.trigger(this.leftClick);
28 | });
29 |
30 | it('should not prevent the default behavior of that link', function() {
31 | expect(this.leftClick.preventDefault).to.not.have.beenCalled;
32 | });
33 | });
34 |
35 | describe('and clicking a link within the scoped element', function() {
36 | beforeEach(function() {
37 | this.$scopedLink.trigger(this.leftClick);
38 | });
39 |
40 | it('should prevent the default behavior of that link', function() {
41 | expect(this.leftClick.preventDefault).to.have.beenCalledOnce;
42 | });
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/test/unit/start.js:
--------------------------------------------------------------------------------
1 | describe('When calling start', function() {
2 | beforeEach(function() {
3 | this.sinon.stub(_, 'bind', _.identity);
4 | });
5 |
6 | describe('and passing no options', function() {
7 | beforeEach(function() {
8 | this.$body = Backbone.Intercept._getRootElement();
9 | this.sinon.stub(this.$body, 'on');
10 | Backbone.Intercept.start();
11 | });
12 |
13 | it('should set handlers for click and submit events', function() {
14 | expect(_.bind)
15 | .to.have.been.calledTwice
16 | .and.calledWithExactly(Backbone.Intercept._interceptLinks, Backbone.Intercept)
17 | .and.calledWithExactly(Backbone.Intercept._interceptForms, Backbone.Intercept);
18 | expect(this.$body.on)
19 | .to.have.been.calledTwice
20 | .and.calledWithExactly('click.backboneIntercept', 'a', Backbone.Intercept._interceptLinks)
21 | .and.calledWithExactly('submit.backboneIntercept', Backbone.Intercept._interceptForms);
22 | });
23 | });
24 |
25 | describe('and specifying false for links', function() {
26 | beforeEach(function() {
27 | this.$body = Backbone.Intercept._getRootElement();
28 | this.sinon.stub(this.$body, 'on');
29 | Backbone.Intercept.start({links: false});
30 | });
31 |
32 | it('should only set handlers for link events', function() {
33 | expect(_.bind)
34 | .to.have.been.calledOnce
35 | .and.calledWithExactly(Backbone.Intercept._interceptForms, Backbone.Intercept);
36 | expect(this.$body.on)
37 | .to.have.been.calledOnce
38 | .and.calledWithExactly('submit.backboneIntercept', Backbone.Intercept._interceptForms);
39 | });
40 | });
41 |
42 | describe('and specifying false for forms', function() {
43 | beforeEach(function() {
44 | this.$body = Backbone.Intercept._getRootElement();
45 | this.sinon.stub(this.$body, 'on');
46 | Backbone.Intercept.start({forms: false});
47 | });
48 |
49 | it('should only set handlers for link events', function() {
50 | expect(_.bind)
51 | .to.have.been.calledOnce
52 | .and.calledWithExactly(Backbone.Intercept._interceptLinks, Backbone.Intercept);
53 | expect(this.$body.on)
54 | .to.have.been.calledOnce
55 | .and.calledWithExactly('click.backboneIntercept', 'a', Backbone.Intercept._interceptLinks);
56 | });
57 | });
58 |
59 | describe('and specifying false for links and forms', function() {
60 | beforeEach(function() {
61 | this.$body = Backbone.Intercept._getRootElement();
62 | this.sinon.stub(this.$body, 'on');
63 | Backbone.Intercept.start({forms: false, links: false});
64 | });
65 |
66 | it('should not set any handlers', function() {
67 | expect(this.$body.on).to.not.have.beenCalled;
68 | });
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/test/unit/stop.js:
--------------------------------------------------------------------------------
1 | describe('When calling stop', function() {
2 | beforeEach(function() {
3 | this.$body = Backbone.Intercept._getRootElement();
4 | this.sinon.stub(this.$body, 'off');
5 | Backbone.Intercept.stop();
6 | });
7 |
8 | it('should remove all listeners', function() {
9 | expect(this.$body.off)
10 | .to.have.been.calledOnce
11 | .and.calledWithExactly('.backboneIntercept');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------