volt 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Readme.md +97 -56
- data/VERSION +1 -1
- data/app/volt/assets/js/sockjs-0.3.4.min.js +27 -0
- data/app/volt/assets/js/vertxbus.js +216 -0
- data/app/volt/tasks/live_query/live_query.rb +5 -5
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +5 -0
- data/app/volt/tasks/store_tasks.rb +44 -18
- data/docs/WHY.md +10 -0
- data/lib/volt/cli.rb +18 -7
- data/lib/volt/controllers/model_controller.rb +30 -8
- data/lib/volt/extra_core/inflections.rb +63 -0
- data/lib/volt/extra_core/inflector/inflections.rb +203 -0
- data/lib/volt/extra_core/inflector/methods.rb +63 -0
- data/lib/volt/extra_core/inflector.rb +4 -0
- data/lib/volt/extra_core/object.rb +9 -0
- data/lib/volt/extra_core/string.rb +10 -14
- data/lib/volt/models/array_model.rb +45 -27
- data/lib/volt/models/cursor.rb +6 -0
- data/lib/volt/models/model.rb +127 -12
- data/lib/volt/models/model_hash_behaviour.rb +8 -5
- data/lib/volt/models/model_helpers.rb +4 -4
- data/lib/volt/models/model_state.rb +22 -0
- data/lib/volt/models/persistors/array_store.rb +49 -35
- data/lib/volt/models/persistors/base.rb +3 -3
- data/lib/volt/models/persistors/model_store.rb +17 -6
- data/lib/volt/models/persistors/query/query_listener.rb +0 -2
- data/lib/volt/models/persistors/store.rb +0 -4
- data/lib/volt/models/persistors/store_state.rb +27 -0
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validations/errors.rb +0 -0
- data/lib/volt/models/validations/length.rb +13 -0
- data/lib/volt/models/validations/validations.rb +82 -0
- data/lib/volt/models.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +29 -14
- data/lib/volt/page/bindings/base_binding.rb +2 -2
- data/lib/volt/page/bindings/component_binding.rb +29 -25
- data/lib/volt/page/bindings/content_binding.rb +1 -0
- data/lib/volt/page/bindings/each_binding.rb +25 -33
- data/lib/volt/page/bindings/event_binding.rb +0 -1
- data/lib/volt/page/bindings/if_binding.rb +3 -1
- data/lib/volt/page/bindings/template_binding.rb +61 -28
- data/lib/volt/page/document_events.rb +3 -1
- data/lib/volt/page/draw_cycle.rb +22 -0
- data/lib/volt/page/page.rb +10 -1
- data/lib/volt/page/reactive_template.rb +23 -16
- data/lib/volt/page/sub_context.rb +1 -1
- data/lib/volt/page/targets/attribute_section.rb +3 -2
- data/lib/volt/page/targets/attribute_target.rb +0 -4
- data/lib/volt/page/targets/base_section.rb +25 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +13 -14
- data/lib/volt/page/targets/binding_document/html_node.rb +4 -0
- data/lib/volt/page/targets/dom_section.rb +16 -67
- data/lib/volt/page/targets/dom_template.rb +99 -0
- data/lib/volt/page/targets/helpers/comment_searchers.rb +29 -0
- data/lib/volt/page/template_renderer.rb +2 -14
- data/lib/volt/reactive/array_extensions.rb +0 -1
- data/lib/volt/reactive/event_chain.rb +9 -2
- data/lib/volt/reactive/events.rb +44 -37
- data/lib/volt/reactive/object_tracking.rb +1 -1
- data/lib/volt/reactive/reactive_array.rb +18 -0
- data/lib/volt/reactive/reactive_count.rb +108 -0
- data/lib/volt/reactive/reactive_generator.rb +44 -0
- data/lib/volt/reactive/reactive_value.rb +73 -73
- data/lib/volt/reactive/string_extensions.rb +1 -1
- data/lib/volt/router/routes.rb +205 -88
- data/lib/volt/server/component_handler.rb +3 -1
- data/lib/volt/server/html_parser/view_parser.rb +20 -4
- data/lib/volt/server/rack/component_paths.rb +13 -10
- data/lib/volt/server/rack/index_files.rb +4 -4
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server.rb +10 -3
- data/spec/apps/kitchen_sink/.gitignore +8 -0
- data/spec/apps/kitchen_sink/Gemfile +32 -0
- data/spec/apps/kitchen_sink/app/home/views/index/index.html +3 -5
- data/spec/apps/kitchen_sink/config.ru +4 -0
- data/spec/apps/kitchen_sink/public/index.html +2 -2
- data/spec/extra_core/inflector_spec.rb +8 -0
- data/spec/models/event_chain_spec.rb +18 -0
- data/spec/models/model_buffers_spec.rb +9 -0
- data/spec/models/model_spec.rb +22 -9
- data/spec/models/reactive_array_spec.rb +26 -1
- data/spec/models/reactive_call_times_spec.rb +28 -0
- data/spec/models/reactive_value_spec.rb +19 -0
- data/spec/models/validations_spec.rb +39 -0
- data/spec/page/bindings/content_binding_spec.rb +1 -0
- data/spec/{templates → page/bindings}/template_binding_spec.rb +54 -0
- data/spec/router/routes_spec.rb +156 -8
- data/spec/server/html_parser/sandlebars_parser_spec.rb +55 -47
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -11
- data/spec/templates/targets/binding_document/component_node_spec.rb +12 -0
- data/templates/project/Gemfile.tt +11 -0
- data/templates/project/app/home/config/routes.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +5 -5
- data/templates/project/app/home/views/index/index.html +6 -6
- data/volt.gemspec +5 -6
- metadata +34 -76
- data/app/volt/assets/js/sockjs-0.2.1.min.js +0 -27
- data/lib/volt/reactive/object_tracker.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fac33a348622b758d4f6ccff8d30234a2720d9c5
|
4
|
+
data.tar.gz: 427c22c2ebb283980203073ff812323f6c238e41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf61e39ae71ccc6af0eca7f0c04dfe32e80bc91595059ccf8abb46c4a9c1a75d24aab2dcf985fc94d410103164bdf8b708149a0b807bd837ea395167cb654afb
|
7
|
+
data.tar.gz: a98c2352510292f41f34960b376519f77753a228630d521ed756613a090cf006f42f011c85898cd1c3a1c6301672d9851bfa27666dd0fcff8f18605e679fabfc
|
data/Gemfile
CHANGED
@@ -3,7 +3,6 @@ source 'http://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
# gem 'rspec', '3.0.0.beta1'
|
6
|
-
# gem 'nokogiri', :require => false
|
7
6
|
gem 'opal', git: 'https://github.com/opal/opal.git'
|
8
7
|
gem 'opal-jquery', :git => 'https://github.com/opal/opal-jquery.git'
|
9
8
|
gem 'sockjs', git: 'https://github.com/kacperk/sockjs-ruby.git', require: false, platforms: :mri
|
@@ -34,4 +33,4 @@ group :development do
|
|
34
33
|
# gem 'guard-rspec'
|
35
34
|
# gem 'opal-rspec', '0.3.0.beta2'#, git: 'https://github.com/adambeynon/opal-rspec.git'
|
36
35
|
# gem 'yard', require: false
|
37
|
-
end
|
36
|
+
end
|
data/Readme.md
CHANGED
@@ -8,28 +8,29 @@
|
|
8
8
|
|
9
9
|
# Volt
|
10
10
|
|
11
|
-
Volt is a ruby web framework where your ruby code runs on both the server and the client (via [opal](https://github.com/opal/opal).) The
|
11
|
+
Volt is a ruby web framework where your ruby code runs on both the server and the client (via [opal](https://github.com/opal/opal).) The DOM automatically update as the user interacts with the page. Page state can be stored in the URL, if the user hits a URL directly, the HTML will first be rendered on the server for faster load times and easier indexing by search engines.
|
12
12
|
|
13
13
|
Instead of syncing data between the client and server via HTTP, volt uses a persistent connection between the client and server. When data updated on one client, it is updated in the database and any other listening clients. (With almost no setup code needed)
|
14
14
|
|
15
|
-
Pages HTML is written in a handlebars like template language. Volt uses data flow/reactive programming to automatically and
|
15
|
+
Pages HTML is written in a handlebars like template language. Volt uses data flow/reactive programming to automatically and intelligently propagate changes to the DOM (or anything other code wanting to know when a value updates) When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
|
16
16
|
|
17
17
|
See a quick demo video here: [http://www.youtube.com/watch?v=j0vFIRMzarI](http://www.youtube.com/watch?v=j0vFIRMzarI)
|
18
18
|
|
19
|
+
|
19
20
|
## Goals
|
20
21
|
|
21
22
|
Volt has the following goals:
|
22
23
|
|
23
|
-
1. Developer
|
24
|
+
1. Developer happiness
|
24
25
|
2. Write once on the client and the server
|
25
26
|
3. Automatic data syncing between client and server
|
26
27
|
4. Apps are built as nested components. Components can be shared (via gems)
|
27
28
|
5. Concurrent. Volt provides tools to simplify concurrency. Component rendering is done in parallel on the server.
|
28
|
-
6.
|
29
|
+
6. Intelligent asset management
|
29
30
|
7. Secure (shouldn't need to be said, but it does)
|
30
31
|
8. Be fast/light
|
31
32
|
9. Understandable code base
|
32
|
-
10. Control
|
33
|
+
10. Control upgradeability
|
33
34
|
|
34
35
|
# Road Map
|
35
36
|
|
@@ -67,7 +68,7 @@ You can access the volt console with:
|
|
67
68
|
1. [Getting Help](#getting-help)
|
68
69
|
2. [Rendering](#rendering)
|
69
70
|
1. [Reactive Values](#reactive-values)
|
70
|
-
1. [ReactiveValue
|
71
|
+
1. [ReactiveValue Gotchas](#reactivevalue-gotchas)
|
71
72
|
3. [Views](#views)
|
72
73
|
1. [Bindings](#bindings)
|
73
74
|
1. [Content Binding](#content-binding)
|
@@ -82,9 +83,10 @@ You can access the volt console with:
|
|
82
83
|
4. [Automatic Model Conversion](#automatic-model-conversion)
|
83
84
|
5. [Controllers](#controllers)
|
84
85
|
6. [Components](#components)
|
85
|
-
1. [
|
86
|
-
2. [
|
87
|
-
3. [
|
86
|
+
1. [Dependencies](#dependencies)
|
87
|
+
2. [Assets](#assets)
|
88
|
+
3. [Component Generator](#component-generator)
|
89
|
+
4. [Provided Components](#provided-components)
|
88
90
|
1. [Notices](#notices)
|
89
91
|
2. [Flash](#flash)
|
90
92
|
7. [Controls](#controls)
|
@@ -94,7 +96,7 @@ You can access the volt console with:
|
|
94
96
|
|
95
97
|
# Getting Help
|
96
98
|
|
97
|
-
Volt is still a work in progress, but early feedback is
|
99
|
+
Volt is still a work in progress, but early feedback is appreciated. Use the following to communicate with the developers, someone will get back to you very quickly:
|
98
100
|
|
99
101
|
- **If you need help**: post on [stackoverflow.com](http://www.stackoverflow.com), be sure to tag your question with voltrb
|
100
102
|
- **If you found a bug**: post on [github issues](https://github.com/voltrb/volt/issues)
|
@@ -111,7 +113,7 @@ When a user interacts with a web page, typically we want to do two things:
|
|
111
113
|
|
112
114
|
For example when a user clicks to add a new todo item to a todo list, we might create a JavaScript object to represent the todo item, then add an item to the list's DOM. A lot of work needs to be done to make sure that the JavaScript object and the DOM always stay in sync.
|
113
115
|
|
114
|
-
|
116
|
+
The idea of "reactive programming" has been used to simplify maintaining the DOM. The idea is instead of having event handlers that manage a model (or JavaScript object) and manage the DOM, we have event handlers that manage reactive data models. We describe our DOM layer in a declarative way so that it automatically knows how to render our data models.
|
115
117
|
|
116
118
|
## Reactive Value's
|
117
119
|
|
@@ -122,9 +124,9 @@ To build bindings, Volt provides the ReactiveValue class. This wraps any object
|
|
122
124
|
# => @"my object"
|
123
125
|
```
|
124
126
|
|
125
|
-
When .inspect is called on a ReactiveValue (like in the console), an @ is placed
|
127
|
+
When .inspect is called on a ReactiveValue (like in the console), an @ is placed in front of the value's inspect string, so you know its reactive.
|
126
128
|
|
127
|
-
When you call a method on a ReactiveValue, you get back a new reactive value that depends on the previous one. It
|
129
|
+
When you call a method on a ReactiveValue, you get back a new reactive value that depends on the previous one. It remembers how it was created and you can call .cur on it any time to get its current value, which will be computed based off of the first reactive value. (Keep in mind below that + is a method call, the same as a.+(b) in ruby.)
|
128
130
|
|
129
131
|
```ruby
|
130
132
|
a = ReactiveValue.new(1)
|
@@ -146,7 +148,7 @@ When you call a method on a ReactiveValue, you get back a new reactive value tha
|
|
146
148
|
# => 7
|
147
149
|
```
|
148
150
|
|
149
|
-
This provides the backbone for reactive programming. We setup computation/flow graphs instead of doing an actual
|
151
|
+
This provides the backbone for reactive programming. We setup computation/flow graphs instead of doing an actual calculation. Calling .cur (or .inspect, .to_s, etc..) runs the computation and returns the current value at that time, based on all of its dependencies.
|
150
152
|
|
151
153
|
ReactiveValue's also let you setup listeners and trigger events:
|
152
154
|
|
@@ -157,7 +159,7 @@ ReactiveValue's also let you setup listeners and trigger events:
|
|
157
159
|
# => A Changed
|
158
160
|
```
|
159
161
|
|
160
|
-
These events
|
162
|
+
These events propagate to any reactive value's created off of a reactive value.
|
161
163
|
|
162
164
|
```ruby
|
163
165
|
a = ReactiveValue.new(1)
|
@@ -170,7 +172,7 @@ These events propigate to any reactive value's created off of a reactive value.
|
|
170
172
|
|
171
173
|
This event flow lets us know when an object has changed, so we can update everything that depended on that object.
|
172
174
|
|
173
|
-
Lastly, we can also pass in other reactive value's as arguments to methods on a reactive value. The dependencies will be tracked for both and events will
|
175
|
+
Lastly, we can also pass in other reactive value's as arguments to methods on a reactive value. The dependencies will be tracked for both and events will propagate down from both. (Also, note that doing `.cur =` to update the current value triggers a "changed" event.)
|
174
176
|
|
175
177
|
```ruby
|
176
178
|
a = ReactiveValue.new(1)
|
@@ -190,7 +192,7 @@ Lastly, we can also pass in other reactive value's as arguments to methods on a
|
|
190
192
|
# => C changed
|
191
193
|
```
|
192
194
|
|
193
|
-
### ReactiveValue
|
195
|
+
### ReactiveValue Gotcha's
|
194
196
|
|
195
197
|
There are a few simple things to keep in mind with ReactiveValue's. In order to make them mostly compatible with other ruby objects, a two methods do not return another ReactiveValue.
|
196
198
|
|
@@ -248,29 +250,33 @@ Views in Volt are use a templating language similar to handlebars. They can be
|
|
248
250
|
<:Body>
|
249
251
|
```
|
250
252
|
|
251
|
-
Section headers should start with a capital letter so as not to be confused with [controls](#controls). Section headers do not use
|
253
|
+
Section headers should start with a capital letter so as not to be confused with [controls](#controls). Section headers do not use closing tags. If section headers are not provided, the Body section is assumed.
|
252
254
|
|
253
255
|
Section's help you split up different parts of the same content (title and body usually), but within the same file.
|
254
256
|
|
255
257
|
## Bindings
|
256
258
|
|
257
|
-
One you understand the basics of ReactiveValue's, we can discuss bindings. In Volt, you code your views in a handlebar's like template language. Volt provides
|
259
|
+
One you understand the basics of ReactiveValue's, we can discuss bindings. In Volt, you code your views in a handlebar's like template language. Volt provides several bindings, which handle rendering of something for you. Content bindings are anything inbetween { and }
|
258
260
|
|
259
261
|
### Content binding
|
260
262
|
|
261
263
|
The most basic binding is a content binding:
|
262
264
|
|
265
|
+
```html
|
263
266
|
<p>{some_method}<p>
|
267
|
+
```
|
264
268
|
|
265
|
-
The content binding runs the ruby code between { and }, then renders the return value. If the returned value is a ReactiveValue, it will update the value updated whenever a 'changed' event is
|
269
|
+
The content binding runs the ruby code between { and }, then renders the return value. If the returned value is a ReactiveValue, it will update the value updated whenever a 'changed' event is triggered on the reactive value.
|
266
270
|
|
267
271
|
### If binding
|
268
272
|
|
269
273
|
An if binding lets you provide basic flow control.
|
270
274
|
|
275
|
+
```html
|
271
276
|
{#if _some_check?}
|
272
277
|
<p>render this</p>
|
273
278
|
{/}
|
279
|
+
```
|
274
280
|
|
275
281
|
Blocks are closed with a {/}
|
276
282
|
|
@@ -278,6 +284,7 @@ When the #if binding is rendered, it will run the ruby code after #if. If the c
|
|
278
284
|
|
279
285
|
If bindings can also have #elsif and #else blocks.
|
280
286
|
|
287
|
+
```html
|
281
288
|
{#if _condition_1?}
|
282
289
|
<p>condition 1 true</p>
|
283
290
|
{#elsif _condition_2?}
|
@@ -285,22 +292,27 @@ If bindings can also have #elsif and #else blocks.
|
|
285
292
|
{#else}
|
286
293
|
<p>neither true</p>
|
287
294
|
{/}
|
295
|
+
```
|
288
296
|
|
289
297
|
### Each binding
|
290
298
|
|
291
299
|
For iteration over objects, the each binding is provided.
|
292
300
|
|
301
|
+
```html
|
293
302
|
{#each _items as item}
|
294
303
|
<p>{item}</p>
|
295
304
|
{/}
|
305
|
+
```
|
296
306
|
|
297
307
|
Above, if _items was an array, the block would be rendered for each item, setting 'item' to the value of the array element.
|
298
308
|
|
299
309
|
You can also access the position of the item in the array with the #index method.
|
300
310
|
|
311
|
+
```html
|
301
312
|
{#each _items as item}
|
302
313
|
<p>{index}. {item}</p>
|
303
314
|
{/}
|
315
|
+
```
|
304
316
|
|
305
317
|
For the array: ['one', 'two', 'three'] this would print:
|
306
318
|
|
@@ -308,23 +320,29 @@ For the array: ['one', 'two', 'three'] this would print:
|
|
308
320
|
1. two
|
309
321
|
2. three
|
310
322
|
|
311
|
-
You can do {index + 1} to correct the
|
323
|
+
You can do {index + 1} to correct the zero offset.
|
312
324
|
|
313
|
-
When items are removed or added to the array, the #each binding automatically and
|
325
|
+
When items are removed or added to the array, the #each binding automatically and intelligently add or removes the items from/to the DOM.
|
314
326
|
|
315
327
|
## Attribute Bindings
|
316
328
|
|
317
329
|
Bindings can also be placed inside of attributes.
|
318
330
|
|
331
|
+
```html
|
319
332
|
<p class="{#if _is_cool?}cool{/}">Text</p>
|
333
|
+
```
|
320
334
|
|
321
335
|
There are some special features provided to make for elements work as "two way bindings"
|
322
336
|
|
337
|
+
```html
|
323
338
|
<input type="text" value="{_name}" />
|
339
|
+
```
|
324
340
|
|
325
341
|
In the example above, if _name changes, the field will update and if the field is updated, _name will be changed.
|
326
342
|
|
343
|
+
```html
|
327
344
|
<input type="checkbox" checked="{_checked}" />
|
345
|
+
```
|
328
346
|
|
329
347
|
If the value of a checked attribute is true, the checkbox will be shown checked. If it is checked/unchecked, the value will be updated to true or false.
|
330
348
|
|
@@ -337,14 +355,14 @@ If you have a controller at app/home/controller/index_controller.rb, and a view
|
|
337
355
|
When you need to use { and } outside of bindings. Anything in a triple mustache will be escaped and not processed as a binding:
|
338
356
|
|
339
357
|
```html
|
340
|
-
{{{ bindings look like: {this} }}}
|
358
|
+
{{{ bindings look like: {this} }}}
|
341
359
|
```
|
342
360
|
|
343
361
|
# Models
|
344
362
|
|
345
|
-
Volt's concept of a model is slightly different from many frameworks where a model is the name for the ORM to the database. In Volt a model is a class where you can store data easily.
|
363
|
+
Volt's concept of a model is slightly different from many frameworks where a model is the name for the ORM to the database. In Volt a model is a class where you can store data easily. Models can be created with a "Persistor", which is responsible for storing the data in the model. Models created without a persistor, simply store the data in the classes instance. Lets first see how to use a model.
|
346
364
|
|
347
|
-
Volt comes with many built-in models, one is called 'page'. If you call #page on a controller, you will get access to the model. Models provided by Volt are automatically wrapped in a ReactiveValue.
|
365
|
+
Volt comes with many built-in models, one is called 'page'. If you call #page on a controller, you will get access to the model. Models provided by Volt are automatically wrapped in a ReactiveValue so update events can be tracked.
|
348
366
|
|
349
367
|
```ruby
|
350
368
|
page._name = 'Ryan'
|
@@ -352,9 +370,9 @@ Volt comes with many built-in models, one is called 'page'. If you call #page o
|
|
352
370
|
# => @'Ryan'
|
353
371
|
```
|
354
372
|
|
355
|
-
Models act like a hash that you can access with getters and setters that start with an _ Prefixing with an underscore makes sure we don't accidentally try to call a method that doesn't exist and get back nil. There is no need to define which fields a model has, they act similar to a hash, but with a
|
373
|
+
Models act like a hash that you can access with getters and setters that start with an _ If an underscore method is called that hasn't yet been assigned, you will get back a "nil model". Prefixing with an underscore makes sure we don't accidentally try to call a method that doesn't exist and get back nil model instead of raising an exception. There is no need to define which fields a model has, they act similar to a hash, but with a different access and assign syntax.
|
356
374
|
|
357
|
-
Models also let you nest data:
|
375
|
+
Models also let you nest data without creating the intermediate models:
|
358
376
|
|
359
377
|
```ruby
|
360
378
|
page._settings._color = 'blue'
|
@@ -367,7 +385,7 @@ Models also let you nest data:
|
|
367
385
|
|
368
386
|
Nested data is automatically setup when assigned. In this case, page._settings is a model that is part of the page model.
|
369
387
|
|
370
|
-
You can also append to a model if its not defined yet.
|
388
|
+
You can also append to a model if its not defined yet. In Volt models, plural properties are assumed to contain arrays (or more specifically ArrayModels)
|
371
389
|
|
372
390
|
```ruby
|
373
391
|
page._items << 'item 1'
|
@@ -378,18 +396,18 @@ You can also append to a model if its not defined yet.
|
|
378
396
|
# => @"item 1"
|
379
397
|
```
|
380
398
|
|
381
|
-
|
399
|
+
ArrayModels can be appended to and accessed just like regular arrays.
|
382
400
|
|
383
401
|
## Provided Collections
|
384
402
|
|
385
|
-
Above I mentioned that Volt comes with many default collection models
|
403
|
+
Above I mentioned that Volt comes with many default collection models accessible from a controller. Each stores in a different location.
|
386
404
|
|
387
405
|
| Name | Storage Location |
|
388
406
|
|-----------|---------------------------------------------------------------------------|
|
389
407
|
| page | page provides a temporary store that only lasts for the life of the page. |
|
390
408
|
| store | store syncs the data to the backend database and provides query methods. |
|
391
409
|
| session | values will be stored in a session cookie. |
|
392
|
-
| params | values will be stored in the params and
|
410
|
+
| params | values will be stored in the params and URL. Routes can be setup to change how params are shown in the URL. (See routes for more info) |
|
393
411
|
| flash | any strings assigned will be shown at the top of the page and cleared as the user navigates between pages. |
|
394
412
|
| controller| a model for the current controller |
|
395
413
|
|
@@ -397,7 +415,7 @@ Above I mentioned that Volt comes with many default collection models accessable
|
|
397
415
|
|
398
416
|
## Reactive Models
|
399
417
|
|
400
|
-
Because all models provided by Volt are wrapped in a ReactiveValue, you can register listeners on them and be updated when values change. You can also call methods on their values and get updates when the source's change. Bindings also setup listeners. Models should be the main place you store all data in Volt. While you can use ReactiveValue's manually, most of the time you will want to just use something like the
|
418
|
+
Because all models provided by Volt are wrapped in a ReactiveValue, you can register listeners on them and be updated when values change. You can also call methods on their values and get updates when the source's change. Bindings also setup listeners. Models should be the main place you store all data in Volt. While you can use ReactiveValue's manually, most of the time you will want to just use something like the controller model.
|
401
419
|
|
402
420
|
## Model Events
|
403
421
|
|
@@ -423,7 +441,7 @@ Models trigger events when their data is updated. Currently models emit three e
|
|
423
441
|
|
424
442
|
### Hash -> Model
|
425
443
|
|
426
|
-
For
|
444
|
+
For convenience, when placing a hash inside of another model, it is automatically converted into a model. Models are similar to hashes, but provide support for things like persistence and triggering reactive events.
|
427
445
|
|
428
446
|
```ruby
|
429
447
|
user = Model.new
|
@@ -482,7 +500,7 @@ You can get a normal array again by calling .to_a on an ArrayModel.
|
|
482
500
|
|
483
501
|
# Controllers
|
484
502
|
|
485
|
-
A controller can be any class in Volt, however it is common to have that class inherit from ModelController. A model controller lets you specify a model that the controller works off of. This is a common pattern in Volt.
|
503
|
+
A controller can be any class in Volt, however it is common to have that class inherit from ModelController. A model controller lets you specify a model that the controller works off of. This is a common pattern in Volt. The model for a controller can be assigned by one of the following:
|
486
504
|
|
487
505
|
1. A symbol representing the name of a provided collection model:
|
488
506
|
|
@@ -494,21 +512,25 @@ A controller can be any class in Volt, however it is common to have that class i
|
|
494
512
|
end
|
495
513
|
```
|
496
514
|
|
497
|
-
|
515
|
+
2. Calling self.method = in a method:
|
498
516
|
|
499
517
|
```ruby
|
500
518
|
class TodosController < ModelController
|
501
519
|
def initialize
|
502
|
-
model :page
|
520
|
+
self.model = :page
|
503
521
|
end
|
504
522
|
end
|
505
523
|
```
|
506
524
|
|
525
|
+
In methods, the #model method returns the current model.
|
526
|
+
|
507
527
|
See the [provided collections](#provided-collections) section for a list of the available collection models.
|
508
528
|
|
509
529
|
You can also provide your own object to model.
|
510
530
|
|
511
|
-
|
531
|
+
In the example above any methods not defined on the TodosController will fall through to the provided model. All views in views/{controller_name} will have this controller as the target for any ruby run in their bindings. This means that calls on self (implicit or with self.) will have the model as their target (after calling through the controller). This lets you add methods to the controller to control how the model is handled, or provide extra methods to the views.
|
532
|
+
|
533
|
+
Volt is more similar to an MVVM architecture than an MVC architecture. Instead of the controllers passing data off to the views, the controllers are the context for the views. When using a ModelController, the controller automatically forwards all methods it does not handle to the model. This is convienant since you can set a model in the controller and then access its properties directly with methods in bindings. This lets you do something like ```{_name}``` instead of something like ```{@model._name}```
|
512
534
|
|
513
535
|
Controllers in the app/home component do not need to be namespaced, all other components should namespace controllers like so:
|
514
536
|
|
@@ -524,9 +546,11 @@ Here "auth" would be the component name.
|
|
524
546
|
|
525
547
|
# Components
|
526
548
|
|
527
|
-
Apps are made up of Components. Each folder under app/ is a component. When you visit a route, it loads all of the files in the component on the front end, so new pages within the component can be rendered
|
549
|
+
Apps are made up of Components. Each folder under app/ is a component. When you visit a route, it loads all of the files in the component on the front end, so new pages within the component can be rendered without a new http request. If a URL is visited that routes to a different component, the request will be loaded as a normal page load and all of that components files will be loaded. You can think of components as the "reload boundary" between sections of your app.
|
528
550
|
|
529
|
-
|
551
|
+
## Dependencies
|
552
|
+
|
553
|
+
You can also use controls (see below) from one component in another. To do this, you must require the component from the component you wish to use them in. This can be done in the ```config/dependencies.rb``` file. Just put
|
530
554
|
|
531
555
|
```ruby
|
532
556
|
component 'component_name'
|
@@ -549,13 +573,15 @@ Note above though that jquery and bootstrap are currently included by default.
|
|
549
573
|
|
550
574
|
**Note, asset management is still early, and likely will change quite a bit**
|
551
575
|
|
552
|
-
In volt, assets such as JavaScript and CSS (or sass) are automatically included on the page for you. Anything placed inside of a components asset folder is served at /assets (via [Sprockets](https://github.com/sstephenson/sprockets)) Link and script tags are automatically added for each css and js file in assets/css and assets/js respectively. Files are included in their lexical order, so you can add numbers in front if you need to change the load order.
|
576
|
+
In volt, assets such as JavaScript and CSS (or sass) are automatically included on the page for you. Anything placed inside of a components asset/js or assets/css folder is served at /assets/{js,css} (via [Sprockets](https://github.com/sstephenson/sprockets)) Link and script tags are automatically added for each css and js file in assets/css and assets/js respectively. Files are included in their lexical order, so you can add numbers in front if you need to change the load order.
|
553
577
|
|
554
578
|
Any JS/CSS from an included component or component gem will be included as well. By default [bootstrap](http://getbootstrap.com/) is provided by the volt-bootstrap gem.
|
555
579
|
|
580
|
+
**Note: asset bundling is on the TODO list**
|
581
|
+
|
556
582
|
## Component Generator
|
557
583
|
|
558
|
-
Components can easily be shared as a gem. Volt provides a scaffold for component gems. In a folder (not in a volt project), simply type: volt gem {component_name} This will create the files needed for the gem. Note that all volt component gems will be prefixed with volt- so they can easily be found by others.
|
584
|
+
Components can easily be shared as a gem. Volt provides a scaffold for component gems. In a folder (not in a volt project), simply type: volt gem {component_name} This will create the files needed for the gem. Note that all volt component gems will be prefixed with volt- so they can easily be found by others on github and rubygems.
|
559
585
|
|
560
586
|
While developing, you can use the component by placing the following in your Gemfile:
|
561
587
|
|
@@ -638,9 +664,9 @@ Failing above, Volt will look for a view folder with the control name, and an in
|
|
638
664
|
Next, all folders under app/ are checked. The view path looked for is {component}/index/index.html with a section of :body.
|
639
665
|
|
640
666
|
5. gems
|
641
|
-
Lastly the app folder of all gems that start with volt are checked. They are
|
667
|
+
Lastly the app folder of all gems that start with volt are checked. They are checked for a similar path to component.
|
642
668
|
|
643
|
-
When you create a control, you can also specify multiple parts of the search path in the name. The parts should be
|
669
|
+
When you create a control, you can also specify multiple parts of the search path in the name. The parts should be separated by a : Example:
|
644
670
|
|
645
671
|
```html
|
646
672
|
<:blog:comments />
|
@@ -655,28 +681,45 @@ The above would search the following:
|
|
655
681
|
| blog | comments | index.html | :body |
|
656
682
|
| gems/blog | comments | index.html | :body |
|
657
683
|
|
658
|
-
|
684
|
+
Once the view file for the control or template is found, it will look for a matching controller. If the control is specified as a local template, an empty ModelController will be used. If a controller is found and loaded, a corrosponding "action" method will be called on it if its exists. Action methods default to "index" unless the component or template path has two parts, in which case the last part is the action.
|
685
|
+
|
686
|
+
# Control Arguments/Attributes
|
687
|
+
|
688
|
+
Like other html tags, controls can be passed attributes. These are then converted into a hash and passed as the first argument to the initialize method on the controller. The standard ModelController's initialize will then assign each key/value in the attributes hash as instance values. This makes it easy to access attributes passed in.
|
689
|
+
|
690
|
+
```html
|
691
|
+
|
692
|
+
<:Body>
|
693
|
+
|
694
|
+
<ul>
|
695
|
+
{#each _todos as todo}
|
696
|
+
<:todo name="{todo._name}" />
|
697
|
+
{/}
|
698
|
+
</ul>
|
699
|
+
|
700
|
+
<:Todo>
|
701
|
+
<li>{@name}</li>
|
659
702
|
|
660
|
-
|
703
|
+
```
|
661
704
|
|
662
705
|
|
663
706
|
# Routes
|
664
707
|
|
665
|
-
Routes in Volt are very different from traditional backend frameworks. Since data is synchronized using websockets, routes are mainly used to serialize the state of the application in a pretty way. When a page is first loaded, the
|
708
|
+
Routes in Volt are very different from traditional backend frameworks. Since data is synchronized using websockets, routes are mainly used to serialize the state of the application into the url in a pretty way. When a page is first loaded, the URL is parsed with the routes and the params model's values are set from the URL. Later if the params model is updated, the URL is updated based on the routes.
|
666
709
|
|
667
|
-
This means that routes in
|
710
|
+
This means that routes in Volt have to be able to go both from URL to params and params to URL. It should also be noted that if a link is clicked and the controller/view to render the new URL is within the current component (or an included component), the page will not be reloaded, the URL will be updated with the HTML5 history API, and the params hash will reflect the new URL. You can use the changes in params to render different views based on the URL.
|
668
711
|
|
669
712
|
## Routes file
|
670
713
|
|
671
|
-
Routes are specified on a per-component basis in the config/routes.rb file. Routes simply map from
|
714
|
+
Routes are specified on a per-component basis in the config/routes.rb file. Routes simply map from URL to params.
|
672
715
|
|
673
716
|
```ruby
|
674
717
|
get "/todos", _view: 'todos'
|
675
718
|
```
|
676
719
|
|
677
|
-
Routes take two arguments, a path, and a params hash. When a new
|
720
|
+
Routes take two arguments, a path, and a params hash. When a new URL is loaded and the path is matched on a route, the params will be set to the params provided for that route.
|
678
721
|
|
679
|
-
When the params are changed, the
|
722
|
+
When the params are changed, the URL will be set to the path for the route that's params hash matches.
|
680
723
|
|
681
724
|
Route path's can also contain variables similar to bindings.
|
682
725
|
|
@@ -684,7 +727,7 @@ Route path's can also contain variables similar to bindings.
|
|
684
727
|
get "/todos/{_index}", _view: 'todos'
|
685
728
|
```
|
686
729
|
|
687
|
-
In the case above, if any
|
730
|
+
In the case above, if any URL matches /todos/*, (where * is anything but a slash), it will be the active route. ```params._view``` would be set to 'todos', and ```params._index``` would be set to the value in the path.
|
688
731
|
|
689
732
|
If ```params._view``` is 'todos' and ```params._index``` is not nil, the route would be matched.
|
690
733
|
|
@@ -719,10 +762,9 @@ TODO
|
|
719
762
|
|
720
763
|
# Data Store
|
721
764
|
|
722
|
-
Volt provides a data store collection on the front-end and the back-end.
|
765
|
+
Volt provides a data store collection on the front-end and the back-end. In store, all plural names are assumed to be collections (like an array), and all singular are assumed to be a model (like a hash).
|
723
766
|
|
724
767
|
```ruby
|
725
|
-
|
726
768
|
store._things
|
727
769
|
```
|
728
770
|
|
@@ -735,7 +777,6 @@ store._things
|
|
735
777
|
| loaded | yes | data is loaded and there is an event bound |
|
736
778
|
| dirty | no | data was either accessed without binding an event, or an event was bound, but later unbound. |
|
737
779
|
|
780
|
+
# Contributing
|
738
781
|
|
739
|
-
#
|
740
|
-
|
741
|
-
- only the relevant dom is updated. There is no match and patch algorithm to update from strings like other frameworks, all associations are tracked through our reactive values, so we know exactly what needs to be updated without the need to generate any extra html. This has a few advantages, namely that things like input fields are retained, so any properties (focus, tab position, etc...) are also retained.
|
782
|
+
You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on #voltrb on freenode (irc) so we don't duplicate work. Pull requests are always welcome, but asking about helping on IRC should save some duplication.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.2
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/* SockJS client, version 0.3.4, http://sockjs.org, MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2011-2012 VMware, Inc.
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
22
|
+
*/
|
23
|
+
|
24
|
+
// JSON2 by Douglas Crockford (minified).
|
25
|
+
var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g;return e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g;return e}}function quote(a){escapable.lastIndex=0;return escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function f(a){return a<10?"0"+a:a}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver=="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")})}()
|
26
|
+
|
27
|
+
SockJS=function(){var a=document,b=window,c={},d=function(){};d.prototype.addEventListener=function(a,b){this._listeners||(this._listeners={}),a in this._listeners||(this._listeners[a]=[]);var d=this._listeners[a];c.arrIndexOf(d,b)===-1&&d.push(b);return},d.prototype.removeEventListener=function(a,b){if(!(this._listeners&&a in this._listeners))return;var d=this._listeners[a],e=c.arrIndexOf(d,b);if(e!==-1){d.length>1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d<this._listeners[b].length;d++)this._listeners[b][d].apply(this,c)};var e=function(a,b){this.type=a;if(typeof b!="undefined")for(var c in b){if(!b.hasOwnProperty(c))continue;this[c]=b[c]}};e.prototype.toString=function(){var a=[];for(var b in this){if(!this.hasOwnProperty(b))continue;var c=this[b];typeof c=="function"&&(c="[function]"),a.push(b+"="+c)}return"SimpleEvent("+a.join(", ")+")"};var f=function(a){var b=this;b._events=a||[],b._listeners={}};f.prototype.emit=function(a){var b=this;b._verifyType(a);if(b._nuked)return;var c=Array.prototype.slice.call(arguments,1);b["on"+a]&&b["on"+a].apply(b,c);if(a in b._listeners)for(var d=0;d<b._listeners[a].length;d++)b._listeners[a][d].apply(b,c)},f.prototype.on=function(a,b){var c=this;c._verifyType(a);if(c._nuked)return;a in c._listeners||(c._listeners[a]=[]),c._listeners[a].push(b)},f.prototype._verifyType=function(a){var b=this;c.arrIndexOf(b._events,a)===-1&&c.log("Event "+JSON.stringify(a)+" not listed "+JSON.stringify(b._events)+" in "+b)},f.prototype.nuke=function(){var a=this;a._nuked=!0;for(var b=0;b<a._events.length;b++)delete a[a._events[b]];a._listeners={}};var g="abcdefghijklmnopqrstuvwxyz0123456789_";c.random_string=function(a,b){b=b||g.length;var c,d=[];for(c=0;c<a;c++)d.push(g.substr(Math.floor(Math.random()*b),1));return d.join("")},c.random_number=function(a){return Math.floor(Math.random()*a)},c.random_number_string=function(a){var b=(""+(a-1)).length,d=Array(b+1).join("0");return(d+c.random_number(a)).slice(-b)},c.getOrigin=function(a){a+="/";var b=a.split("/").slice(0,3);return b.join("/")},c.isSameOriginUrl=function(a,c){return c||(c=b.location.href),a.split("/").slice(0,3).join("/")===c.split("/").slice(0,3).join("/")},c.getParentDomain=function(a){if(/^[0-9.]*$/.test(a))return a;if(/^\[/.test(a))return a;if(!/[.]/.test(a))return a;var b=a.split(".").slice(1);return b.join(".")},c.objectExtend=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a};var h="_jp";c.polluteGlobalNamespace=function(){h in b||(b[h]={})},c.closeFrame=function(a,b){return"c"+JSON.stringify([a,b])},c.userSetCode=function(a){return a===1e3||a>=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.flatUrl=function(a){return a.indexOf("?")===-1&&a.indexOf("#")===-1},c.amendUrl=function(b){var d=a.location;if(!b)throw new Error("Wrong url for SockJS");if(!c.flatUrl(b))throw new Error("Only basic urls are supported in SockJS");return b.indexOf("//")===0&&(b=d.protocol+b),b.indexOf("/")===0&&(b=d.protocol+"//"+d.host+b),b=b.replace(/[/]+$/,""),b},c.arrIndexOf=function(a,b){for(var c=0;c<a.length;c++)if(a[c]===b)return c;return-1},c.arrSkip=function(a,b){var d=c.arrIndexOf(a,b);if(d===-1)return a.slice();var e=a.slice(0,d);return e.concat(a.slice(d+1))},c.isArray=Array.isArray||function(a){return{}.toString.call(a).indexOf("Array")>=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b<o.length;b++){var c=o[b];a[c]=y[c]&&y[c].enabled()}return a},c.detectProtocols=function(a,b,c){var d={},e=[];b||(b=o);for(var f=0;f<b.length;f++){var g=b[f];d[g]=a[g]}var h=function(a){var b=a.shift();d[b]?e.push(b):a.length>0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xhr-streaming"]&&!c.null_origin?e.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-streaming"):h(["iframe-eventsource","iframe-htmlfile"]),d["xhr-polling"]&&!c.null_origin?e.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-polling"):h(["iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={},r=!1,s=function(){for(var a in q)q[a](),delete q[a]},t=function(){if(r)return;r=!0,s()};c.attachEvent("unload",t),c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,r&&c.delay(s),b},c.unload_del=function(a){a in q&&delete q[a]},c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g,h=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},i=function(){e&&(h(),setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.unload_del(g))},j=function(a){e&&(i(),d(a))},k=function(a,b){try{e&&e.contentWindow&&e.contentWindow.postMessage(a,b)}catch(c){}};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){j("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){j("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){j("timeout")},15e3),g=c.unload_add(i),{post:k,cleanup:i,loaded:h}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i,j=function(){clearTimeout(f)},k=function(){e&&(j(),c.unload_del(g),i.parentNode.removeChild(i),i=e=null,CollectGarbage())},l=function(a){e&&(k(),d(a))},m=function(a,b){try{i&&i.contentWindow&&i.contentWindow.postMessage(a,b)}catch(c){}};e.open(),e.write('<html><script>document.domain="'+document.domain+'";'+"</s"+"cript></html>"),e.close(),e.parentWindow[h]=b[h];var n=e.createElement("div");return e.body.appendChild(n),i=e.createElement("iframe"),n.appendChild(i),i.src=a,f=setTimeout(function(){l("timeout")},15e3),g=c.unload_add(k),{post:m,cleanup:k,loaded:j}};var u=function(){};u.prototype=new f(["chunk","finish"]),u.prototype._start=function(a,d,e,f){var g=this;try{g.xhr=new XMLHttpRequest}catch(h){}if(!g.xhr)try{g.xhr=new b.ActiveXObject("Microsoft.XMLHTTP")}catch(h){}if(b.ActiveXObject||b.XDomainRequest)d+=(d.indexOf("?")===-1?"?":"&")+"t="+ +(new Date);g.unload_ref=c.unload_add(function(){g._cleanup(!0)});try{g.xhr.open(a,d,!0)}catch(i){g.emit("finish",0,""),g._cleanup();return}if(!f||!f.no_credentials)g.xhr.withCredentials="true";if(f&&f.headers)for(var j in f.headers)g.xhr.setRequestHeader(j,f.headers[j]);g.xhr.onreadystatechange=function(){if(g.xhr){var a=g.xhr;switch(a.readyState){case 3:try{var b=a.status,c=a.responseText}catch(a){}b===1223&&(b=204),c&&c.length>0&&g.emit("chunk",b,c);break;case 4:var b=a.status;b===1223&&(b=204),g.emit("finish",b,a.responseText),g._cleanup(!1)}}},g.xhr.send(e)},u.prototype._cleanup=function(a){var b=this;if(!b.xhr)return;c.unload_del(b.unload_ref),b.xhr.onreadystatechange=function(){};if(a)try{b.xhr.abort()}catch(d){}b.unload_ref=b.xhr=null},u.prototype.close=function(){var a=this;a.nuke(),a._cleanup(!0)};var v=c.XHRCorsObject=function(){var a=this,b=arguments;c.delay(function(){a._start.apply(a,b)})};v.prototype=new u;var w=c.XHRLocalObject=function(a,b,d){var e=this;c.delay(function(){e._start(a,b,d,{no_credentials:!0})})};w.prototype=new u;var x=c.XDRObject=function(a,b,d){var e=this;c.delay(function(){e._start(a,b,d)})};x.prototype=new f(["chunk","finish"]),x.prototype._start=function(a,b,d){var e=this,f=new XDomainRequest;b+=(b.indexOf("?")===-1?"?":"&")+"t="+ +(new Date);var g=f.ontimeout=f.onerror=function(){e.emit("finish",0,""),e._cleanup(!1)};f.onprogress=function(){e.emit("chunk",200,f.responseText)},f.onload=function(){e.emit("finish",200,f.responseText),e._cleanup(!1)},e.xdr=f,e.unload_ref=c.unload_add(function(){e._cleanup(!0)});try{e.xdr.open(a,b),e.xdr.send(d)}catch(h){g()}},x.prototype._cleanup=function(a){var b=this;if(!b.xdr)return;c.unload_del(b.unload_ref),b.xdr.ontimeout=b.xdr.onerror=b.xdr.onprogress=b.xdr.onload=null;if(a)try{b.xdr.abort()}catch(d){}b.unload_ref=b.xdr=null},x.prototype.close=function(){var a=this;a.nuke(),a._cleanup(!0)},c.isXHRCorsCapable=function(){return b.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest?1:b.XDomainRequest&&a.domain?2:L.enabled()?3:4};var y=function(a,d,e){if(this===b)return new y(a,d,e);var f=this,g;f._options={devel:!1,debug:!1,protocols_whitelist:[],info:undefined,rtt:undefined},e&&c.objectExtend(f._options,e),f._base_url=c.amendUrl(a),f._server=f._options.server||c.random_number_string(1e3),f._options.protocols_whitelist&&f._options.protocols_whitelist.length?g=f._options.protocols_whitelist:(typeof d=="string"&&d.length>0?g=[d]:c.isArray(d)?g=d:g=null,g&&f._debug('Deprecated API: Use "protocols_whitelist" option instead of supplying protocol list as a second parameter to SockJS constructor.')),f._protocols=[],f.protocol=null,f.readyState=y.CONNECTING,f._ir=S(f._base_url),f._ir.onfinish=function(a,b){f._ir=null,a?(f._options.info&&(a=c.objectExtend(a,f._options.info)),f._options.rtt&&(b=f._options.rtt),f._applyInfo(a,b,g),f._didClose()):f._didClose(1002,"Can't connect to server",!0)}};y.prototype=new d,y.version="0.3.4",y.CONNECTING=0,y.OPEN=1,y.CLOSING=2,y.CLOSED=3,y.prototype._debug=function(){this._options.debug&&c.log.apply(c,arguments)},y.prototype._dispatchOpen=function(){var a=this;a.readyState===y.CONNECTING?(a._transport_tref&&(clearTimeout(a._transport_tref),a._transport_tref=null),a.readyState=y.OPEN,a.dispatchEvent(new e("open"))):a._didClose(1006,"Server lost session")},y.prototype._dispatchMessage=function(a){var b=this;if(b.readyState!==y.OPEN)return;b.dispatchEvent(new e("message",{data:a}))},y.prototype._dispatchHeartbeat=function(a){var b=this;if(b.readyState!==y.OPEN)return;b.dispatchEvent(new e("heartbeat",{}))},y.prototype._didClose=function(a,b,d){var f=this;if(f.readyState!==y.CONNECTING&&f.readyState!==y.OPEN&&f.readyState!==y.CLOSING)throw new Error("INVALID_STATE_ERR");f._ir&&(f._ir.nuke(),f._ir=null),f._transport&&(f._transport.doCleanup(),f._transport=null);var g=new e("close",{code:a,reason:b,wasClean:c.userSetCode(a)});if(!c.userSetCode(a)&&f.readyState===y.CONNECTING&&!d){if(f._try_next_protocol(g))return;g=new e("close",{code:2e3,reason:"All transports failed",wasClean:!1,last_event:g})}f.readyState=y.CLOSED,c.delay(function(){f.dispatchEvent(g)})},y.prototype._didMessage=function(a){var b=this,c=a.slice(0,1);switch(c){case"o":b._dispatchOpen();break;case"a":var d=JSON.parse(a.slice(1)||"[]");for(var e=0;e<d.length;e++)b._dispatchMessage(d[e]);break;case"m":var d=JSON.parse(a.slice(1)||"null");b._dispatchMessage(d);break;case"c":var d=JSON.parse(a.slice(1)||"[]");b._didClose(d[0],d[1]);break;case"h":b._dispatchHeartbeat()}},y.prototype._try_next_protocol=function(b){var d=this;d.protocol&&(d._debug("Closed transport:",d.protocol,""+b),d.protocol=null),d._transport_tref&&(clearTimeout(d._transport_tref),d._transport_tref=null);for(;;){var e=d.protocol=d._protocols.shift();if(!e)return!1;if(y[e]&&y[e].need_body===!0&&(!a.body||typeof a.readyState!="undefined"&&a.readyState!=="complete"))return d._protocols.unshift(e),d.protocol="waiting-for-load",c.attachEvent("load",function(){d._try_next_protocol()}),!0;if(!!y[e]&&!!y[e].enabled(d._options)){var f=y[e].roundTrips||1,g=(d._options.rto||0)*f||5e3;d._transport_tref=c.delay(g,function(){d.readyState===y.CONNECTING&&d._didClose(2007,"Transport timeouted")});var h=c.random_string(8),i=d._base_url+"/"+d._server+"/"+h;return d._debug("Opening transport:",e," url:"+i," RTO:"+d._options.rto),d._transport=new y[e](d,i,d._base_url),!0}d._debug("Skipping transport:",e)}},y.prototype.close=function(a,b){var d=this;if(a&&!c.userSetCode(a))throw new Error("INVALID_ACCESS_ERR");return d.readyState!==y.CONNECTING&&d.readyState!==y.OPEN?!1:(d.readyState=y.CLOSING,d._didClose(a||1e3,b||"Normal closure"),!0)},y.prototype.send=function(a){var b=this;if(b.readyState===y.CONNECTING)throw new Error("INVALID_STATE_ERR");return b.readyState===y.OPEN&&b._transport.doSend(c.quote(""+a)),!0},y.prototype._applyInfo=function(b,d,e){var f=this;f._options.info=b,f._options.rtt=d,f._options.rto=c.countRTO(d),f._options.info.null_origin=!a.domain;var g=c.probeProtocols();f._protocols=c.detectProtocols(g,e,b)};var z=y.websocket=function(a,d){var e=this,f=d+"/websocket";f.slice(0,5)==="https"?f="wss"+f.slice(5):f="ws"+f.slice(4),e.ri=a,e.url=f;var g=b.WebSocket||b.MozWebSocket;e.ws=new g(e.url),e.ws.onmessage=function(a){e.ri._didMessage(a.data)},e.unload_ref=c.unload_add(function(){e.ws.close()}),e.ws.onclose=function(){e.ri._didMessage(c.closeFrame(1006,"WebSocket connection broken"))}};z.prototype.doSend=function(a){this.ws.send("["+a+"]")},z.prototype.doCleanup=function(){var a=this,b=a.ws;b&&(b.onmessage=b.onclose=null,b.close(),c.unload_del(a.unload_ref),a.unload_ref=a.ri=a.ws=null)},z.enabled=function(){return!!b.WebSocket||!!b.MozWebSocket},z.roundTrips=2;var A=function(){};A.prototype.send_constructor=function(a){var b=this;b.send_buffer=[],b.sender=a},A.prototype.doSend=function(a){var b=this;b.send_buffer.push(a),b.send_stop||b.send_schedule()},A.prototype.send_schedule_wait=function(){var a=this,b;a.send_stop=function(){a.send_stop=null,clearTimeout(b)},b=c.delay(25,function(){a.send_stop=null,a.send_schedule()})},A.prototype.send_schedule=function(){var a=this;if(a.send_buffer.length>0){var b="["+a.send_buffer.join(",")+"]";a.send_stop=a.sender(a.trans_url,b,function(b,c){a.send_stop=null,b===!1?a.ri._didClose(1006,"Sending error "+c):a.send_schedule_wait()}),a.send_buffer=[]}},A.prototype.send_destructor=function(){var a=this;a._send_stop&&a._send_stop(),a._send_stop=null};var B=function(b,d,e){var f=this;if(!("_send_form"in f)){var g=f._send_form=a.createElement("form"),h=f._send_area=a.createElement("textarea");h.name="d",g.style.display="none",g.style.position="absolute",g.method="POST",g.enctype="application/x-www-form-urlencoded",g.acceptCharset="UTF-8",g.appendChild(h),a.body.appendChild(g)}var g=f._send_form,h=f._send_area,i="a"+c.random_string(8);g.target=i,g.action=b+"/jsonp_send?i="+i;var j;try{j=a.createElement('<iframe name="'+i+'">')}catch(k){j=a.createElement("iframe"),j.name=i}j.id=i,g.appendChild(j),j.style.display="none";try{h.value=d}catch(l){c.log("Your browser is seriously broken. Go home! "+l.message)}g.submit();var m=function(a){if(!j.onerror)return;j.onreadystatechange=j.onerror=j.onload=null,c.delay(500,function(){j.parentNode.removeChild(j),j=null}),h.value="",e(!0)};return j.onerror=j.onload=m,j.onreadystatechange=function(a){j.readyState=="complete"&&m()},m},C=function(a){return function(b,c,d){var e=new a("POST",b+"/xhr_send",c);return e.onfinish=function(a,b){d(a===200||a===204,"http status "+a)},function(a){d(!1,a)}}},D=function(b,d){var e,f=a.createElement("script"),g,h=function(a){g&&(g.parentNode.removeChild(g),g=null),f&&(clearTimeout(e),f.parentNode.removeChild(f),f.onreadystatechange=f.onerror=f.onload=f.onclick=null,f=null,d(a),d=null)},i=!1,j=null;f.id="a"+c.random_string(8),f.src=b,f.type="text/javascript",f.charset="UTF-8",f.onerror=function(a){j||(j=setTimeout(function(){i||h(c.closeFrame(1006,"JSONP script loaded abnormally (onerror)"))},1e3))},f.onload=function(a){h(c.closeFrame(1006,"JSONP script loaded abnormally (onload)"))},f.onreadystatechange=function(a){if(/loaded|closed/.test(f.readyState)){if(f&&f.htmlFor&&f.onclick){i=!0;try{f.onclick()}catch(b){}}f&&h(c.closeFrame(1006,"JSONP script loaded abnormally (onreadystatechange)"))}};if(typeof f.async=="undefined"&&a.attachEvent)if(!/opera/i.test(navigator.userAgent)){try{f.htmlFor=f.id,f.event="onclick"}catch(k){}f.async=!0}else g=a.createElement("script"),g.text="try{var a = document.getElementById('"+f.id+"'); if(a)a.onerror();}catch(x){};",f.async=g.async=!1;typeof f.async!="undefined"&&(f.async=!0),e=setTimeout(function(){h(c.closeFrame(1006,"JSONP script loaded abnormally (timeout)"))},35e3);var l=a.getElementsByTagName("head")[0];return l.insertBefore(f,l.firstChild),g&&l.insertBefore(g,l.firstChild),h},E=y["jsonp-polling"]=function(a,b){c.polluteGlobalNamespace();var d=this;d.ri=a,d.trans_url=b,d.send_constructor(B),d._schedule_recv()};E.prototype=new A,E.prototype._schedule_recv=function(){var a=this,b=function(b){a._recv_stop=null,b&&(a._is_closing||a.ri._didMessage(b)),a._is_closing||a._schedule_recv()};a._recv_stop=F(a.trans_url+"/jsonp",D,b)},E.enabled=function(){return!0},E.need_body=!0,E.prototype.doCleanup=function(){var a=this;a._is_closing=!0,a._recv_stop&&a._recv_stop(),a.ri=a._recv_stop=null,a.send_destructor()};var F=function(a,d,e){var f="a"+c.random_string(6),g=a+"?c="+escape(h+"."+f),i=0,j=function(a){switch(i){case 0:delete b[h][f],e(a);break;case 1:e(a),i=2;break;case 2:delete b[h][f]}},k=d(g,j);b[h][f]=k;var l=function(){b[h][f]&&(i=1,b[h][f](c.closeFrame(1e3,"JSONP user aborted read")))};return l},G=function(){};G.prototype=new A,G.prototype.run=function(a,b,c,d,e){var f=this;f.ri=a,f.trans_url=b,f.send_constructor(C(e)),f.poll=new $(a,d,b+c,e)},G.prototype.doCleanup=function(){var a=this;a.poll&&(a.poll.abort(),a.poll=null)};var H=y["xhr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",bd,c.XHRCorsObject)};H.prototype=new G,H.enabled=function(){return b.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest&&!/opera/i.test(navigator.userAgent)},H.roundTrips=2,H.need_body=!0;var I=y["xdr-streaming"]=function(a,b){this.run(a,b,"/xhr_streaming",bd,c.XDRObject)};I.prototype=new G,I.enabled=function(){return!!b.XDomainRequest},I.roundTrips=2;var J=y["xhr-polling"]=function(a,b){this.run(a,b,"/xhr",bd,c.XHRCorsObject)};J.prototype=new G,J.enabled=H.enabled,J.roundTrips=2;var K=y["xdr-polling"]=function(a,b){this.run(a,b,"/xhr",bd,c.XDRObject)};K.prototype=new G,K.enabled=I.enabled,K.roundTrips=2;var L=function(){};L.prototype.i_constructor=function(a,b,d){var e=this;e.ri=a,e.origin=c.getOrigin(d),e.base_url=d,e.trans_url=b;var f=d+"/iframe.html";e.ri._options.devel&&(f+="?t="+ +(new Date)),e.window_id=c.random_string(8),f+="#"+e.window_id,e.iframeObj=c.createIframe(f,function(a){e.ri._didClose(1006,"Unable to load an iframe ("+a+")")}),e.onmessage_cb=c.bind(e.onmessage,e),c.attachMessage(e.onmessage_cb)},L.prototype.doCleanup=function(){var a=this;if(a.iframeObj){c.detachMessage(a.onmessage_cb);try{a.iframeObj.iframe.contentWindow&&a.postMessage("c")}catch(b){}a.iframeObj.cleanup(),a.iframeObj=null,a.onmessage_cb=a.iframeObj=null}},L.prototype.onmessage=function(a){var b=this;if(a.origin!==b.origin)return;var c=a.data.slice(0,8),d=a.data.slice(8,9),e=a.data.slice(9);if(c!==b.window_id)return;switch(d){case"s":b.iframeObj.loaded(),b.postMessage("s",JSON.stringify([y.version,b.protocol,b.trans_url,b.base_url]));break;case"t":b.ri._didMessage(e)}},L.prototype.postMessage=function(a,b){var c=this;c.iframeObj.post(c.window_id+a+(b||""),c.origin)},L.prototype.doSend=function(a){this.postMessage("m",a)},L.enabled=function(){var a=navigator&&navigator.userAgent&&navigator.userAgent.indexOf("Konqueror")!==-1;return(typeof b.postMessage=="function"||typeof b.postMessage=="object")&&!a};var M,N=function(a,d){parent!==b?parent.postMessage(M+a+(d||""),"*"):c.log("Can't postMessage, no parent window.",a,d)},O=function(){};O.prototype._didClose=function(a,b){N("t",c.closeFrame(a,b))},O.prototype._didMessage=function(a){N("t",a)},O.prototype._doSend=function(a){this._transport.doSend(a)},O.prototype._doCleanup=function(){this._transport.doCleanup()},c.parent_origin=undefined,y.bootstrap_iframe=function(){var d;M=a.location.hash.slice(1);var e=function(a){if(a.source!==parent)return;typeof c.parent_origin=="undefined"&&(c.parent_origin=a.origin);if(a.origin!==c.parent_origin)return;var e=a.data.slice(0,8),f=a.data.slice(8,9),g=a.data.slice(9);if(e!==M)return;switch(f){case"s":var h=JSON.parse(g),i=h[0],j=h[1],k=h[2],l=h[3];i!==y.version&&c.log('Incompatibile SockJS! Main site uses: "'+i+'", the iframe:'+' "'+y.version+'".');if(!c.flatUrl(k)||!c.flatUrl(l)){c.log("Only basic urls are supported in SockJS");return}if(!c.isSameOriginUrl(k)||!c.isSameOriginUrl(l)){c.log("Can't connect to different domain from within an iframe. ("+JSON.stringify([b.location.href,k,l])+")");return}d=new O,d._transport=new O[j](d,k,l);break;case"m":d._doSend(g);break;case"c":d&&d._doCleanup(),d=null}};c.attachMessage(e),N("s")};var P=function(a,b){var d=this;c.delay(function(){d.doXhr(a,b)})};P.prototype=new f(["finish"]),P.prototype.doXhr=function(a,b){var d=this,e=(new Date).getTime(),f=new b("GET",a+"/info"),g=c.delay(8e3,function(){f.ontimeout()});f.onfinish=function(a,b){clearTimeout(g),g=null;if(a===200){var c=(new Date).getTime()-e,f=JSON.parse(b);typeof f!="object"&&(f={}),d.emit("finish",f,c)}else d.emit("finish")},f.ontimeout=function(){f.close(),d.emit("finish")}};var Q=function(b){var d=this,e=function(){var a=new L;a.protocol="w-iframe-info-receiver";var c=function(b){if(typeof b=="string"&&b.substr(0,1)==="m"){var c=JSON.parse(b.substr(1)),e=c[0],f=c[1];d.emit("finish",e,f)}else d.emit("finish");a.doCleanup(),a=null},e={_options:{},_didClose:c,_didMessage:c};a.i_constructor(e,b,b)};a.body?e():c.attachEvent("load",e)};Q.prototype=new f(["finish"]);var R=function(){var a=this;c.delay(function(){a.emit("finish",{},2e3)})};R.prototype=new f(["finish"]);var S=function(a){if(c.isSameOriginUrl(a))return new P(a,c.XHRLocalObject);switch(c.isXHRCorsCapable()){case 1:return new P(a,c.XHRLocalObject);case 2:return new P(a,c.XDRObject);case 3:return new Q(a);default:return new R}},T=O["w-iframe-info-receiver"]=function(a,b,d){var e=new P(d,c.XHRLocalObject);e.onfinish=function(b,c){a._didMessage("m"+JSON.stringify([b,c])),a._didClose()}};T.prototype.doCleanup=function(){};var U=y["iframe-eventsource"]=function(){var a=this;a.protocol="w-iframe-eventsource",a.i_constructor.apply(a,arguments)};U.prototype=new L,U.enabled=function(){return"EventSource"in b&&L.enabled()},U.need_body=!0,U.roundTrips=3;var V=O["w-iframe-eventsource"]=function(a,b){this.run(a,b,"/eventsource",_,c.XHRLocalObject)};V.prototype=new G;var W=y["iframe-xhr-polling"]=function(){var a=this;a.protocol="w-iframe-xhr-polling",a.i_constructor.apply(a,arguments)};W.prototype=new L,W.enabled=function(){return b.XMLHttpRequest&&L.enabled()},W.need_body=!0,W.roundTrips=3;var X=O["w-iframe-xhr-polling"]=function(a,b){this.run(a,b,"/xhr",bd,c.XHRLocalObject)};X.prototype=new G;var Y=y["iframe-htmlfile"]=function(){var a=this;a.protocol="w-iframe-htmlfile",a.i_constructor.apply(a,arguments)};Y.prototype=new L,Y.enabled=function(){return L.enabled()},Y.need_body=!0,Y.roundTrips=3;var Z=O["w-iframe-htmlfile"]=function(a,b){this.run(a,b,"/htmlfile",bc,c.XHRLocalObject)};Z.prototype=new G;var $=function(a,b,c,d){var e=this;e.ri=a,e.Receiver=b,e.recv_url=c,e.AjaxObject=d,e._scheduleRecv()};$.prototype._scheduleRecv=function(){var a=this,b=a.poll=new a.Receiver(a.recv_url,a.AjaxObject),c=0;b.onmessage=function(b){c+=1,a.ri._didMessage(b.data)},b.onclose=function(c){a.poll=b=b.onmessage=b.onclose=null,a.poll_is_closing||(c.reason==="permanent"?a.ri._didClose(1006,"Polling error ("+c.reason+")"):a._scheduleRecv())}},$.prototype.abort=function(){var a=this;a.poll_is_closing=!0,a.poll&&a.poll.abort()};var _=function(a){var b=this,d=new EventSource(a);d.onmessage=function(a){b.dispatchEvent(new e("message",{data:unescape(a.data)}))},b.es_close=d.onerror=function(a,f){var g=f?"user":d.readyState!==2?"network":"permanent";b.es_close=d.onmessage=d.onerror=null,d.close(),d=null,c.delay(200,function(){b.dispatchEvent(new e("close",{reason:g}))})}};_.prototype=new d,_.prototype.abort=function(){var a=this;a.es_close&&a.es_close({},!0)};var ba,bb=function(){if(ba===undefined)if("ActiveXObject"in b)try{ba=!!(new ActiveXObject("htmlfile"))}catch(a){}else ba=!1;return ba},bc=function(a){var d=this;c.polluteGlobalNamespace(),d.id="a"+c.random_string(6,26),a+=(a.indexOf("?")===-1?"?":"&")+"c="+escape(h+"."+d.id);var f=bb()?c.createHtmlfile:c.createIframe,g;b[h][d.id]={start:function(){g.loaded()},message:function(a){d.dispatchEvent(new e("message",{data:a}))},stop:function(){d.iframe_close({},"network")}},d.iframe_close=function(a,c){g.cleanup(),d.iframe_close=g=null,delete b[h][d.id],d.dispatchEvent(new e("close",{reason:c}))},g=f(a,function(a){d.iframe_close({},"permanent")})};bc.prototype=new d,bc.prototype.abort=function(){var a=this;a.iframe_close&&a.iframe_close({},"user")};var bd=function(a,b){var c=this,d=0;c.xo=new b("POST",a,null),c.xo.onchunk=function(a,b){if(a!==200)return;for(;;){var f=b.slice(d),g=f.indexOf("\n");if(g===-1)break;d+=g+1;var h=f.slice(0,g);c.dispatchEvent(new e("message",{data:h}))}},c.xo.onfinish=function(a,b){c.xo.onchunk(a,b),c.xo=null;var d=a===200?"network":"permanent";c.dispatchEvent(new e("close",{reason:d}))}};return bd.prototype=new d,bd.prototype.abort=function(){var a=this;a.xo&&(a.xo.close(),a.dispatchEvent(new e("close",{reason:"user"})),a.xo=null)},y.getUtils=function(){return c},y.getIframeTransport=function(){return L},y}(),"_sockjs_onload"in window&&setTimeout(_sockjs_onload,1),typeof define=="function"&&define.amd&&define("sockjs",[],function(){return SockJS})
|