Merge lp:~matsubara/juju-gui/tarmac-test into lp:~juju-gui/juju-gui/juju-gui-test

Proposed by Diogo Matsubara
Status: Merged
Approved by: Diogo Matsubara
Approved revision: 756
Merged at revision: 756
Proposed branch: lp:~matsubara/juju-gui/tarmac-test
Merge into: lp:~juju-gui/juju-gui/juju-gui-test
Diff against target: 14792 lines (+8357/-2851) (has conflicts)
132 files modified
.jshintignore (+1/-0)
CHANGES.yaml (+37/-6)
Makefile (+29/-11)
README (+4/-0)
app/app.js (+87/-27)
app/assets/images/non-sprites/category-app-server.svg (+124/-0)
app/assets/images/non-sprites/category-application.svg (+90/-0)
app/assets/images/non-sprites/category-cache-proxy.svg (+119/-0)
app/assets/images/non-sprites/category-database.svg (+115/-0)
app/assets/images/non-sprites/category-file-server.svg (+74/-0)
app/assets/images/non-sprites/category-misc.svg (+90/-0)
app/assets/images/non-sprites/category_icons/category-app-server.svg (+124/-0)
app/assets/images/non-sprites/category_icons/category-application.svg (+90/-0)
app/assets/images/non-sprites/category_icons/category-cache-proxy.svg (+119/-0)
app/assets/images/non-sprites/category_icons/category-database.svg (+115/-0)
app/assets/images/non-sprites/category_icons/category-file-server.svg (+74/-0)
app/assets/images/non-sprites/category_icons/category-misc.svg (+90/-0)
app/assets/javascripts/Object.observe.poly.js (+246/-0)
app/assets/javascripts/app-cookies-extension.js (+75/-0)
app/assets/javascripts/ns-routing-app-extension.js (+72/-22)
app/assets/javascripts/reconnecting-websocket.js (+10/-8)
app/assets/stylesheets/bootstrap-responsive-2.0.4.css (+0/-815)
app/assets/stylesheets/juju-bootstrap.css (+156/-0)
app/config-debug.js (+4/-0)
app/config-prod.js (+4/-0)
app/index.html (+76/-50)
app/models/browser.js (+26/-1)
app/models/charm.js (+137/-17)
app/models/handlers.js (+3/-3)
app/models/models.js (+28/-2)
app/modules-debug.js (+31/-2)
app/store/charm.js (+25/-3)
app/store/env/fakebackend.js (+51/-11)
app/store/env/go.js (+53/-0)
app/store/env/sandbox.js (+231/-1)
app/subapps/browser/browser.js (+192/-33)
app/subapps/browser/templates/browser_charm.handlebars (+26/-11)
app/subapps/browser/templates/browser_qa.handlebars (+42/-23)
app/subapps/browser/templates/editorial.handlebars (+6/-2)
app/subapps/browser/templates/sidebar.handlebars (+3/-0)
app/subapps/browser/views/charm.js (+135/-30)
app/subapps/browser/views/charmresults.js (+2/-2)
app/subapps/browser/views/editorial.js (+28/-1)
app/subapps/browser/views/view.js (+1/-1)
app/templates/browser-search.handlebars (+3/-0)
app/templates/category-icons.partial (+55/-0)
app/templates/charm-token.handlebars (+10/-1)
app/templates/charm.handlebars (+1/-0)
app/templates/notifications.handlebars (+2/-4)
app/templates/overview.handlebars (+4/-0)
app/templates/service-configuration.handlebars (+4/-0)
app/templates/service-configuration.partial (+33/-0)
app/templates/serviceOverview.handlebars (+13/-0)
app/templates/sharing-widget.handlebars (+22/-0)
app/templates/view-container.handlebars (+14/-0)
app/views/charm-panel.js (+40/-597)
app/views/databinding.js (+370/-0)
app/views/environment.js (+34/-0)
app/views/notifications.js (+1/-0)
app/views/service.js (+125/-0)
app/views/topology/importexport.js (+6/-1)
app/views/topology/landscape.js (+6/-1)
app/views/topology/panzoom.js (+34/-6)
app/views/topology/relation.js (+6/-1)
app/views/topology/service.js (+89/-6)
app/views/topology/utils.js (+37/-2)
app/views/topology/viewport.js (+6/-1)
app/views/utils.js (+27/-16)
app/views/view-container.js (+361/-0)
app/websocket-logging.js (+123/-0)
app/widgets/charm-search.js (+1/-1)
app/widgets/charm-token.js (+13/-2)
app/widgets/sharing-widget.js (+92/-0)
bin/merge-files (+2/-1)
bin/test-charm (+11/-9)
docs/analytics.rst (+182/-0)
docs/datasources.rst (+1/-1)
docs/index.rst (+2/-0)
docs/viewlet-data-binding.rst (+148/-0)
lib/deploy_charm_for_testing.py (+0/-1)
lib/templates.js (+1/-1)
lib/views/browser/charm-full.less (+5/-5)
lib/views/browser/charm-token.less (+2/-0)
lib/views/browser/content-sidebar.less (+26/-1)
lib/views/browser/editorial.less (+41/-0)
lib/views/browser/main.less (+5/-3)
lib/views/browser/minimized.less (+1/-0)
lib/views/browser/mixins.less (+0/-26)
lib/views/browser/sharing-widget.less (+23/-0)
lib/views/cookies.less (+50/-0)
lib/views/juju-inspector.less (+1/-1)
lib/views/stylesheet.less (+185/-105)
lib/websocketreplay.py (+8/-2)
test/browser.py (+32/-10)
test/data/related.json (+962/-0)
test/index.html (+43/-16)
test/test_app.js (+16/-12)
test/test_application_notifications.js (+1/-0)
test/test_browser_app.js (+129/-3)
test/test_browser_charm_details.js (+177/-53)
test/test_browser_editorial.js (+27/-8)
test/test_browser_models.js (+9/-0)
test/test_browser_search_view.js (+4/-3)
test/test_charm_panel.js (+11/-449)
test/test_charm_running.py (+102/-24)
test/test_charm_store.js (+18/-8)
test/test_charm_token.js (+35/-1)
test/test_cookies_app_extension.js (+70/-0)
test/test_databinding.js (+144/-0)
test/test_deploy_charm_for_testing.py (+24/-14)
test/test_endpoints.js (+4/-7)
test/test_env_go.js (+2/-0)
test/test_fakebackend.js (+69/-0)
test/test_feature_flags.js (+20/-5)
test/test_model.js (+102/-6)
test/test_notifications.js (+16/-10)
test/test_resizing_textarea.js (+4/-4)
test/test_routing.js (+29/-3)
test/test_sandbox.js (+241/-7)
test/test_service_module.js (+12/-3)
test/test_service_view.js (+181/-182)
test/test_sharing_widget.js (+63/-0)
test/test_startup.js.bottom (+1/-8)
test/test_startup.js.top (+18/-0)
test/test_topology.js (+18/-0)
test/test_topology_utils.js (+17/-0)
test/test_utils.js (+27/-0)
test/test_view_container.js (+160/-0)
test/test_websocket_logging.js (+109/-0)
test/test_websocketreplay.py (+39/-0)
test/utils.js (+28/-22)
undocumented (+122/-116)
Text conflict in README
To merge this branch: bzr merge lp:~matsubara/juju-gui/tarmac-test
Reviewer Review Type Date Requested Status
Diogo Matsubara Approve
Review via email: mp+171570@code.launchpad.net

Commit message

test change

Description of the change

test change

To post a comment you must log in.
Revision history for this message
Diogo Matsubara (matsubara) :
review: Approve
Revision history for this message
Jujugui Lander (jujugui-lander) wrote :

Attempt to merge into lp:~juju-gui/juju-gui/juju-gui-test failed due to conflicts:

text conflict in README

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.jshintignore'
2--- .jshintignore 2013-05-14 17:31:39 +0000
3+++ .jshintignore 2013-06-26 14:26:26 +0000
4@@ -1,3 +1,4 @@
5 app/assets/javascripts/prettify.js
6 app/assets/javascripts/FileSaver.js
7 app/assets/javascripts/unscaled-pack-layout.js
8+app/assets/javascripts/Object.observe.poly.js
9
10=== modified file 'CHANGES.yaml'
11--- CHANGES.yaml 2013-05-23 22:12:32 +0000
12+++ CHANGES.yaml 2013-06-26 14:26:26 +0000
13@@ -22,29 +22,60 @@
14 # [NUMBER].[NUMBER].[NUMBER] per http://semver.org/ .
15
16 - unreleased:
17+- 0.6.1:
18+ - >
19+ Fix critical jumping service bug (LP bug 1192596) and related drag
20+ problems on service creation.
21+ - Add feedback link.
22+ - Add prototype of data binding conflict resolution (feature-flagged).
23+ - >
24+ Add SetCharm to Go sandbox, in continuing preparation for supporting
25+ charm upgrades (not yet exposed to end-user).
26+ - Add incremental progress on charm sharing widget (feature-flagged).
27+ - Reduce test fragility and make other test improvements.
28+- 0.6.0:
29+ - New charm browser for finding available charms.
30+ - Visual styling changes.
31+ - The beginnings of a Go backend sandbox.
32+ - Bug fixes and improved CI reliability.
33+ - Automatic view portal zoom and centering.
34+ - Support for Google Analytics.
35+ - Linting of yuidoc comments.
36+ - Linting of copyright headers.
37+ - Linting of project documentation files.
38+ - Utility for recording and playback of websocket traffic for debugging.
39+ - Caching of search results.
40+ - Improved development HTTP server behavior.
41+ - Improved project documentation.
42 - 0.5.0:
43 - Visual styling fixes.
44 - Many small bugfixes.
45 - Internal code reorganization and refactoring.
46- - Configuration values can now be multi-line. The text entry widget
47+ - >
48+ Configuration values can now be multi-line. The text entry widget
49 automatically grows to accomodate multiple lines.
50 - Mousewheel zoom now works in firefox.
51 - The environment view now shows some help text when the canvas is empty.
52 - Changes to support faster deplyoment of the GUI charm (make npm-cache).
53- - Experimental keyboard shortcuts. These will certainly change in the
54+ - >
55+ Experimental keyboard shortcuts. These will certainly change in the
56 future so don't train your fingers just yet.
57 - Experimental import/output functionality. Also sure to change.
58- - No longer reports trivial errors caused by being in restricted
59+ - >
60+ No longer reports trivial errors caused by being in restricted
61 (read-only) mode. E.g., moving a service does not generate an error.
62 - Much nicer default layout of the services.
63- - Removed HTML5 application cache as it was causing more problems than it
64+ - >
65+ Removed HTML5 application cache as it was causing more problems than it
66 was solving.
67 - Added licensing info to project (AGPL).
68- - Fixed a memory leak in the code that reacts to changes coming from the
69+ - >
70+ Fixed a memory leak in the code that reacts to changes coming from the
71 Juju environment.
72 - 0.4.0:
73 - Support for Juju Core (Go Juju).
74- - New "sandbox" mode for in-browser-memory fake juju (set "sandbox" to
75+ - >
76+ New "sandbox" mode for in-browser-memory fake juju (set "sandbox" to
77 "true" in config.js).
78 - Support for Firefox.
79 - Support for Landscape integration.
80
81=== modified file 'Makefile'
82--- Makefile 2013-05-29 19:06:28 +0000
83+++ Makefile 2013-06-26 14:26:26 +0000
84@@ -38,7 +38,7 @@
85 -e '^app/assets/javascripts/gallery-.*\.js$$' \
86 -e '^server.js$$')
87 THIRD_PARTY_JS=app/assets/javascripts/reconnecting-websocket.js
88-LINT_IGNORE='app/assets/javascripts/prettify.js, app/assets/javascripts/FileSaver.js, app/assets/javascripts/spinner.js'
89+LINT_IGNORE='app/assets/javascripts/prettify.js, app/assets/javascripts/FileSaver.js, app/assets/javascripts/spinner.js, app/assets/javascripts/Object.observe.poly.js'
90 NODE_TARGETS=node_modules/chai node_modules/cryptojs node_modules/d3 \
91 node_modules/expect.js node_modules/express \
92 node_modules/graceful-fs node_modules/grunt node_modules/jshint \
93@@ -143,7 +143,7 @@
94 # As an optimization, we stash this value in the local PWD variable.
95 PWD=$(shell pwd)
96
97-all: build
98+all: build virtualenv/bin/python
99 @echo "\nDebug and production environments built."
100 @echo "Run 'make help' to list the main available targets."
101
102@@ -172,7 +172,7 @@
103 bin/generateTemplates
104
105 yuidoc/index.html: node_modules/yuidocjs $(JSFILES)
106- node_modules/.bin/yuidoc -o yuidoc -x assets app
107+ node_modules/.bin/yuidoc --lint -o yuidoc -x assets app
108
109 main-doc:
110 make -C docs SPHINXOPTS=-W html
111@@ -203,12 +203,12 @@
112
113 $(NON_SPRITE_IMAGES):
114 mkdir -p build-shared/juju-ui/assets/images
115- cp app/assets/images/non-sprites/* build-shared/juju-ui/assets/images/
116+ cp -r app/assets/images/non-sprites/* build-shared/juju-ui/assets/images/
117
118 install-npm-packages: $(NODE_TARGETS)
119
120 $(NODE_TARGETS): package.json
121- npm install
122+ npm install --cache-min=999999999
123 # Keep all targets up to date, not just new/changed ones.
124 for dirname in $(NODE_TARGETS); do touch $$dirname ; done
125 @# Check to see if we made what we expected to make, and warn if we did
126@@ -262,7 +262,8 @@
127 undocumented:
128 bin/lint-yuidoc --generate-undocumented > undocumented
129
130-yuidoc-lint: $(JSFILES)
131+lint-yuidoc: $(JSFILES)
132+ node_modules/.bin/yuidoc --lint -x assets app
133 bin/lint-yuidoc
134
135 recess: node_modules/recess
136@@ -272,10 +273,23 @@
137 node_modules/recess/bin/recess lib/views/stylesheet.less \
138 --config recess.json | grep -q Perfect
139
140-lint: test-prep jshint gjslint recess yuidoc-lint test-filtering
141+lint: test-prep jshint gjslint recess lint-license-headers test-filtering \
142+ lint-yuidoc
143+
144+lint-license-headers:
145+ @# Take the list of JS files in one long line and break them into
146+ @# multiple lines (this assumes there are no spaces in the paths).
147+ @# Remove non-JS files, remove third-party files, and remove files in
148+ @# the root of the project. Finally, search for copyright notices in
149+ @# the files and report files that do not have one.
150+ echo $(JSFILES) | sed 's/ /\n/g' \
151+ | grep '\.js$$' | grep -v /assets/ | grep / \
152+ | xargs -I {} sh -c "grep -L '^Copyright (C) 2[^ ]* Canonical Ltd.' {}" \
153+ || (echo "The above files are missing copyright headers."; false)
154
155 virtualenv/bin/python:
156- virtualenv virtualenv
157+ virtualenv virtualenv --system-site-packages
158+ virtualenv/bin/easy_install archives/selenium-2.33.0.tar.gz
159
160 virtualenv/bin/gjslint virtualenv/bin/fixjsstyle: virtualenv/bin/python
161 virtualenv/bin/easy_install archives/closure_linter-latest.tar.gz
162@@ -317,6 +331,7 @@
163
164 LINK_DEBUG_FILES=$(call shared-link-files-list,debug) \
165 build-debug/juju-ui/app.js \
166+ build-debug/juju-ui/websocket-logging.js \
167 build-debug/juju-ui/models \
168 build-debug/juju-ui/store \
169 build-debug/juju-ui/subapps \
170@@ -363,6 +378,7 @@
171 $(LINK_DEBUG_FILES):
172 $(call link-files,debug)
173 ln -sf "$(PWD)/app/app.js" build-debug/juju-ui/
174+ ln -sf "$(PWD)/app/websocket-logging.js" build-debug/juju-ui/
175 ln -sf "$(PWD)/app/models" build-debug/juju-ui/
176 ln -sf "$(PWD)/app/store" build-debug/juju-ui/
177 ln -sf "$(PWD)/app/subapps" build-debug/juju-ui/
178@@ -456,6 +472,7 @@
179 clean:
180 rm -rf build-shared build-debug build-prod
181 find app/assets/javascripts/ -type l | xargs rm -rf
182+ rm -f test/test_startup.js
183
184 clean-deps:
185 rm -rf node_modules virtualenv
186@@ -585,8 +602,9 @@
187 # targets are alphabetically sorted, they like it that way :-)
188 .PHONY: beautify build build-files build-devel clean clean-all clean-deps \
189 clean-docs code-doc debug devel docs dist gjslint help \
190- install-npm-packages jshint lint main-doc npm-cache npm-cache-file \
191- prep prod recess server spritegen test test-debug test-misc test-prep \
192- test-prod undocumented view-code-doc view-docs view-main-doc yuidoc-lint
193+ install-npm-packages jshint lint lint-yuidoc main-doc npm-cache \
194+ npm-cache-file prep prod recess server spritegen test test-debug \
195+ test-misc test-prep test-prod undocumented view-code-doc view-docs \
196+ view-main-doc
197
198 .DEFAULT_GOAL := all
199
200=== modified file 'README'
201--- README 2013-06-13 22:48:11 +0000
202+++ README 2013-06-26 14:26:26 +0000
203@@ -2,8 +2,12 @@
204 alongside the whole project documentation. To do this, you need the
205 dependencies described in the "Documentation" section of the HACKING
206 file.
207+<<<<<<< TREE
208
209 foo
210+=======
211+one line change
212+>>>>>>> MERGE-SOURCE
213 ======
214 README
215 ======
216
217=== modified file 'app/app.js'
218--- app/app.js 2013-05-28 12:43:25 +0000
219+++ app/app.js 2013-06-26 14:26:26 +0000
220@@ -47,12 +47,16 @@
221 */
222 var JujuGUI = Y.Base.create('juju-gui', Y.App, [
223 Y.juju.SubAppRegistration,
224- Y.juju.NSRouter], {
225+ Y.juju.NSRouter,
226+ Y.juju.Cookies], {
227
228 /*
229 Extension properties
230 */
231- subApplications: [],
232+ subApplications: [{
233+ type: Y.juju.subapps.Browser,
234+ config: {}
235+ }],
236
237 defaultNamespace: 'charmstore',
238 /*
239@@ -132,7 +136,7 @@
240
241 },
242
243- /**
244+ /*
245 * Declarative keybindings on the window object.
246 *
247 * Prefix supported are:
248@@ -197,14 +201,18 @@
249 },
250 help: 'Navigate to the Environment overview.'
251 },
252- '+': {
253+ 'S-+': {
254 fire: 'zoom_in',
255 help: 'Zoom In'
256 },
257- '-': {
258+ 'S--': {
259 fire: 'zoom_out',
260 help: 'Zoom Out'
261 },
262+ 'S-0': {
263+ fire: 'panToCenter',
264+ help: 'Center the Environment overview'
265+ },
266 'esc': {
267 fire: 'clearState',
268 callback: function() {
269@@ -226,7 +234,6 @@
270
271 'S-d': {
272 callback: function(evt) {
273- /* global saveAs: false */
274 this.env.exportEnvironment(function(r) {
275 var exportData = JSON.stringify(r.result, undefined, 2);
276 var exportBlob = new Blob([exportData],
277@@ -235,8 +242,16 @@
278 });
279 },
280 help: 'Export the environment'
281+ },
282+
283+ 'C-S-d': {
284+ callback: function(evt) {
285+ Y.fire('saveWebsocketLog');
286+ },
287+ help: 'Save the websocket log to a file'
288 }
289
290+
291 },
292
293 /**
294@@ -284,12 +299,22 @@
295 });
296 this._keybindings = Y.one(window).on('keydown', function(evt) {
297 //Normalize key-code
298+ var source = evt.target.getDOMNode();
299+ // Target filtering, we want to listen on window
300+ // but not honor hotkeys when focused on
301+ // text oriented input fields
302+ if (['INPUT', 'TEXTAREA'].indexOf(source.tagName) !== -1) {
303+ return;
304+ }
305 var symbolic = [];
306 if (evt.ctrlKey) { symbolic.push('C');}
307 if (evt.altKey) { symbolic.push('A');}
308 if (evt.shiftKey) { symbolic.push('S');}
309- symbolic.push(code_map[evt.keyCode] ||
310- String.fromCharCode(evt.keyCode).toLowerCase());
311+ if (code_map[evt.keyCode]) {
312+ symbolic.push(code_map[evt.which]);
313+ } else {
314+ symbolic.push(String.fromCharCode(evt.which).toLowerCase());
315+ }
316 var trigger = symbolic.join('-');
317 var spec = this.keybindings[trigger];
318 if (spec) {
319@@ -335,15 +360,19 @@
320 }
321 }
322
323- // XXX: #1185002 the charm browser subapp feature flag needs to be
324- // removed
325- if (window.flags.browser_enabled) {
326- this.subApplications.push({
327- type: Y.juju.subapps.Browser,
328- config: {}
329- });
330+ if (window.flags && window.flags.websocket_capture) {
331+ this.websocketLogging = new Y.juju.WebsocketLogging();
332 }
333
334+ /**
335+ Reference to the juju.Cookies instance.
336+
337+ @property cookieHandler
338+ @type {juju.Cookies}
339+ @default null
340+ */
341+ this.cookieHandler = null;
342+
343 this.renderEnvironment = true;
344 // If this property has a value other than '/' then
345 // navigate to it after logging in.
346@@ -418,7 +447,7 @@
347 };
348 var apiBackend = this.get('apiBackend');
349 // The sandbox mode does not support the Go API (yet?).
350- if (this.get('sandbox') && apiBackend === 'python') {
351+ if (this.get('sandbox')) {
352 var sandboxModule = Y.namespace('juju.environments.sandbox');
353 var State = Y.namespace('juju.environments').FakeBackend;
354 var state = new State({charmStore: this.charm_store});
355@@ -427,8 +456,18 @@
356 credentials[envOptions.user] = envOptions.password;
357 state.set('authorizedUsers', credentials);
358 }
359- envOptions.conn = new sandboxModule.ClientConnection(
360- {juju: new sandboxModule.PyJujuAPI({state: state})});
361+ if (apiBackend === 'python') {
362+ envOptions.conn = new sandboxModule.ClientConnection(
363+ {juju: new sandboxModule.PyJujuAPI({state: state})});
364+ } else if (apiBackend === 'go') {
365+ envOptions.conn = new sandboxModule.ClientConnection(
366+ {juju: new sandboxModule.GoJujuAPI({state: state})});
367+ } else {
368+ // Clean ourselves up before giving up the ghost, for tests' sake.
369+ this.destroy();
370+ throw 'unrecognized backend type: ' + apiBackend;
371+ }
372+
373 }
374 this.env = juju.newEnvironment(envOptions, apiBackend);
375 }
376@@ -518,10 +557,6 @@
377 env: this.env,
378 app: this
379 });
380- this.charmPanel.setDefaultSeries(this.env.get('defaultSeries'));
381- this.env.after('defaultSeriesChange', Y.bind(function(ev) {
382- this.charmPanel.setDefaultSeries(ev.newVal);
383- }, this));
384
385 // Halt the default navigation on the juju logo to allow us to show
386 // the real root view without namespaces
387@@ -534,7 +569,10 @@
388 }, this);
389 }
390
391- Y.one('#logout-trigger').on('click', this.logout, this);
392+ Y.one('#logout-trigger').on('click', function(e) {
393+ e.halt();
394+ this.logout();
395+ }, this);
396
397 // Attach SubApplications. The subapps should share the same db.
398 cfg.db = this.db;
399@@ -565,7 +603,7 @@
400 catch (err) {
401 // Unable to create simulator, usually due to mocks or an
402 // unsupported environment
403- console.log('Unable to create simulator: ', err);
404+ console.log('Unable to create simulator: ');
405 }
406 }
407 },
408@@ -908,7 +946,7 @@
409 // If the Juju environment is not connected, exit without letting the
410 // route dispatch proceed. On env connection change, the app will
411 // re-dispatch and this route callback will be executed again.
412- if (!this.env.get('connected')) {
413+ if (!this.env || !this.env.get('connected')) {
414 return;
415 }
416 var credentials = this.env.getCredentials();
417@@ -1181,6 +1219,24 @@
418 finalPath = this.nsRouter.url({ gui: matches[idx].path });
419 }
420 return finalPath;
421+ },
422+
423+ /**
424+ * Make sure the user agrees to cookie usage.
425+ *
426+ * @method authorizeCookieUse
427+ * @param {Object} req The request.
428+ * @param {Object} res The response.
429+ * @param {Object} next The next route handler.
430+ *
431+ */
432+ authorizeCookieUse: function(req, res, next) {
433+ var analyticsEnabled = this.get('useAnalytics');
434+ if (analyticsEnabled) {
435+ this.cookieHandler = this.cookieHandler || new Y.juju.Cookies();
436+ this.cookieHandler.check();
437+ }
438+ next();
439 }
440
441 }, {
442@@ -1203,7 +1259,7 @@
443 * `namespace`: (optional) when namespace is specified this route should
444 * only match when the URL fragment occurs in that namespace. The
445 * default namespace (as passed to this.nsRouter) is assumed if no
446- * namespace attribute is specified.
447+ * namespace attribute is specified.
448 *
449 * `model`: `model.name` (required)
450 *
451@@ -1224,6 +1280,7 @@
452 { path: '*', callbacks: 'show_notifications_view'},
453 { path: '*', callbacks: 'toggleStaticViews'},
454 { path: '*', callbacks: 'show_environment'},
455+ { path: '*', callbacks: 'authorizeCookieUse'},
456 // Charms.
457 { path: '/charms/',
458 callbacks: 'show_charm_collection',
459@@ -1271,7 +1328,7 @@
460
461 Y.namespace('juju').App = JujuGUI;
462
463-}, '0.5.2', {
464+}, '0.5.3', {
465 requires: [
466 'juju-charm-models',
467 'juju-charm-panel',
468@@ -1291,6 +1348,7 @@
469 'juju-views',
470 'juju-view-login',
471 'juju-landscape',
472+ 'juju-websocket-logging',
473 'io',
474 'json-parse',
475 'app-base',
476@@ -1298,6 +1356,8 @@
477 'base',
478 'node',
479 'model',
480+ 'app-cookies-extension',
481+ 'cookie',
482 'app-subapp-extension',
483 'sub-app',
484 'subapp-browser',
485
486=== added file 'app/assets/images/charm-app-servers-160.png'
487Binary files app/assets/images/charm-app-servers-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-app-servers-160.png 2013-06-26 14:26:26 +0000 differ
488=== added file 'app/assets/images/charm-app-servers-64.png'
489Binary files app/assets/images/charm-app-servers-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-app-servers-64.png 2013-06-26 14:26:26 +0000 differ
490=== added file 'app/assets/images/charm-app-servers-96.png'
491Binary files app/assets/images/charm-app-servers-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-app-servers-96.png 2013-06-26 14:26:26 +0000 differ
492=== added file 'app/assets/images/charm-applications-160.png'
493Binary files app/assets/images/charm-applications-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-applications-160.png 2013-06-26 14:26:26 +0000 differ
494=== added file 'app/assets/images/charm-applications-64.png'
495Binary files app/assets/images/charm-applications-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-applications-64.png 2013-06-26 14:26:26 +0000 differ
496=== added file 'app/assets/images/charm-applications-96.png'
497Binary files app/assets/images/charm-applications-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-applications-96.png 2013-06-26 14:26:26 +0000 differ
498=== added file 'app/assets/images/charm-cache-proxy-160.png'
499Binary files app/assets/images/charm-cache-proxy-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-cache-proxy-160.png 2013-06-26 14:26:26 +0000 differ
500=== added file 'app/assets/images/charm-cache-proxy-64.png'
501Binary files app/assets/images/charm-cache-proxy-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-cache-proxy-64.png 2013-06-26 14:26:26 +0000 differ
502=== added file 'app/assets/images/charm-cache-proxy-96.png'
503Binary files app/assets/images/charm-cache-proxy-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-cache-proxy-96.png 2013-06-26 14:26:26 +0000 differ
504=== added file 'app/assets/images/charm-databases-160.png'
505Binary files app/assets/images/charm-databases-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-databases-160.png 2013-06-26 14:26:26 +0000 differ
506=== added file 'app/assets/images/charm-databases-64.png'
507Binary files app/assets/images/charm-databases-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-databases-64.png 2013-06-26 14:26:26 +0000 differ
508=== added file 'app/assets/images/charm-databases-96.png'
509Binary files app/assets/images/charm-databases-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-databases-96.png 2013-06-26 14:26:26 +0000 differ
510=== added file 'app/assets/images/charm-file-servers-160.png'
511Binary files app/assets/images/charm-file-servers-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-file-servers-160.png 2013-06-26 14:26:26 +0000 differ
512=== added file 'app/assets/images/charm-file-servers-64.png'
513Binary files app/assets/images/charm-file-servers-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-file-servers-64.png 2013-06-26 14:26:26 +0000 differ
514=== added file 'app/assets/images/charm-file-servers-96.png'
515Binary files app/assets/images/charm-file-servers-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-file-servers-96.png 2013-06-26 14:26:26 +0000 differ
516=== added file 'app/assets/images/charm-misc-160.png'
517Binary files app/assets/images/charm-misc-160.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-misc-160.png 2013-06-26 14:26:26 +0000 differ
518=== added file 'app/assets/images/charm-misc-64.png'
519Binary files app/assets/images/charm-misc-64.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-misc-64.png 2013-06-26 14:26:26 +0000 differ
520=== added file 'app/assets/images/charm-misc-96.png'
521Binary files app/assets/images/charm-misc-96.png 1970-01-01 00:00:00 +0000 and app/assets/images/charm-misc-96.png 2013-06-26 14:26:26 +0000 differ
522=== added file 'app/assets/images/email-click.png'
523Binary files app/assets/images/email-click.png 1970-01-01 00:00:00 +0000 and app/assets/images/email-click.png 2013-06-26 14:26:26 +0000 differ
524=== added file 'app/assets/images/email-hover.png'
525Binary files app/assets/images/email-hover.png 1970-01-01 00:00:00 +0000 and app/assets/images/email-hover.png 2013-06-26 14:26:26 +0000 differ
526=== added file 'app/assets/images/email-normal.png'
527Binary files app/assets/images/email-normal.png 1970-01-01 00:00:00 +0000 and app/assets/images/email-normal.png 2013-06-26 14:26:26 +0000 differ
528=== added file 'app/assets/images/facebook-click.png'
529Binary files app/assets/images/facebook-click.png 1970-01-01 00:00:00 +0000 and app/assets/images/facebook-click.png 2013-06-26 14:26:26 +0000 differ
530=== added file 'app/assets/images/facebook-hover.png'
531Binary files app/assets/images/facebook-hover.png 1970-01-01 00:00:00 +0000 and app/assets/images/facebook-hover.png 2013-06-26 14:26:26 +0000 differ
532=== added file 'app/assets/images/facebook-normal.png'
533Binary files app/assets/images/facebook-normal.png 1970-01-01 00:00:00 +0000 and app/assets/images/facebook-normal.png 2013-06-26 14:26:26 +0000 differ
534=== added file 'app/assets/images/google-click.png'
535Binary files app/assets/images/google-click.png 1970-01-01 00:00:00 +0000 and app/assets/images/google-click.png 2013-06-26 14:26:26 +0000 differ
536=== added file 'app/assets/images/google-hover.png'
537Binary files app/assets/images/google-hover.png 1970-01-01 00:00:00 +0000 and app/assets/images/google-hover.png 2013-06-26 14:26:26 +0000 differ
538=== added file 'app/assets/images/google-normal.png'
539Binary files app/assets/images/google-normal.png 1970-01-01 00:00:00 +0000 and app/assets/images/google-normal.png 2013-06-26 14:26:26 +0000 differ
540=== added file 'app/assets/images/header_notification_centre.png'
541Binary files app/assets/images/header_notification_centre.png 1970-01-01 00:00:00 +0000 and app/assets/images/header_notification_centre.png 2013-06-26 14:26:26 +0000 differ
542=== added file 'app/assets/images/header_notification_left.png'
543Binary files app/assets/images/header_notification_left.png 1970-01-01 00:00:00 +0000 and app/assets/images/header_notification_left.png 2013-06-26 14:26:26 +0000 differ
544=== added file 'app/assets/images/header_notification_right.png'
545Binary files app/assets/images/header_notification_right.png 1970-01-01 00:00:00 +0000 and app/assets/images/header_notification_right.png 2013-06-26 14:26:26 +0000 differ
546=== added file 'app/assets/images/juju_logo_dark.png'
547Binary files app/assets/images/juju_logo_dark.png 1970-01-01 00:00:00 +0000 and app/assets/images/juju_logo_dark.png 2013-06-26 14:26:26 +0000 differ
548=== added file 'app/assets/images/non-sprites/category-app-server.svg'
549--- app/assets/images/non-sprites/category-app-server.svg 1970-01-01 00:00:00 +0000
550+++ app/assets/images/non-sprites/category-app-server.svg 2013-06-26 14:26:26 +0000
551@@ -0,0 +1,124 @@
552+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
553+<!-- Created with Inkscape (http://www.inkscape.org/) -->
554+
555+<svg
556+ xmlns:dc="http://purl.org/dc/elements/1.1/"
557+ xmlns:cc="http://creativecommons.org/ns#"
558+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
559+ xmlns:svg="http://www.w3.org/2000/svg"
560+ xmlns="http://www.w3.org/2000/svg"
561+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
562+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
563+ width="47.94035"
564+ height="47.94035"
565+ id="svg4400"
566+ version="1.1"
567+ inkscape:version="0.48.3.1 r9886"
568+ sodipodi:docname="category-app-server.svg">
569+ <defs
570+ id="defs4402" />
571+ <sodipodi:namedview
572+ id="base"
573+ pagecolor="#ffffff"
574+ bordercolor="#666666"
575+ borderopacity="1.0"
576+ inkscape:pageopacity="0.0"
577+ inkscape:pageshadow="2"
578+ inkscape:zoom="5.0931703"
579+ inkscape:cx="85.779492"
580+ inkscape:cy="6.1041666"
581+ inkscape:document-units="px"
582+ inkscape:current-layer="layer1"
583+ showgrid="true"
584+ fit-margin-top="0"
585+ fit-margin-left="0"
586+ fit-margin-right="0"
587+ fit-margin-bottom="0"
588+ inkscape:window-width="1920"
589+ inkscape:window-height="1029"
590+ inkscape:window-x="0"
591+ inkscape:window-y="24"
592+ inkscape:window-maximized="1">
593+ <inkscape:grid
594+ type="xygrid"
595+ id="grid4469"
596+ empspacing="8"
597+ visible="true"
598+ enabled="true"
599+ snapvisiblegridlinesonly="true" />
600+ </sodipodi:namedview>
601+ <metadata
602+ id="metadata4405">
603+ <rdf:RDF>
604+ <cc:Work
605+ rdf:about="">
606+ <dc:format>image/svg+xml</dc:format>
607+ <dc:type
608+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
609+ <dc:title></dc:title>
610+ </cc:Work>
611+ </rdf:RDF>
612+ </metadata>
613+ <g
614+ inkscape:label="Layer 1"
615+ inkscape:groupmode="layer"
616+ id="layer1"
617+ transform="translate(-630.31553,-996.96343)">
618+ <g
619+ style="stroke:#505050;stroke-opacity:1;display:inline"
620+ id="g4781-3"
621+ transform="matrix(1.5005353,0,0,1.4833177,-852.22207,-34.476776)">
622+ <path
623+ sodipodi:nodetypes="ccccc"
624+ inkscape:connector-curvature="0"
625+ id="path4783-4"
626+ d="m 996.33998,725.99069 7.65092,-3.80699 0,-8.70167 -7.65092,3.26312 z"
627+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:2.01851225;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
628+ <path
629+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.982494;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
630+ d="m 996.35027,726.0088 -7.35894,-3.81802 0,-8.72686 7.35894,3.27257 z"
631+ id="path4785-7"
632+ inkscape:connector-curvature="0"
633+ sodipodi:nodetypes="ccccc" />
634+ <path
635+ inkscape:connector-curvature="0"
636+ id="path4787-1"
637+ d="m 996.50004,716.7407 7.50006,-3.26798 -7.50006,-3.81266 -7.50001,3.81266 z"
638+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
639+ <path
640+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.98247516;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
641+ d="m 1011.6499,726.00881 7.3589,-3.81802 0,-8.72688 -7.3589,3.27258 z"
642+ id="path4789-0"
643+ inkscape:connector-curvature="0"
644+ sodipodi:nodetypes="ccccc" />
645+ <path
646+ sodipodi:nodetypes="ccccc"
647+ inkscape:connector-curvature="0"
648+ id="path4791-1"
649+ d="m 1011.6602,725.99068 -7.651,-3.80699 0,-8.70165 7.651,3.26312 z"
650+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.01853108;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
651+ <path
652+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
653+ d="m 1011.5,716.7407 7.5001,-3.26798 -7.5001,-3.81266 -7.5,3.81266 z"
654+ id="path4793-3"
655+ inkscape:connector-curvature="0" />
656+ <path
657+ sodipodi:nodetypes="ccccc"
658+ inkscape:connector-curvature="0"
659+ id="path4795-6"
660+ d="m 1003.6526,713.34891 7.3564,-3.81813 0,-8.7271 -7.3564,3.27267 z"
661+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.9821583;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
662+ <path
663+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.01884151;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
664+ d="m 1003.6629,713.33046 -7.65353,-3.8069 0,-8.70143 7.65353,3.26304 z"
665+ id="path4797-0"
666+ inkscape:connector-curvature="0"
667+ sodipodi:nodetypes="ccccc" />
668+ <path
669+ inkscape:connector-curvature="0"
670+ id="path4799-9"
671+ d="m 1003.5,704.08064 7.5001,-3.26799 L 1003.5,697 996,700.81265 z"
672+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
673+ </g>
674+ </g>
675+</svg>
676
677=== added file 'app/assets/images/non-sprites/category-application.svg'
678--- app/assets/images/non-sprites/category-application.svg 1970-01-01 00:00:00 +0000
679+++ app/assets/images/non-sprites/category-application.svg 2013-06-26 14:26:26 +0000
680@@ -0,0 +1,90 @@
681+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
682+<!-- Created with Inkscape (http://www.inkscape.org/) -->
683+
684+<svg
685+ xmlns:dc="http://purl.org/dc/elements/1.1/"
686+ xmlns:cc="http://creativecommons.org/ns#"
687+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
688+ xmlns:svg="http://www.w3.org/2000/svg"
689+ xmlns="http://www.w3.org/2000/svg"
690+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
691+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
692+ width="47.94035"
693+ height="47.94035"
694+ id="svg4400"
695+ version="1.1"
696+ inkscape:version="0.48.3.1 r9886"
697+ sodipodi:docname="category-application.svg">
698+ <defs
699+ id="defs4402" />
700+ <sodipodi:namedview
701+ id="base"
702+ pagecolor="#ffffff"
703+ bordercolor="#666666"
704+ borderopacity="1.0"
705+ inkscape:pageopacity="0.0"
706+ inkscape:pageshadow="2"
707+ inkscape:zoom="5.0931703"
708+ inkscape:cx="85.779492"
709+ inkscape:cy="6.1041666"
710+ inkscape:document-units="px"
711+ inkscape:current-layer="layer1"
712+ showgrid="true"
713+ fit-margin-top="0"
714+ fit-margin-left="0"
715+ fit-margin-right="0"
716+ fit-margin-bottom="0"
717+ inkscape:window-width="1920"
718+ inkscape:window-height="1029"
719+ inkscape:window-x="0"
720+ inkscape:window-y="24"
721+ inkscape:window-maximized="1">
722+ <inkscape:grid
723+ type="xygrid"
724+ id="grid4469"
725+ empspacing="8"
726+ visible="true"
727+ enabled="true"
728+ snapvisiblegridlinesonly="true" />
729+ </sodipodi:namedview>
730+ <metadata
731+ id="metadata4405">
732+ <rdf:RDF>
733+ <cc:Work
734+ rdf:about="">
735+ <dc:format>image/svg+xml</dc:format>
736+ <dc:type
737+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
738+ <dc:title></dc:title>
739+ </cc:Work>
740+ </rdf:RDF>
741+ </metadata>
742+ <g
743+ inkscape:label="Layer 1"
744+ inkscape:groupmode="layer"
745+ id="layer1"
746+ transform="translate(-630.31553,-996.96343)">
747+ <g
748+ style="stroke:#505050;stroke-width:0.99999881;stroke-opacity:1;display:inline"
749+ id="g4746-5"
750+ transform="matrix(1.5000001,0,0,1.4999966,-852.18459,-227.09334)">
751+ <path
752+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
753+ d="m 1004,847 14,-7 0,-16 -14,6 z"
754+ id="path4748-9"
755+ inkscape:connector-curvature="0"
756+ sodipodi:nodetypes="ccccc" />
757+ <path
758+ sodipodi:nodetypes="ccccc"
759+ inkscape:connector-curvature="0"
760+ id="path4750-9"
761+ d="m 1004,847 -14,-7 0,-16 14,6 z"
762+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
763+ <path
764+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
765+ d="m 1004,830 14,-6 -14,-7 -14,7 z"
766+ id="path4752-1"
767+ inkscape:connector-curvature="0" />
768+ </g>
769+ </g>
770+</svg>
771
772=== added file 'app/assets/images/non-sprites/category-cache-proxy.svg'
773--- app/assets/images/non-sprites/category-cache-proxy.svg 1970-01-01 00:00:00 +0000
774+++ app/assets/images/non-sprites/category-cache-proxy.svg 2013-06-26 14:26:26 +0000
775@@ -0,0 +1,119 @@
776+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
777+<!-- Created with Inkscape (http://www.inkscape.org/) -->
778+
779+<svg
780+ xmlns:dc="http://purl.org/dc/elements/1.1/"
781+ xmlns:cc="http://creativecommons.org/ns#"
782+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
783+ xmlns:svg="http://www.w3.org/2000/svg"
784+ xmlns="http://www.w3.org/2000/svg"
785+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
786+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
787+ width="47.94035"
788+ height="47.94035"
789+ id="svg4400"
790+ version="1.1"
791+ inkscape:version="0.48.3.1 r9886"
792+ sodipodi:docname="category-cache-proxy.svg">
793+ <defs
794+ id="defs4402" />
795+ <sodipodi:namedview
796+ id="base"
797+ pagecolor="#ffffff"
798+ bordercolor="#666666"
799+ borderopacity="1.0"
800+ inkscape:pageopacity="0.0"
801+ inkscape:pageshadow="2"
802+ inkscape:zoom="5.0931703"
803+ inkscape:cx="85.779492"
804+ inkscape:cy="6.1041666"
805+ inkscape:document-units="px"
806+ inkscape:current-layer="layer1"
807+ showgrid="true"
808+ fit-margin-top="0"
809+ fit-margin-left="0"
810+ fit-margin-right="0"
811+ fit-margin-bottom="0"
812+ inkscape:window-width="1920"
813+ inkscape:window-height="1029"
814+ inkscape:window-x="0"
815+ inkscape:window-y="24"
816+ inkscape:window-maximized="1">
817+ <inkscape:grid
818+ type="xygrid"
819+ id="grid4469"
820+ empspacing="8"
821+ visible="true"
822+ enabled="true"
823+ snapvisiblegridlinesonly="true" />
824+ </sodipodi:namedview>
825+ <metadata
826+ id="metadata4405">
827+ <rdf:RDF>
828+ <cc:Work
829+ rdf:about="">
830+ <dc:format>image/svg+xml</dc:format>
831+ <dc:type
832+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
833+ <dc:title></dc:title>
834+ </cc:Work>
835+ </rdf:RDF>
836+ </metadata>
837+ <g
838+ inkscape:label="Layer 1"
839+ inkscape:groupmode="layer"
840+ id="layer1"
841+ transform="translate(-630.31553,-996.96343)">
842+ <path
843+ sodipodi:type="arc"
844+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
845+ id="path3598"
846+ sodipodi:cx="1012"
847+ sodipodi:cy="420"
848+ sodipodi:rx="4"
849+ sodipodi:ry="4"
850+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
851+ transform="matrix(-1.625,0,0,1.625,2289.3155,329.90378)" />
852+ <path
853+ transform="matrix(-1.625,0,0,1.625,2316.3155,329.90378)"
854+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
855+ sodipodi:ry="4"
856+ sodipodi:rx="4"
857+ sodipodi:cy="420"
858+ sodipodi:cx="1012"
859+ id="path4368"
860+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
861+ sodipodi:type="arc" />
862+ <path
863+ transform="matrix(-3.2499973,0,0,3.2499973,3933.8128,-352.59507)"
864+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
865+ sodipodi:ry="4"
866+ sodipodi:rx="4"
867+ sodipodi:cy="420"
868+ sodipodi:cx="1012"
869+ id="path4370"
870+ style="color:#000000;fill:none;stroke:#505050;stroke-width:0.92307782;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
871+ sodipodi:type="arc" />
872+ <path
873+ sodipodi:type="arc"
874+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
875+ id="path4372"
876+ sodipodi:cx="1012"
877+ sodipodi:cy="420"
878+ sodipodi:rx="4"
879+ sodipodi:ry="4"
880+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
881+ transform="matrix(-1.5,0,0,1.5,2178.3155,407.90378)" />
882+ <path
883+ style="color:#000000;fill:none;stroke:#505050;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.70000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
884+ d="m 672.31551,1012.4038 -28.00004,0"
885+ id="path4374"
886+ inkscape:connector-curvature="0" />
887+ <path
888+ style="color:#000000;fill:none;stroke:#505050;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.70000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
889+ d="m 661.31551,1039.9038 -17.00004,-28"
890+ id="path4376"
891+ inkscape:connector-curvature="0"
892+ sodipodi:nodetypes="cc" />
893+ </g>
894+</svg>
895
896=== added file 'app/assets/images/non-sprites/category-database.svg'
897--- app/assets/images/non-sprites/category-database.svg 1970-01-01 00:00:00 +0000
898+++ app/assets/images/non-sprites/category-database.svg 2013-06-26 14:26:26 +0000
899@@ -0,0 +1,115 @@
900+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
901+<!-- Created with Inkscape (http://www.inkscape.org/) -->
902+
903+<svg
904+ xmlns:dc="http://purl.org/dc/elements/1.1/"
905+ xmlns:cc="http://creativecommons.org/ns#"
906+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
907+ xmlns:svg="http://www.w3.org/2000/svg"
908+ xmlns="http://www.w3.org/2000/svg"
909+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
910+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
911+ width="47.94035"
912+ height="47.94035"
913+ id="svg4400"
914+ version="1.1"
915+ inkscape:version="0.48.3.1 r9886"
916+ sodipodi:docname="category-database.svg">
917+ <defs
918+ id="defs4402" />
919+ <sodipodi:namedview
920+ id="base"
921+ pagecolor="#ffffff"
922+ bordercolor="#666666"
923+ borderopacity="1.0"
924+ inkscape:pageopacity="0.0"
925+ inkscape:pageshadow="2"
926+ inkscape:zoom="5.0931703"
927+ inkscape:cx="85.779492"
928+ inkscape:cy="6.1041666"
929+ inkscape:document-units="px"
930+ inkscape:current-layer="layer1"
931+ showgrid="true"
932+ fit-margin-top="0"
933+ fit-margin-left="0"
934+ fit-margin-right="0"
935+ fit-margin-bottom="0"
936+ inkscape:window-width="1920"
937+ inkscape:window-height="1029"
938+ inkscape:window-x="0"
939+ inkscape:window-y="24"
940+ inkscape:window-maximized="1">
941+ <inkscape:grid
942+ type="xygrid"
943+ id="grid4469"
944+ empspacing="8"
945+ visible="true"
946+ enabled="true"
947+ snapvisiblegridlinesonly="true" />
948+ </sodipodi:namedview>
949+ <metadata
950+ id="metadata4405">
951+ <rdf:RDF>
952+ <cc:Work
953+ rdf:about="">
954+ <dc:format>image/svg+xml</dc:format>
955+ <dc:type
956+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
957+ <dc:title></dc:title>
958+ </cc:Work>
959+ </rdf:RDF>
960+ </metadata>
961+ <g
962+ inkscape:label="Layer 1"
963+ inkscape:groupmode="layer"
964+ id="layer1"
965+ transform="translate(-630.31553,-996.96343)">
966+ <g
967+ style="stroke:#505050;stroke-width:1;stroke-opacity:1;display:inline"
968+ id="g4760-2"
969+ transform="matrix(1.4999997,0,0,1.4999997,-851.68417,-316.59602)">
970+ <path
971+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-288.66662)"
972+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -9.1127,0 -16.5,-1.34315 -16.5,-3 0,-1.65685 7.3873,-3 16.5,-3 9.1127,0 16.5,1.34315 16.5,3 z"
973+ sodipodi:ry="3"
974+ sodipodi:rx="16.5"
975+ sodipodi:cy="877"
976+ sodipodi:cx="1004.5"
977+ id="path4762-0"
978+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
979+ sodipodi:type="arc" />
980+ <path
981+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
982+ d="m 991,881 0,21.66665 0,0 c 0,2.20914 5.82029,4 13,4 7.1797,0 13,-1.79086 13,-4 L 1017,881"
983+ id="path4764-6"
984+ inkscape:connector-curvature="0"
985+ sodipodi:nodetypes="cccssc" />
986+ <path
987+ sodipodi:type="arc"
988+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
989+ id="path4766-6"
990+ sodipodi:cx="1004.5"
991+ sodipodi:cy="877"
992+ sodipodi:rx="16.5"
993+ sodipodi:ry="3"
994+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -7.46799,0 -14.00492,-0.91199 -15.93778,-2.22354"
995+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-281.33332)"
996+ sodipodi:start="0"
997+ sodipodi:end="2.8797933"
998+ sodipodi:open="true" />
999+ <path
1000+ sodipodi:open="true"
1001+ sodipodi:end="2.8797933"
1002+ sodipodi:start="0"
1003+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-273.99995)"
1004+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -7.46799,0 -14.00492,-0.91199 -15.93778,-2.22354"
1005+ sodipodi:ry="3"
1006+ sodipodi:rx="16.5"
1007+ sodipodi:cy="877"
1008+ sodipodi:cx="1004.5"
1009+ id="path4768-5"
1010+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1011+ sodipodi:type="arc" />
1012+ </g>
1013+ </g>
1014+</svg>
1015
1016=== added file 'app/assets/images/non-sprites/category-file-server.svg'
1017--- app/assets/images/non-sprites/category-file-server.svg 1970-01-01 00:00:00 +0000
1018+++ app/assets/images/non-sprites/category-file-server.svg 2013-06-26 14:26:26 +0000
1019@@ -0,0 +1,74 @@
1020+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1021+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1022+
1023+<svg
1024+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1025+ xmlns:cc="http://creativecommons.org/ns#"
1026+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1027+ xmlns:svg="http://www.w3.org/2000/svg"
1028+ xmlns="http://www.w3.org/2000/svg"
1029+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1030+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1031+ width="47.94035"
1032+ height="47.94035"
1033+ id="svg4400"
1034+ version="1.1"
1035+ inkscape:version="0.48.3.1 r9886"
1036+ sodipodi:docname="category-file-server.svg">
1037+ <defs
1038+ id="defs4402" />
1039+ <sodipodi:namedview
1040+ id="base"
1041+ pagecolor="#ffffff"
1042+ bordercolor="#666666"
1043+ borderopacity="1.0"
1044+ inkscape:pageopacity="0.0"
1045+ inkscape:pageshadow="2"
1046+ inkscape:zoom="5.0931703"
1047+ inkscape:cx="85.779492"
1048+ inkscape:cy="6.1041666"
1049+ inkscape:document-units="px"
1050+ inkscape:current-layer="layer1"
1051+ showgrid="true"
1052+ fit-margin-top="0"
1053+ fit-margin-left="0"
1054+ fit-margin-right="0"
1055+ fit-margin-bottom="0"
1056+ inkscape:window-width="1920"
1057+ inkscape:window-height="1029"
1058+ inkscape:window-x="0"
1059+ inkscape:window-y="24"
1060+ inkscape:window-maximized="1">
1061+ <inkscape:grid
1062+ type="xygrid"
1063+ id="grid4469"
1064+ empspacing="8"
1065+ visible="true"
1066+ enabled="true"
1067+ snapvisiblegridlinesonly="true" />
1068+ </sodipodi:namedview>
1069+ <metadata
1070+ id="metadata4405">
1071+ <rdf:RDF>
1072+ <cc:Work
1073+ rdf:about="">
1074+ <dc:format>image/svg+xml</dc:format>
1075+ <dc:type
1076+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1077+ <dc:title></dc:title>
1078+ </cc:Work>
1079+ </rdf:RDF>
1080+ </metadata>
1081+ <g
1082+ inkscape:label="Layer 1"
1083+ inkscape:groupmode="layer"
1084+ id="layer1"
1085+ transform="translate(-630.31553,-996.96343)">
1086+ <path
1087+ inkscape:connector-curvature="0"
1088+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#505050;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
1089+ d="m 634.81553,996.90381 c -2.46705,0 -4.5,2.03295 -4.5,4.49999 l 0,3 0,1.5 0,25.5 c 0,2.4671 2.03295,4.5 4.5,4.5 l 9.5,0 0,-3 -9.5,0 c -0.85695,0 -1.5,-0.643 -1.5,-1.5 l 0,-25.5 0,-1.5 0,-3 c 0,-0.857 0.64305,-1.49999 1.5,-1.49999 l 12,0 c 0.8569,0 1.5,0.64299 1.5,1.49999 -0.002,0.047 -0.002,0.094 0,0.1406 l 0,1.3593 1.2657,0 0.2343,0 24,0 c 0.8569,0 1.5,0.6431 1.5,1.5 l 0,27 0,4.2188 c 1.7336,-0.6275 3,-2.2811 3,-4.2188 l 0,-27 c 0,-2.467 -2.0329,-4.49996 -4.5,-4.49996 l -23.1093,0 c -0.6631,-1.66422 -2.0029,-3 -3.8907,-3 l -12,0 z m 15.32985,16.99989 c -1.662,0 -3,1.338 -3,3 l 0,25.0001 c 0,1.662 1.338,3 3,3 l 19.17015,0 c 1.662,0 3,-1.338 3,-3 l 0,-25 c 0,-1.662 -1.338,-3 -3,-3 z m 2.41915,4.0001 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 13.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7925 -0.7215,1.5112 -1.506,1.5 l -13.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z m 0,6 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 13.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7926 -0.7215,1.5112 -1.506,1.5 l -13.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z m 0,6 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 7.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7926 -0.7215,1.5112 -1.506,1.5 l -7.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z"
1090+ id="rect4570-0"
1091+ sodipodi:nodetypes="csccssccssccssscccccssccssscsccsssssssssccccccsccsccccccccsccsccccccccsccscc" />
1092+ </g>
1093+</svg>
1094
1095=== added file 'app/assets/images/non-sprites/category-misc.svg'
1096--- app/assets/images/non-sprites/category-misc.svg 1970-01-01 00:00:00 +0000
1097+++ app/assets/images/non-sprites/category-misc.svg 2013-06-26 14:26:26 +0000
1098@@ -0,0 +1,90 @@
1099+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1100+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1101+
1102+<svg
1103+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1104+ xmlns:cc="http://creativecommons.org/ns#"
1105+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1106+ xmlns:svg="http://www.w3.org/2000/svg"
1107+ xmlns="http://www.w3.org/2000/svg"
1108+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1109+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1110+ width="47.94035"
1111+ height="47.94035"
1112+ id="svg4400"
1113+ version="1.1"
1114+ inkscape:version="0.48.3.1 r9886"
1115+ sodipodi:docname="New document 3">
1116+ <defs
1117+ id="defs4402" />
1118+ <sodipodi:namedview
1119+ id="base"
1120+ pagecolor="#ffffff"
1121+ bordercolor="#666666"
1122+ borderopacity="1.0"
1123+ inkscape:pageopacity="0.0"
1124+ inkscape:pageshadow="2"
1125+ inkscape:zoom="6.3664629"
1126+ inkscape:cx="37.208976"
1127+ inkscape:cy="23.504348"
1128+ inkscape:document-units="px"
1129+ inkscape:current-layer="layer1"
1130+ showgrid="false"
1131+ fit-margin-top="0"
1132+ fit-margin-left="0"
1133+ fit-margin-right="0"
1134+ fit-margin-bottom="0"
1135+ inkscape:window-width="1920"
1136+ inkscape:window-height="1029"
1137+ inkscape:window-x="0"
1138+ inkscape:window-y="24"
1139+ inkscape:window-maximized="1" />
1140+ <metadata
1141+ id="metadata4405">
1142+ <rdf:RDF>
1143+ <cc:Work
1144+ rdf:about="">
1145+ <dc:format>image/svg+xml</dc:format>
1146+ <dc:type
1147+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1148+ <dc:title></dc:title>
1149+ </cc:Work>
1150+ </rdf:RDF>
1151+ </metadata>
1152+ <g
1153+ inkscape:label="Layer 1"
1154+ inkscape:groupmode="layer"
1155+ id="layer1"
1156+ transform="translate(-630.31553,-996.96343)">
1157+ <g
1158+ style="stroke:#505050;stroke-opacity:1;display:inline"
1159+ id="g4736-4"
1160+ transform="matrix(1.4999966,0,0,1.4999966,-851.71092,-137.06381)">
1161+ <path
1162+ transform="matrix(1.3636397,0,0,1.3636397,-365.09423,-280.72982)"
1163+ d="m 1015,772 c 0,6.07513 -4.9249,11 -11,11 -6.07513,0 -11,-4.92487 -11,-11 0,-6.07513 4.92487,-11 11,-11 6.0751,0 11,4.92487 11,11 z"
1164+ sodipodi:ry="11"
1165+ sodipodi:rx="11"
1166+ sodipodi:cy="772"
1167+ sodipodi:cx="1004"
1168+ id="path4738-2"
1169+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.466663;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1170+ sodipodi:type="arc" />
1171+ <path
1172+ inkscape:connector-curvature="0"
1173+ id="path4740-2"
1174+ d="m 1003.6667,762.90178 0,18.19643"
1175+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1176+ <path
1177+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1178+ d="m 1011.8793,767.45089 -15.75859,9.09821"
1179+ id="path4742-7"
1180+ inkscape:connector-curvature="0" />
1181+ <path
1182+ inkscape:connector-curvature="0"
1183+ id="path4744-6"
1184+ d="m 1011.8793,776.54911 -15.75858,-9.09823"
1185+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1186+ </g>
1187+ </g>
1188+</svg>
1189
1190=== added directory 'app/assets/images/non-sprites/category_icons'
1191=== added file 'app/assets/images/non-sprites/category_icons/category-app-server.svg'
1192--- app/assets/images/non-sprites/category_icons/category-app-server.svg 1970-01-01 00:00:00 +0000
1193+++ app/assets/images/non-sprites/category_icons/category-app-server.svg 2013-06-26 14:26:26 +0000
1194@@ -0,0 +1,124 @@
1195+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1196+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1197+
1198+<svg
1199+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1200+ xmlns:cc="http://creativecommons.org/ns#"
1201+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1202+ xmlns:svg="http://www.w3.org/2000/svg"
1203+ xmlns="http://www.w3.org/2000/svg"
1204+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1205+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1206+ width="47.94035"
1207+ height="47.94035"
1208+ id="svg4400"
1209+ version="1.1"
1210+ inkscape:version="0.48.3.1 r9886"
1211+ sodipodi:docname="category-app-server.svg">
1212+ <defs
1213+ id="defs4402" />
1214+ <sodipodi:namedview
1215+ id="base"
1216+ pagecolor="#ffffff"
1217+ bordercolor="#666666"
1218+ borderopacity="1.0"
1219+ inkscape:pageopacity="0.0"
1220+ inkscape:pageshadow="2"
1221+ inkscape:zoom="5.0931703"
1222+ inkscape:cx="85.779492"
1223+ inkscape:cy="6.1041666"
1224+ inkscape:document-units="px"
1225+ inkscape:current-layer="layer1"
1226+ showgrid="true"
1227+ fit-margin-top="0"
1228+ fit-margin-left="0"
1229+ fit-margin-right="0"
1230+ fit-margin-bottom="0"
1231+ inkscape:window-width="1920"
1232+ inkscape:window-height="1029"
1233+ inkscape:window-x="0"
1234+ inkscape:window-y="24"
1235+ inkscape:window-maximized="1">
1236+ <inkscape:grid
1237+ type="xygrid"
1238+ id="grid4469"
1239+ empspacing="8"
1240+ visible="true"
1241+ enabled="true"
1242+ snapvisiblegridlinesonly="true" />
1243+ </sodipodi:namedview>
1244+ <metadata
1245+ id="metadata4405">
1246+ <rdf:RDF>
1247+ <cc:Work
1248+ rdf:about="">
1249+ <dc:format>image/svg+xml</dc:format>
1250+ <dc:type
1251+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1252+ <dc:title></dc:title>
1253+ </cc:Work>
1254+ </rdf:RDF>
1255+ </metadata>
1256+ <g
1257+ inkscape:label="Layer 1"
1258+ inkscape:groupmode="layer"
1259+ id="layer1"
1260+ transform="translate(-630.31553,-996.96343)">
1261+ <g
1262+ style="stroke:#505050;stroke-opacity:1;display:inline"
1263+ id="g4781-3"
1264+ transform="matrix(1.5005353,0,0,1.4833177,-852.22207,-34.476776)">
1265+ <path
1266+ sodipodi:nodetypes="ccccc"
1267+ inkscape:connector-curvature="0"
1268+ id="path4783-4"
1269+ d="m 996.33998,725.99069 7.65092,-3.80699 0,-8.70167 -7.65092,3.26312 z"
1270+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:2.01851225;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1271+ <path
1272+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.982494;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1273+ d="m 996.35027,726.0088 -7.35894,-3.81802 0,-8.72686 7.35894,3.27257 z"
1274+ id="path4785-7"
1275+ inkscape:connector-curvature="0"
1276+ sodipodi:nodetypes="ccccc" />
1277+ <path
1278+ inkscape:connector-curvature="0"
1279+ id="path4787-1"
1280+ d="m 996.50004,716.7407 7.50006,-3.26798 -7.50006,-3.81266 -7.50001,3.81266 z"
1281+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1282+ <path
1283+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.98247516;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1284+ d="m 1011.6499,726.00881 7.3589,-3.81802 0,-8.72688 -7.3589,3.27258 z"
1285+ id="path4789-0"
1286+ inkscape:connector-curvature="0"
1287+ sodipodi:nodetypes="ccccc" />
1288+ <path
1289+ sodipodi:nodetypes="ccccc"
1290+ inkscape:connector-curvature="0"
1291+ id="path4791-1"
1292+ d="m 1011.6602,725.99068 -7.651,-3.80699 0,-8.70165 7.651,3.26312 z"
1293+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.01853108;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1294+ <path
1295+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1296+ d="m 1011.5,716.7407 7.5001,-3.26798 -7.5001,-3.81266 -7.5,3.81266 z"
1297+ id="path4793-3"
1298+ inkscape:connector-curvature="0" />
1299+ <path
1300+ sodipodi:nodetypes="ccccc"
1301+ inkscape:connector-curvature="0"
1302+ id="path4795-6"
1303+ d="m 1003.6526,713.34891 7.3564,-3.81813 0,-8.7271 -7.3564,3.27267 z"
1304+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.9821583;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1305+ <path
1306+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.01884151;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1307+ d="m 1003.6629,713.33046 -7.65353,-3.8069 0,-8.70143 7.65353,3.26304 z"
1308+ id="path4797-0"
1309+ inkscape:connector-curvature="0"
1310+ sodipodi:nodetypes="ccccc" />
1311+ <path
1312+ inkscape:connector-curvature="0"
1313+ id="path4799-9"
1314+ d="m 1003.5,704.08064 7.5001,-3.26799 L 1003.5,697 996,700.81265 z"
1315+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1316+ </g>
1317+ </g>
1318+</svg>
1319
1320=== added file 'app/assets/images/non-sprites/category_icons/category-application.svg'
1321--- app/assets/images/non-sprites/category_icons/category-application.svg 1970-01-01 00:00:00 +0000
1322+++ app/assets/images/non-sprites/category_icons/category-application.svg 2013-06-26 14:26:26 +0000
1323@@ -0,0 +1,90 @@
1324+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1325+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1326+
1327+<svg
1328+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1329+ xmlns:cc="http://creativecommons.org/ns#"
1330+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1331+ xmlns:svg="http://www.w3.org/2000/svg"
1332+ xmlns="http://www.w3.org/2000/svg"
1333+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1334+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1335+ width="47.94035"
1336+ height="47.94035"
1337+ id="svg4400"
1338+ version="1.1"
1339+ inkscape:version="0.48.3.1 r9886"
1340+ sodipodi:docname="category-application.svg">
1341+ <defs
1342+ id="defs4402" />
1343+ <sodipodi:namedview
1344+ id="base"
1345+ pagecolor="#ffffff"
1346+ bordercolor="#666666"
1347+ borderopacity="1.0"
1348+ inkscape:pageopacity="0.0"
1349+ inkscape:pageshadow="2"
1350+ inkscape:zoom="5.0931703"
1351+ inkscape:cx="85.779492"
1352+ inkscape:cy="6.1041666"
1353+ inkscape:document-units="px"
1354+ inkscape:current-layer="layer1"
1355+ showgrid="true"
1356+ fit-margin-top="0"
1357+ fit-margin-left="0"
1358+ fit-margin-right="0"
1359+ fit-margin-bottom="0"
1360+ inkscape:window-width="1920"
1361+ inkscape:window-height="1029"
1362+ inkscape:window-x="0"
1363+ inkscape:window-y="24"
1364+ inkscape:window-maximized="1">
1365+ <inkscape:grid
1366+ type="xygrid"
1367+ id="grid4469"
1368+ empspacing="8"
1369+ visible="true"
1370+ enabled="true"
1371+ snapvisiblegridlinesonly="true" />
1372+ </sodipodi:namedview>
1373+ <metadata
1374+ id="metadata4405">
1375+ <rdf:RDF>
1376+ <cc:Work
1377+ rdf:about="">
1378+ <dc:format>image/svg+xml</dc:format>
1379+ <dc:type
1380+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1381+ <dc:title></dc:title>
1382+ </cc:Work>
1383+ </rdf:RDF>
1384+ </metadata>
1385+ <g
1386+ inkscape:label="Layer 1"
1387+ inkscape:groupmode="layer"
1388+ id="layer1"
1389+ transform="translate(-630.31553,-996.96343)">
1390+ <g
1391+ style="stroke:#505050;stroke-width:0.99999881;stroke-opacity:1;display:inline"
1392+ id="g4746-5"
1393+ transform="matrix(1.5000001,0,0,1.4999966,-852.18459,-227.09334)">
1394+ <path
1395+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1396+ d="m 1004,847 14,-7 0,-16 -14,6 z"
1397+ id="path4748-9"
1398+ inkscape:connector-curvature="0"
1399+ sodipodi:nodetypes="ccccc" />
1400+ <path
1401+ sodipodi:nodetypes="ccccc"
1402+ inkscape:connector-curvature="0"
1403+ id="path4750-9"
1404+ d="m 1004,847 -14,-7 0,-16 14,6 z"
1405+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1406+ <path
1407+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.99999762;stroke-linejoin:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1408+ d="m 1004,830 14,-6 -14,-7 -14,7 z"
1409+ id="path4752-1"
1410+ inkscape:connector-curvature="0" />
1411+ </g>
1412+ </g>
1413+</svg>
1414
1415=== added file 'app/assets/images/non-sprites/category_icons/category-cache-proxy.svg'
1416--- app/assets/images/non-sprites/category_icons/category-cache-proxy.svg 1970-01-01 00:00:00 +0000
1417+++ app/assets/images/non-sprites/category_icons/category-cache-proxy.svg 2013-06-26 14:26:26 +0000
1418@@ -0,0 +1,119 @@
1419+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1420+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1421+
1422+<svg
1423+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1424+ xmlns:cc="http://creativecommons.org/ns#"
1425+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1426+ xmlns:svg="http://www.w3.org/2000/svg"
1427+ xmlns="http://www.w3.org/2000/svg"
1428+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1429+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1430+ width="47.94035"
1431+ height="47.94035"
1432+ id="svg4400"
1433+ version="1.1"
1434+ inkscape:version="0.48.3.1 r9886"
1435+ sodipodi:docname="category-cache-proxy.svg">
1436+ <defs
1437+ id="defs4402" />
1438+ <sodipodi:namedview
1439+ id="base"
1440+ pagecolor="#ffffff"
1441+ bordercolor="#666666"
1442+ borderopacity="1.0"
1443+ inkscape:pageopacity="0.0"
1444+ inkscape:pageshadow="2"
1445+ inkscape:zoom="5.0931703"
1446+ inkscape:cx="85.779492"
1447+ inkscape:cy="6.1041666"
1448+ inkscape:document-units="px"
1449+ inkscape:current-layer="layer1"
1450+ showgrid="true"
1451+ fit-margin-top="0"
1452+ fit-margin-left="0"
1453+ fit-margin-right="0"
1454+ fit-margin-bottom="0"
1455+ inkscape:window-width="1920"
1456+ inkscape:window-height="1029"
1457+ inkscape:window-x="0"
1458+ inkscape:window-y="24"
1459+ inkscape:window-maximized="1">
1460+ <inkscape:grid
1461+ type="xygrid"
1462+ id="grid4469"
1463+ empspacing="8"
1464+ visible="true"
1465+ enabled="true"
1466+ snapvisiblegridlinesonly="true" />
1467+ </sodipodi:namedview>
1468+ <metadata
1469+ id="metadata4405">
1470+ <rdf:RDF>
1471+ <cc:Work
1472+ rdf:about="">
1473+ <dc:format>image/svg+xml</dc:format>
1474+ <dc:type
1475+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1476+ <dc:title></dc:title>
1477+ </cc:Work>
1478+ </rdf:RDF>
1479+ </metadata>
1480+ <g
1481+ inkscape:label="Layer 1"
1482+ inkscape:groupmode="layer"
1483+ id="layer1"
1484+ transform="translate(-630.31553,-996.96343)">
1485+ <path
1486+ sodipodi:type="arc"
1487+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1488+ id="path3598"
1489+ sodipodi:cx="1012"
1490+ sodipodi:cy="420"
1491+ sodipodi:rx="4"
1492+ sodipodi:ry="4"
1493+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
1494+ transform="matrix(-1.625,0,0,1.625,2289.3155,329.90378)" />
1495+ <path
1496+ transform="matrix(-1.625,0,0,1.625,2316.3155,329.90378)"
1497+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
1498+ sodipodi:ry="4"
1499+ sodipodi:rx="4"
1500+ sodipodi:cy="420"
1501+ sodipodi:cx="1012"
1502+ id="path4368"
1503+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1504+ sodipodi:type="arc" />
1505+ <path
1506+ transform="matrix(-3.2499973,0,0,3.2499973,3933.8128,-352.59507)"
1507+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
1508+ sodipodi:ry="4"
1509+ sodipodi:rx="4"
1510+ sodipodi:cy="420"
1511+ sodipodi:cx="1012"
1512+ id="path4370"
1513+ style="color:#000000;fill:none;stroke:#505050;stroke-width:0.92307782;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1514+ sodipodi:type="arc" />
1515+ <path
1516+ sodipodi:type="arc"
1517+ style="color:#000000;fill:#505050;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1518+ id="path4372"
1519+ sodipodi:cx="1012"
1520+ sodipodi:cy="420"
1521+ sodipodi:rx="4"
1522+ sodipodi:ry="4"
1523+ d="m 1016,420 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
1524+ transform="matrix(-1.5,0,0,1.5,2178.3155,407.90378)" />
1525+ <path
1526+ style="color:#000000;fill:none;stroke:#505050;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.70000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1527+ d="m 672.31551,1012.4038 -28.00004,0"
1528+ id="path4374"
1529+ inkscape:connector-curvature="0" />
1530+ <path
1531+ style="color:#000000;fill:none;stroke:#505050;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3.70000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1532+ d="m 661.31551,1039.9038 -17.00004,-28"
1533+ id="path4376"
1534+ inkscape:connector-curvature="0"
1535+ sodipodi:nodetypes="cc" />
1536+ </g>
1537+</svg>
1538
1539=== added file 'app/assets/images/non-sprites/category_icons/category-database.svg'
1540--- app/assets/images/non-sprites/category_icons/category-database.svg 1970-01-01 00:00:00 +0000
1541+++ app/assets/images/non-sprites/category_icons/category-database.svg 2013-06-26 14:26:26 +0000
1542@@ -0,0 +1,115 @@
1543+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1544+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1545+
1546+<svg
1547+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1548+ xmlns:cc="http://creativecommons.org/ns#"
1549+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1550+ xmlns:svg="http://www.w3.org/2000/svg"
1551+ xmlns="http://www.w3.org/2000/svg"
1552+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1553+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1554+ width="47.94035"
1555+ height="47.94035"
1556+ id="svg4400"
1557+ version="1.1"
1558+ inkscape:version="0.48.3.1 r9886"
1559+ sodipodi:docname="category-database.svg">
1560+ <defs
1561+ id="defs4402" />
1562+ <sodipodi:namedview
1563+ id="base"
1564+ pagecolor="#ffffff"
1565+ bordercolor="#666666"
1566+ borderopacity="1.0"
1567+ inkscape:pageopacity="0.0"
1568+ inkscape:pageshadow="2"
1569+ inkscape:zoom="5.0931703"
1570+ inkscape:cx="85.779492"
1571+ inkscape:cy="6.1041666"
1572+ inkscape:document-units="px"
1573+ inkscape:current-layer="layer1"
1574+ showgrid="true"
1575+ fit-margin-top="0"
1576+ fit-margin-left="0"
1577+ fit-margin-right="0"
1578+ fit-margin-bottom="0"
1579+ inkscape:window-width="1920"
1580+ inkscape:window-height="1029"
1581+ inkscape:window-x="0"
1582+ inkscape:window-y="24"
1583+ inkscape:window-maximized="1">
1584+ <inkscape:grid
1585+ type="xygrid"
1586+ id="grid4469"
1587+ empspacing="8"
1588+ visible="true"
1589+ enabled="true"
1590+ snapvisiblegridlinesonly="true" />
1591+ </sodipodi:namedview>
1592+ <metadata
1593+ id="metadata4405">
1594+ <rdf:RDF>
1595+ <cc:Work
1596+ rdf:about="">
1597+ <dc:format>image/svg+xml</dc:format>
1598+ <dc:type
1599+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1600+ <dc:title></dc:title>
1601+ </cc:Work>
1602+ </rdf:RDF>
1603+ </metadata>
1604+ <g
1605+ inkscape:label="Layer 1"
1606+ inkscape:groupmode="layer"
1607+ id="layer1"
1608+ transform="translate(-630.31553,-996.96343)">
1609+ <g
1610+ style="stroke:#505050;stroke-width:1;stroke-opacity:1;display:inline"
1611+ id="g4760-2"
1612+ transform="matrix(1.4999997,0,0,1.4999997,-851.68417,-316.59602)">
1613+ <path
1614+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-288.66662)"
1615+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -9.1127,0 -16.5,-1.34315 -16.5,-3 0,-1.65685 7.3873,-3 16.5,-3 9.1127,0 16.5,1.34315 16.5,3 z"
1616+ sodipodi:ry="3"
1617+ sodipodi:rx="16.5"
1618+ sodipodi:cy="877"
1619+ sodipodi:cx="1004.5"
1620+ id="path4762-0"
1621+ style="color:#000000;fill:#505050;fill-opacity:1;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1622+ sodipodi:type="arc" />
1623+ <path
1624+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1625+ d="m 991,881 0,21.66665 0,0 c 0,2.20914 5.82029,4 13,4 7.1797,0 13,-1.79086 13,-4 L 1017,881"
1626+ id="path4764-6"
1627+ inkscape:connector-curvature="0"
1628+ sodipodi:nodetypes="cccssc" />
1629+ <path
1630+ sodipodi:type="arc"
1631+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1632+ id="path4766-6"
1633+ sodipodi:cx="1004.5"
1634+ sodipodi:cy="877"
1635+ sodipodi:rx="16.5"
1636+ sodipodi:ry="3"
1637+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -7.46799,0 -14.00492,-0.91199 -15.93778,-2.22354"
1638+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-281.33332)"
1639+ sodipodi:start="0"
1640+ sodipodi:end="2.8797933"
1641+ sodipodi:open="true" />
1642+ <path
1643+ sodipodi:open="true"
1644+ sodipodi:end="2.8797933"
1645+ sodipodi:start="0"
1646+ transform="matrix(0.78787872,0,0,1.3333333,212.57582,-273.99995)"
1647+ d="m 1021,877 c 0,1.65685 -7.3873,3 -16.5,3 -7.46799,0 -14.00492,-0.91199 -15.93778,-2.22354"
1648+ sodipodi:ry="3"
1649+ sodipodi:rx="16.5"
1650+ sodipodi:cy="877"
1651+ sodipodi:cx="1004.5"
1652+ id="path4768-5"
1653+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.95133114;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1654+ sodipodi:type="arc" />
1655+ </g>
1656+ </g>
1657+</svg>
1658
1659=== added file 'app/assets/images/non-sprites/category_icons/category-file-server.svg'
1660--- app/assets/images/non-sprites/category_icons/category-file-server.svg 1970-01-01 00:00:00 +0000
1661+++ app/assets/images/non-sprites/category_icons/category-file-server.svg 2013-06-26 14:26:26 +0000
1662@@ -0,0 +1,74 @@
1663+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1664+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1665+
1666+<svg
1667+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1668+ xmlns:cc="http://creativecommons.org/ns#"
1669+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1670+ xmlns:svg="http://www.w3.org/2000/svg"
1671+ xmlns="http://www.w3.org/2000/svg"
1672+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1673+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1674+ width="47.94035"
1675+ height="47.94035"
1676+ id="svg4400"
1677+ version="1.1"
1678+ inkscape:version="0.48.3.1 r9886"
1679+ sodipodi:docname="category-file-server.svg">
1680+ <defs
1681+ id="defs4402" />
1682+ <sodipodi:namedview
1683+ id="base"
1684+ pagecolor="#ffffff"
1685+ bordercolor="#666666"
1686+ borderopacity="1.0"
1687+ inkscape:pageopacity="0.0"
1688+ inkscape:pageshadow="2"
1689+ inkscape:zoom="5.0931703"
1690+ inkscape:cx="85.779492"
1691+ inkscape:cy="6.1041666"
1692+ inkscape:document-units="px"
1693+ inkscape:current-layer="layer1"
1694+ showgrid="true"
1695+ fit-margin-top="0"
1696+ fit-margin-left="0"
1697+ fit-margin-right="0"
1698+ fit-margin-bottom="0"
1699+ inkscape:window-width="1920"
1700+ inkscape:window-height="1029"
1701+ inkscape:window-x="0"
1702+ inkscape:window-y="24"
1703+ inkscape:window-maximized="1">
1704+ <inkscape:grid
1705+ type="xygrid"
1706+ id="grid4469"
1707+ empspacing="8"
1708+ visible="true"
1709+ enabled="true"
1710+ snapvisiblegridlinesonly="true" />
1711+ </sodipodi:namedview>
1712+ <metadata
1713+ id="metadata4405">
1714+ <rdf:RDF>
1715+ <cc:Work
1716+ rdf:about="">
1717+ <dc:format>image/svg+xml</dc:format>
1718+ <dc:type
1719+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1720+ <dc:title></dc:title>
1721+ </cc:Work>
1722+ </rdf:RDF>
1723+ </metadata>
1724+ <g
1725+ inkscape:label="Layer 1"
1726+ inkscape:groupmode="layer"
1727+ id="layer1"
1728+ transform="translate(-630.31553,-996.96343)">
1729+ <path
1730+ inkscape:connector-curvature="0"
1731+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#505050;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
1732+ d="m 634.81553,996.90381 c -2.46705,0 -4.5,2.03295 -4.5,4.49999 l 0,3 0,1.5 0,25.5 c 0,2.4671 2.03295,4.5 4.5,4.5 l 9.5,0 0,-3 -9.5,0 c -0.85695,0 -1.5,-0.643 -1.5,-1.5 l 0,-25.5 0,-1.5 0,-3 c 0,-0.857 0.64305,-1.49999 1.5,-1.49999 l 12,0 c 0.8569,0 1.5,0.64299 1.5,1.49999 -0.002,0.047 -0.002,0.094 0,0.1406 l 0,1.3593 1.2657,0 0.2343,0 24,0 c 0.8569,0 1.5,0.6431 1.5,1.5 l 0,27 0,4.2188 c 1.7336,-0.6275 3,-2.2811 3,-4.2188 l 0,-27 c 0,-2.467 -2.0329,-4.49996 -4.5,-4.49996 l -23.1093,0 c -0.6631,-1.66422 -2.0029,-3 -3.8907,-3 l -12,0 z m 15.32985,16.99989 c -1.662,0 -3,1.338 -3,3 l 0,25.0001 c 0,1.662 1.338,3 3,3 l 19.17015,0 c 1.662,0 3,-1.338 3,-3 l 0,-25 c 0,-1.662 -1.338,-3 -3,-3 z m 2.41915,4.0001 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 13.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7925 -0.7215,1.5112 -1.506,1.5 l -13.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z m 0,6 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 13.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7926 -0.7215,1.5112 -1.506,1.5 l -13.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z m 0,6 c 0.016,-2e-4 0.031,-2e-4 0.047,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 0.047,0 0.094,0 0.141,0 l 7.775,0 c 0.7845,-0.011 1.506,0.7075 1.506,1.5 0,0.7926 -0.7215,1.5112 -1.506,1.5 l -7.775,0 c -0.7774,0.1238 -1.5963,-0.4779 -1.719,-1.2632 -0.1227,-0.7853 0.4727,-1.6126 1.2501,-1.7368 z"
1733+ id="rect4570-0"
1734+ sodipodi:nodetypes="csccssccssccssscccccssccssscsccsssssssssccccccsccsccccccccsccsccccccccsccscc" />
1735+ </g>
1736+</svg>
1737
1738=== added file 'app/assets/images/non-sprites/category_icons/category-misc.svg'
1739--- app/assets/images/non-sprites/category_icons/category-misc.svg 1970-01-01 00:00:00 +0000
1740+++ app/assets/images/non-sprites/category_icons/category-misc.svg 2013-06-26 14:26:26 +0000
1741@@ -0,0 +1,90 @@
1742+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1743+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1744+
1745+<svg
1746+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1747+ xmlns:cc="http://creativecommons.org/ns#"
1748+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1749+ xmlns:svg="http://www.w3.org/2000/svg"
1750+ xmlns="http://www.w3.org/2000/svg"
1751+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1752+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1753+ width="47.94035"
1754+ height="47.94035"
1755+ id="svg4400"
1756+ version="1.1"
1757+ inkscape:version="0.48.3.1 r9886"
1758+ sodipodi:docname="New document 3">
1759+ <defs
1760+ id="defs4402" />
1761+ <sodipodi:namedview
1762+ id="base"
1763+ pagecolor="#ffffff"
1764+ bordercolor="#666666"
1765+ borderopacity="1.0"
1766+ inkscape:pageopacity="0.0"
1767+ inkscape:pageshadow="2"
1768+ inkscape:zoom="6.3664629"
1769+ inkscape:cx="37.208976"
1770+ inkscape:cy="23.504348"
1771+ inkscape:document-units="px"
1772+ inkscape:current-layer="layer1"
1773+ showgrid="false"
1774+ fit-margin-top="0"
1775+ fit-margin-left="0"
1776+ fit-margin-right="0"
1777+ fit-margin-bottom="0"
1778+ inkscape:window-width="1920"
1779+ inkscape:window-height="1029"
1780+ inkscape:window-x="0"
1781+ inkscape:window-y="24"
1782+ inkscape:window-maximized="1" />
1783+ <metadata
1784+ id="metadata4405">
1785+ <rdf:RDF>
1786+ <cc:Work
1787+ rdf:about="">
1788+ <dc:format>image/svg+xml</dc:format>
1789+ <dc:type
1790+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1791+ <dc:title></dc:title>
1792+ </cc:Work>
1793+ </rdf:RDF>
1794+ </metadata>
1795+ <g
1796+ inkscape:label="Layer 1"
1797+ inkscape:groupmode="layer"
1798+ id="layer1"
1799+ transform="translate(-630.31553,-996.96343)">
1800+ <g
1801+ style="stroke:#505050;stroke-opacity:1;display:inline"
1802+ id="g4736-4"
1803+ transform="matrix(1.4999966,0,0,1.4999966,-851.71092,-137.06381)">
1804+ <path
1805+ transform="matrix(1.3636397,0,0,1.3636397,-365.09423,-280.72982)"
1806+ d="m 1015,772 c 0,6.07513 -4.9249,11 -11,11 -6.07513,0 -11,-4.92487 -11,-11 0,-6.07513 4.92487,-11 11,-11 6.0751,0 11,4.92487 11,11 z"
1807+ sodipodi:ry="11"
1808+ sodipodi:rx="11"
1809+ sodipodi:cy="772"
1810+ sodipodi:cx="1004"
1811+ id="path4738-2"
1812+ style="color:#000000;fill:none;stroke:#505050;stroke-width:1.466663;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1813+ sodipodi:type="arc" />
1814+ <path
1815+ inkscape:connector-curvature="0"
1816+ id="path4740-2"
1817+ d="m 1003.6667,762.90178 0,18.19643"
1818+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1819+ <path
1820+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1821+ d="m 1011.8793,767.45089 -15.75859,9.09821"
1822+ id="path4742-7"
1823+ inkscape:connector-curvature="0" />
1824+ <path
1825+ inkscape:connector-curvature="0"
1826+ id="path4744-6"
1827+ d="m 1011.8793,776.54911 -15.75858,-9.09823"
1828+ style="color:#000000;fill:none;stroke:#505050;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
1829+ </g>
1830+ </g>
1831+</svg>
1832
1833=== added file 'app/assets/images/twitter-click.png'
1834Binary files app/assets/images/twitter-click.png 1970-01-01 00:00:00 +0000 and app/assets/images/twitter-click.png 2013-06-26 14:26:26 +0000 differ
1835=== added file 'app/assets/images/twitter-hover.png'
1836Binary files app/assets/images/twitter-hover.png 1970-01-01 00:00:00 +0000 and app/assets/images/twitter-hover.png 2013-06-26 14:26:26 +0000 differ
1837=== added file 'app/assets/images/twitter-normal.png'
1838Binary files app/assets/images/twitter-normal.png 1970-01-01 00:00:00 +0000 and app/assets/images/twitter-normal.png 2013-06-26 14:26:26 +0000 differ
1839=== added file 'app/assets/javascripts/Object.observe.poly.js'
1840--- app/assets/javascripts/Object.observe.poly.js 1970-01-01 00:00:00 +0000
1841+++ app/assets/javascripts/Object.observe.poly.js 2013-06-26 14:26:26 +0000
1842@@ -0,0 +1,246 @@
1843+/*
1844+ Tested against Chromium build with Object.observe and acts EXACTLY the same,
1845+ though Chromium build is MUCH faster
1846+
1847+ Trying to stay as close to the spec as possible,
1848+ this is a work in progress, feel free to comment/update
1849+
1850+ Specification:
1851+ http://wiki.ecmascript.org/doku.php?id=harmony:observe
1852+
1853+ Built using parts of:
1854+ https://github.com/tvcutsem/harmony-reflect/blob/master/examples/observer.js
1855+
1856+ Limits so far;
1857+ Built using polling... Will update again with polling/getter&setters to make things better at some point
1858+*/
1859+'use strict';
1860+if (!Object.observe) {
1861+ (function(extend, global) {
1862+ var isCallable = (function(toString) {
1863+ var s = toString.call(toString),
1864+ u = typeof u;
1865+ return typeof global.alert === 'object' ?
1866+ function(f) {
1867+ return s === toString.call(f) || (!!f && typeof f.toString == u && typeof f.valueOf == u && /^\s*\bfunction\b/.test('' + f));
1868+ } :
1869+ function(f) {
1870+ return s === toString.call(f);
1871+ }
1872+;
1873+ })(extend.prototype.toString);
1874+ var isNumeric = function(n) {
1875+ return !isNaN(parseFloat(n)) && isFinite(n);
1876+ };
1877+ var sameValue = function(x, y) {
1878+ if (x === y) {
1879+ return x !== 0 || 1 / x === 1 / y;
1880+ }
1881+ return x !== x && y !== y;
1882+ };
1883+ var isAccessorDescriptor = function(desc) {
1884+ if (typeof(desc) === 'undefined') {
1885+ return false;
1886+ }
1887+ return ('get' in desc || 'set' in desc);
1888+ };
1889+ var isDataDescriptor = function(desc) {
1890+ if (typeof(desc) === 'undefined') {
1891+ return false;
1892+ }
1893+ return ('value' in desc || 'writable' in desc);
1894+ };
1895+
1896+ var validateArguments = function(O, callback) {
1897+ if (typeof(O) !== 'object') {
1898+ // Throw Error
1899+ throw new TypeError('Object.observeObject called on non-object');
1900+ }
1901+ if (isCallable(callback) === false) {
1902+ // Throw Error
1903+ throw new TypeError('Object.observeObject: Expecting function');
1904+ }
1905+ if (Object.isFrozen(callback) === true) {
1906+ // Throw Error
1907+ throw new TypeError('Object.observeObject: Expecting unfrozen function');
1908+ }
1909+ };
1910+
1911+ var Observer = (function() {
1912+ var wraped = [];
1913+ var Observer = function(O, callback) {
1914+ validateArguments(O, callback);
1915+ Object.getNotifier(O).addListener(callback);
1916+ if (wraped.indexOf(O) === -1) {
1917+ wraped.push(O);
1918+ }else {
1919+ Object.getNotifier(O)._checkPropertyListing();
1920+ }
1921+ };
1922+
1923+ Observer.prototype.deliverChangeRecords = function(O) {
1924+ Object.getNotifier(O).deliverChangeRecords();
1925+ };
1926+
1927+ wraped.lastScanned = 0;
1928+ var f = (function(wrapped) {
1929+ return function() {
1930+ var i = 0, l = wrapped.length, startTime = new Date(), takingTooLong = false;
1931+ for (i = wrapped.lastScanned; (i < l) && (!takingTooLong); i++) {
1932+ Object.getNotifier(wrapped[i])._checkPropertyListing();
1933+ takingTooLong = ((new Date()) - startTime) > 100; // make sure we don't take more than 100 milliseconds to scan all objects
1934+ }
1935+ wrapped.lastScanned = i < l ? i : 0; // reset wrapped so we can make sure that we pick things back up
1936+ setTimeout(f, 100);
1937+ };
1938+ })(wraped);
1939+ setTimeout(f, 100);
1940+
1941+ return Observer;
1942+ })();
1943+
1944+ var Notifier = function(watching) {
1945+ var _listeners = [], _updates = [], _updater = false, properties = [], values = [];
1946+ var self = this;
1947+ Object.defineProperty(self, '_watching', {
1948+ get: (function(watched) {
1949+ return function() {
1950+ return watched;
1951+ };
1952+ })(watching)
1953+ });
1954+ var wrapProperty = function(object, prop) {
1955+ var propType = typeof(object[prop]), descriptor = Object.getOwnPropertyDescriptor(object, prop);
1956+ if ((prop === 'getNotifier') || isAccessorDescriptor(descriptor) || (!descriptor.enumerable)) {
1957+ return false;
1958+ }
1959+ if ((object instanceof Array) && isNumeric(prop)) {
1960+ var idx = properties.length;
1961+ properties[idx] = prop;
1962+ values[idx] = object[prop];
1963+ return true;
1964+ }
1965+ (function(idx, prop) {
1966+ properties[idx] = prop;
1967+ values[idx] = object[prop];
1968+ Object.defineProperty(object, prop, {
1969+ get: function() {
1970+ return values[idx];
1971+ },
1972+ set: function(value) {
1973+ if (!sameValue(values[idx], value)) {
1974+ Object.getNotifier(object).queueUpdate(object, prop, 'updated', values[idx]);
1975+ values[idx] = value;
1976+ }
1977+ }
1978+ });
1979+ })(properties.length, prop);
1980+ return true;
1981+ };
1982+ self._checkPropertyListing = function(dontQueueUpdates) {
1983+ var object = self._watching, keys = Object.keys(object), i = 0, l = keys.length;
1984+ var newKeys = [], oldKeys = properties.slice(0), updates = [];
1985+ var prop, queueUpdates = !dontQueueUpdates, propType, value, idx;
1986+
1987+ for (i = 0; i < l; i++) {
1988+ prop = keys[i];
1989+ value = object[prop];
1990+ propType = typeof(value);
1991+ if ((idx = properties.indexOf(prop)) === -1) {
1992+ if (wrapProperty(object, prop) && queueUpdates) {
1993+ self.queueUpdate(object, prop, 'new', null, object[prop]);
1994+ }
1995+ }else {
1996+ if ((object instanceof Array) && (isNumeric(prop))) {
1997+ if (values[idx] !== value) {
1998+ if (queueUpdates) {
1999+ self.queueUpdate(object, prop, 'updated', values[idx], value);
2000+ }
2001+ values[idx] = value;
2002+ }
2003+ }
2004+ oldKeys.splice(oldKeys.indexOf(prop), 1);
2005+ }
2006+ }
2007+ if (queueUpdates) {
2008+ l = oldKeys.length;
2009+ for (i = 0; i < l; i++) {
2010+ idx = properties.indexOf(oldKeys[i]);
2011+ self.queueUpdate(object, oldKeys[i], 'deleted', values[idx]);
2012+ properties.splice(idx, 1);
2013+ values.splice(idx, 1);
2014+ }
2015+ }
2016+ };
2017+ self.addListener = function(callback) {
2018+ var idx = _listeners.indexOf(callback);
2019+ if (idx === -1) {
2020+ _listeners.push(callback);
2021+ }
2022+ };
2023+ self.removeListener = function(callback) {
2024+ var idx = _listeners.indexOf(callback);
2025+ if (idx > -1) {
2026+ _listeners.splice(idx, 1);
2027+ }
2028+ };
2029+ self.listeners = function() {
2030+ return _listeners;
2031+ };
2032+ self.queueUpdate = function(what, prop, type, was) {
2033+ this.queueUpdates([{
2034+ type: type,
2035+ object: what,
2036+ name: prop,
2037+ oldValue: was
2038+ }]);
2039+ };
2040+ self.queueUpdates = function(updates) {
2041+ var self = this, i = 0, l = updates.length || 0, update;
2042+ for (i = 0; i < l; i++) {
2043+ update = updates[i];
2044+ _updates.push(update);
2045+ }
2046+ if (_updater) {
2047+ clearTimeout(_updater);
2048+ }
2049+ _updater = setTimeout(function() {
2050+ _updater = false;
2051+ self.deliverChangeRecords();
2052+ }, 100);
2053+ };
2054+ self.deliverChangeRecords = function() {
2055+ var i = 0, l = _listeners.length, keepRunning = true;
2056+ for (i = 0; i < l && keepRunning; i++) {
2057+ if (typeof(_listeners[i]) === 'function') {
2058+ if (_listeners[i] === console.log) {
2059+ console.log(_updates);
2060+ }else {
2061+ keepRunning = !(_listeners[i](_updates));
2062+ }
2063+ }
2064+ }
2065+ _updates = [];
2066+ };
2067+ self._checkPropertyListing(true);
2068+ };
2069+
2070+ var _notifiers = [], _indexes = [];
2071+ extend.getNotifier = function(O) {
2072+ var idx = _indexes.indexOf(O), notifier = idx > -1 ? _notifiers[idx] : false;
2073+ if (!notifier) {
2074+ idx = _indexes.length;
2075+ _indexes[idx] = O;
2076+ notifier = _notifiers[idx] = new Notifier(O);
2077+ }
2078+ return notifier;
2079+ };
2080+ extend.observe = function(O, callback) {
2081+ return new Observer(O, callback);
2082+ };
2083+ extend.unobserve = function(O, callback) {
2084+ validateArguments(O, callback);
2085+ extend.getNotifier(O).removeListener(callback);
2086+ };
2087+ })(Object, this);
2088+}
2089
2090=== added file 'app/assets/javascripts/app-cookies-extension.js'
2091--- app/assets/javascripts/app-cookies-extension.js 1970-01-01 00:00:00 +0000
2092+++ app/assets/javascripts/app-cookies-extension.js 2013-06-26 14:26:26 +0000
2093@@ -0,0 +1,75 @@
2094+/*
2095+This file is part of the Juju GUI, which lets users view and manage Juju
2096+environments within a graphical interface (https://launchpad.net/juju-gui).
2097+Copyright (C) 2013 Canonical Ltd.
2098+
2099+This program is free software: you can redistribute it and/or modify it under
2100+the terms of the GNU Affero General Public License version 3, as published by
2101+the Free Software Foundation.
2102+
2103+This program is distributed in the hope that it will be useful, but WITHOUT
2104+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2105+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
2106+General Public License for more details.
2107+
2108+You should have received a copy of the GNU Affero General Public License along
2109+with this program. If not, see <http://www.gnu.org/licenses/>.
2110+*/
2111+
2112+'use strict';
2113+
2114+YUI.add('app-cookies-extension', function(Y) {
2115+
2116+ /**
2117+ A cookies warning handler, enabled when using analytics.
2118+
2119+ @class Cookies
2120+ @extension App
2121+ @param {Object} test_node DOM node only passed in in tests.
2122+ */
2123+ function Cookies(test_node) {
2124+ this.node = test_node || Y.one('.cookie-policy');
2125+ }
2126+
2127+ Cookies.prototype = {
2128+
2129+ /**
2130+ Check that the user accepted cookie usage, and if not display a cookie
2131+ usage warning.
2132+
2133+ @method check
2134+ @return {undefined} Side-effects only.
2135+ */
2136+ check: function() {
2137+ var self = this;
2138+ if (Y.Cookie.get('_cookies_accepted') !== 'true') {
2139+ this.node.setStyle('display', 'block');
2140+ Y.one('.link-cta').once('click', function(evt) {
2141+ evt.preventDefault();
2142+ self.close();
2143+ });
2144+ }
2145+ },
2146+
2147+ /**
2148+ Close the cookie usage warning and set a cookie to denote user agreement.
2149+
2150+ @method close
2151+ @return {undefined} Side-effects only.
2152+ */
2153+ close: function() {
2154+ this.node.setStyle('display', 'none');
2155+ Y.Cookie.set('_cookies_accepted', 'true',
2156+ {expires: new Date('January 12, 2025')});
2157+ }
2158+
2159+ };
2160+
2161+ Y.namespace('juju').Cookies = Cookies;
2162+
2163+}, '0.1.0', {
2164+ requires: [
2165+ 'base',
2166+ 'cookie'
2167+ ]
2168+});
2169
2170=== modified file 'app/assets/javascripts/ns-routing-app-extension.js'
2171--- app/assets/javascripts/ns-routing-app-extension.js 2013-05-22 18:47:13 +0000
2172+++ app/assets/javascripts/ns-routing-app-extension.js 2013-06-26 14:26:26 +0000
2173@@ -61,9 +61,29 @@
2174 return result;
2175 }
2176
2177- var Routes = {
2178- pairs: function() {return pairs(this);}
2179- };
2180+ var Routes = (function() {
2181+ function Routes() {
2182+ return Object.create(this, {
2183+ defaultNamespacePresent: {
2184+ enumerable: false,
2185+ writable: true
2186+ },
2187+ hash: {
2188+ enumerable: false,
2189+ writable: true
2190+ },
2191+ search: {
2192+ enumerable: false,
2193+ writable: true
2194+ },
2195+ pairs: {
2196+ enumable: false,
2197+ value: function() {return pairs(this);}
2198+ }
2199+ });
2200+ }
2201+ return Routes;
2202+ })();
2203
2204 // Multi dimensional router (TARDIS).
2205 var _Router = {
2206@@ -81,6 +101,24 @@
2207 },
2208
2209 /**
2210+ Return the #hash portion of the URL if present.
2211+
2212+ @method getHash
2213+ @param {String} url to parse hash from.
2214+ @return {String} hash || null.
2215+ */
2216+ getHash: function(url) {
2217+ // This will return an array with three values the whole matched hash,
2218+ // the hash symbol, and then the hash without the hash.
2219+ // It is quoted to shush the linter about the .
2220+ var match = url.match('(#)(.[^?\/]*)?');
2221+ if (!match) {
2222+ return undefined;
2223+ }
2224+ return match[2];
2225+ },
2226+
2227+ /**
2228 * Split a URL into components, a subset of the
2229 * Location Object.
2230 *
2231@@ -94,6 +132,7 @@
2232 };
2233 var origin = this._regexUrlOrigin.exec(url);
2234 result.search = this.getQS(url);
2235+ result.hash = this.getHash(url);
2236
2237 if (origin) {
2238 // Take the match.
2239@@ -105,8 +144,9 @@
2240 }
2241
2242 if (result.search) {
2243- result.pathname = result.pathname.substr(0,
2244- (result.pathname.length - result.search.length) - 1);
2245+ result.pathname = result.pathname.substr(
2246+ 0,
2247+ (result.pathname.length - result.search.length) - 1);
2248 }
2249
2250 return result;
2251@@ -127,20 +167,23 @@
2252 **/
2253 parse: function(url, combineFlags) {
2254 combineFlags = Y.mix(this.combineFlags || {}, combineFlags, true);
2255- var result = Object.create(Routes, {
2256- defaultNamespacePresent: {
2257- enumerable: false,
2258- writable: true
2259- }
2260- });
2261+ var result = new Routes();
2262 var parts = this.split(url);
2263 url = parts.pathname;
2264+ result.hash = parts.hash;
2265+ result.search = parts.search;
2266
2267+ // If there was a hash we need to split it off url
2268+ if (result.hash) {
2269+ url = url.slice(0, url.indexOf('#'));
2270+ }
2271 parts = url.split(this._fragment);
2272- // > '/foo/bar'.split(this._fragment)
2273- // ["/foo/bar"]
2274- // > :baz:/foo/bar'.split(this._fragment)
2275- // ["", ":baz:", "/foo/bar"]
2276+ // Example output
2277+ // '/foo/bar'.split(this._fragment)
2278+ // ["/foo/bar"]
2279+ //
2280+ // ':baz:/foo/bar'.split(this._fragment)
2281+ // ["", ":baz:", "/foo/bar"]
2282 if (parts[0]) {
2283 // This is a URL fragment without a namespace.
2284 parts[0] = rtrim(parts[0], '/') + '/';
2285@@ -225,11 +268,20 @@
2286 });
2287
2288 url = slash(url);
2289+
2290+ if (components.hash) {
2291+ url += '#' + components.hash;
2292+ }
2293+
2294+ if (components.search) {
2295+ url += '?' + components.search;
2296+ }
2297 return url;
2298 },
2299
2300 /**
2301- * Smartly combine new namespaced url components with old.
2302+ * Smartly combine new namespaced url components with old. Hash and
2303+ * query string parameters from the incoming URL are preserved.
2304 *
2305 * @method combine
2306 * @param {Object} orig url.
2307@@ -260,7 +312,7 @@
2308 // original value).
2309 delete incoming[this.defaultNamespace];
2310 }
2311- var output = {};
2312+ var output = new Routes();
2313 Y.each(orig, function(v, k) {
2314 if (v && !Y.Lang.isArray(v)) {
2315 v = [v];
2316@@ -290,9 +342,11 @@
2317 }
2318 });
2319 });
2320+
2321+ output.hash = incoming.hash;
2322+ output.search = incoming.search;
2323 url = this.url(output, {excludeRootPaths: true});
2324 return url;
2325-
2326 }
2327 };
2328
2329@@ -363,11 +417,7 @@
2330 delete options.overrideAllNamespaces;
2331 } else {
2332 var loc = Y.getLocation();
2333- var qs = this.nsRouter.getQS(url);
2334 result = this.nsRouter.combine(loc.pathname, url);
2335- if (qs) {
2336- result += '?' + qs;
2337- }
2338 }
2339 if (Y.App.prototype._navigate.call(this, result, options)) {
2340 return true;
2341
2342=== modified file 'app/assets/javascripts/reconnecting-websocket.js'
2343--- app/assets/javascripts/reconnecting-websocket.js 2012-07-24 18:18:43 +0000
2344+++ app/assets/javascripts/reconnecting-websocket.js 2013-06-26 14:26:26 +0000
2345@@ -38,7 +38,7 @@
2346 * onopen // sometime later...
2347 * onmessage
2348 * onmessage
2349- * etc...
2350+ * etc...
2351 *
2352 * It is API compatible with the standard WebSocket API.
2353 *
2354@@ -46,7 +46,7 @@
2355 */
2356
2357 YUI.add("reconnecting-websocket", function(Y) {
2358-
2359+
2360 function ReconnectingWebSocket(url, protocols) {
2361
2362 // These can be altered by calling code.
2363@@ -58,7 +58,7 @@
2364 var ws;
2365 var forcedClose = false;
2366 var timedOut = false;
2367-
2368+
2369 this.url = url;
2370 this.protocols = protocols;
2371 this.readyState = WebSocket.CONNECTING;
2372@@ -81,7 +81,7 @@
2373 if (self.debug || ReconnectingWebSocket.debugAll) {
2374 console.debug('ReconnectingWebSocket', 'attempt-connect', url);
2375 }
2376-
2377+
2378 var localWs = ws;
2379 var timeout = setTimeout(function() {
2380 if (self.debug || ReconnectingWebSocket.debugAll) {
2381@@ -91,7 +91,7 @@
2382 localWs.close();
2383 timedOut = false;
2384 }, self.timeoutInterval);
2385-
2386+
2387 ws.onopen = function(event) {
2388 clearTimeout(timeout);
2389 if (self.debug || ReconnectingWebSocket.debugAll) {
2390@@ -101,7 +101,7 @@
2391 reconnectAttempt = false;
2392 self.onopen(event);
2393 };
2394-
2395+
2396 ws.onclose = function(event) {
2397 clearTimeout(timeout);
2398 ws = null;
2399@@ -125,7 +125,8 @@
2400 if (self.debug || ReconnectingWebSocket.debugAll) {
2401 console.debug('ReconnectingWebSocket', 'onmessage', url, event.data);
2402 }
2403- self.onmessage(event);
2404+ Y.fire('websocketReceive', event.data);
2405+ self.onmessage(event);
2406 };
2407 ws.onerror = function(event) {
2408 if (self.debug || ReconnectingWebSocket.debugAll) {
2409@@ -141,6 +142,7 @@
2410 if (self.debug || ReconnectingWebSocket.debugAll) {
2411 console.debug('ReconnectingWebSocket', 'send', url, data);
2412 }
2413+ Y.fire('websocketSend', data);
2414 return ws.send(data);
2415 } else {
2416 throw 'INVALID_STATE_ERR : Pausing to reconnect websocket';
2417@@ -174,4 +176,4 @@
2418
2419 }, "0.1.0", {
2420 requires: ["base"]
2421-});
2422\ No newline at end of file
2423+});
2424
2425=== removed file 'app/assets/stylesheets/bootstrap-responsive-2.0.4.css'
2426--- app/assets/stylesheets/bootstrap-responsive-2.0.4.css 2012-07-18 21:14:50 +0000
2427+++ app/assets/stylesheets/bootstrap-responsive-2.0.4.css 1970-01-01 00:00:00 +0000
2428@@ -1,815 +0,0 @@
2429-/*!
2430- * Bootstrap Responsive v2.0.4
2431- *
2432- * Copyright 2012 Twitter, Inc
2433- * Licensed under the Apache License v2.0
2434- * http://www.apache.org/licenses/LICENSE-2.0
2435- *
2436- * Designed and built with all the love in the world @twitter by @mdo and @fat.
2437- */
2438-
2439-.clearfix {
2440- *zoom: 1;
2441-}
2442-
2443-.clearfix:before,
2444-.clearfix:after {
2445- display: table;
2446- content: "";
2447-}
2448-
2449-.clearfix:after {
2450- clear: both;
2451-}
2452-
2453-.hide-text {
2454- font: 0/0 a;
2455- color: transparent;
2456- text-shadow: none;
2457- background-color: transparent;
2458- border: 0;
2459-}
2460-
2461-.input-block-level {
2462- display: block;
2463- width: 100%;
2464- min-height: 28px;
2465- -webkit-box-sizing: border-box;
2466- -moz-box-sizing: border-box;
2467- -ms-box-sizing: border-box;
2468- box-sizing: border-box;
2469-}
2470-
2471-.hidden {
2472- display: none;
2473- visibility: hidden;
2474-}
2475-
2476-.visible-phone {
2477- display: none !important;
2478-}
2479-
2480-.visible-tablet {
2481- display: none !important;
2482-}
2483-
2484-.hidden-desktop {
2485- display: none !important;
2486-}
2487-
2488-@media (max-width: 767px) {
2489- .visible-phone {
2490- display: inherit !important;
2491- }
2492- .hidden-phone {
2493- display: none !important;
2494- }
2495- .hidden-desktop {
2496- display: inherit !important;
2497- }
2498- .visible-desktop {
2499- display: none !important;
2500- }
2501-}
2502-
2503-@media (min-width: 768px) and (max-width: 979px) {
2504- .visible-tablet {
2505- display: inherit !important;
2506- }
2507- .hidden-tablet {
2508- display: none !important;
2509- }
2510- .hidden-desktop {
2511- display: inherit !important;
2512- }
2513- .visible-desktop {
2514- display: none !important ;
2515- }
2516-}
2517-
2518-@media (max-width: 480px) {
2519- .nav-collapse {
2520- -webkit-transform: translate3d(0, 0, 0);
2521- }
2522- .page-header h1 small {
2523- display: block;
2524- line-height: 18px;
2525- }
2526- input[type="checkbox"],
2527- input[type="radio"] {
2528- border: 1px solid #ccc;
2529- }
2530- .form-horizontal .control-group > label {
2531- float: none;
2532- width: auto;
2533- padding-top: 0;
2534- text-align: left;
2535- }
2536- .form-horizontal .controls {
2537- margin-left: 0;
2538- }
2539- .form-horizontal .control-list {
2540- padding-top: 0;
2541- }
2542- .form-horizontal .form-actions {
2543- padding-right: 10px;
2544- padding-left: 10px;
2545- }
2546- .modal {
2547- position: absolute;
2548- top: 10px;
2549- right: 10px;
2550- left: 10px;
2551- width: auto;
2552- margin: 0;
2553- }
2554- .modal.fade.in {
2555- top: auto;
2556- }
2557- .modal-header .close {
2558- padding: 10px;
2559- margin: -10px;
2560- }
2561- .carousel-caption {
2562- position: static;
2563- }
2564-}
2565-
2566-@media (max-width: 767px) {
2567- body {
2568- padding-right: 20px;
2569- padding-left: 20px;
2570- }
2571- .navbar-fixed-top,
2572- .navbar-fixed-bottom {
2573- margin-right: -20px;
2574- margin-left: -20px;
2575- }
2576- .container-fluid {
2577- padding: 0;
2578- }
2579- .dl-horizontal dt {
2580- float: none;
2581- width: auto;
2582- clear: none;
2583- text-align: left;
2584- }
2585- .dl-horizontal dd {
2586- margin-left: 0;
2587- }
2588- .container {
2589- width: auto;
2590- }
2591- .row-fluid {
2592- width: 100%;
2593- }
2594- .row,
2595- .thumbnails {
2596- margin-left: 0;
2597- }
2598- [class*="span"],
2599- .row-fluid [class*="span"] {
2600- display: block;
2601- float: none;
2602- width: auto;
2603- margin-left: 0;
2604- }
2605- .input-large,
2606- .input-xlarge,
2607- .input-xxlarge,
2608- input[class*="span"],
2609- select[class*="span"],
2610- textarea[class*="span"],
2611- .uneditable-input {
2612- display: block;
2613- width: 100%;
2614- min-height: 28px;
2615- -webkit-box-sizing: border-box;
2616- -moz-box-sizing: border-box;
2617- -ms-box-sizing: border-box;
2618- box-sizing: border-box;
2619- }
2620- .input-prepend input,
2621- .input-append input,
2622- .input-prepend input[class*="span"],
2623- .input-append input[class*="span"] {
2624- display: inline-block;
2625- width: auto;
2626- }
2627-}
2628-
2629-@media (min-width: 768px) and (max-width: 979px) {
2630- .row {
2631- margin-left: -20px;
2632- *zoom: 1;
2633- }
2634- .row:before,
2635- .row:after {
2636- display: table;
2637- content: "";
2638- }
2639- .row:after {
2640- clear: both;
2641- }
2642- [class*="span"] {
2643- float: left;
2644- margin-left: 20px;
2645- }
2646- .container,
2647- .navbar-fixed-top .container,
2648- .navbar-fixed-bottom .container {
2649- width: 724px;
2650- }
2651- .span12 {
2652- width: 724px;
2653- }
2654- .span11 {
2655- width: 662px;
2656- }
2657- .span10 {
2658- width: 600px;
2659- }
2660- .span9 {
2661- width: 538px;
2662- }
2663- .span8 {
2664- width: 476px;
2665- }
2666- .span7 {
2667- width: 414px;
2668- }
2669- .span6 {
2670- width: 352px;
2671- }
2672- .span5 {
2673- width: 290px;
2674- }
2675- .span4 {
2676- width: 228px;
2677- }
2678- .span3 {
2679- width: 166px;
2680- }
2681- .span2 {
2682- width: 104px;
2683- }
2684- .span1 {
2685- width: 42px;
2686- }
2687- .offset12 {
2688- margin-left: 764px;
2689- }
2690- .offset11 {
2691- margin-left: 702px;
2692- }
2693- .offset10 {
2694- margin-left: 640px;
2695- }
2696- .offset9 {
2697- margin-left: 578px;
2698- }
2699- .offset8 {
2700- margin-left: 516px;
2701- }
2702- .offset7 {
2703- margin-left: 454px;
2704- }
2705- .offset6 {
2706- margin-left: 392px;
2707- }
2708- .offset5 {
2709- margin-left: 330px;
2710- }
2711- .offset4 {
2712- margin-left: 268px;
2713- }
2714- .offset3 {
2715- margin-left: 206px;
2716- }
2717- .offset2 {
2718- margin-left: 144px;
2719- }
2720- .offset1 {
2721- margin-left: 82px;
2722- }
2723- .row-fluid {
2724- width: 100%;
2725- *zoom: 1;
2726- }
2727- .row-fluid:before,
2728- .row-fluid:after {
2729- display: table;
2730- content: "";
2731- }
2732- .row-fluid:after {
2733- clear: both;
2734- }
2735- .row-fluid [class*="span"] {
2736- display: block;
2737- float: left;
2738- width: 100%;
2739- min-height: 28px;
2740- margin-left: 2.762430939%;
2741- *margin-left: 2.709239449638298%;
2742- -webkit-box-sizing: border-box;
2743- -moz-box-sizing: border-box;
2744- -ms-box-sizing: border-box;
2745- box-sizing: border-box;
2746- }
2747- .row-fluid [class*="span"]:first-child {
2748- margin-left: 0;
2749- }
2750- .row-fluid .span12 {
2751- width: 99.999999993%;
2752- *width: 99.9468085036383%;
2753- }
2754- .row-fluid .span11 {
2755- width: 91.436464082%;
2756- *width: 91.38327259263829%;
2757- }
2758- .row-fluid .span10 {
2759- width: 82.87292817100001%;
2760- *width: 82.8197366816383%;
2761- }
2762- .row-fluid .span9 {
2763- width: 74.30939226%;
2764- *width: 74.25620077063829%;
2765- }
2766- .row-fluid .span8 {
2767- width: 65.74585634900001%;
2768- *width: 65.6926648596383%;
2769- }
2770- .row-fluid .span7 {
2771- width: 57.182320438000005%;
2772- *width: 57.129128948638304%;
2773- }
2774- .row-fluid .span6 {
2775- width: 48.618784527%;
2776- *width: 48.5655930376383%;
2777- }
2778- .row-fluid .span5 {
2779- width: 40.055248616%;
2780- *width: 40.0020571266383%;
2781- }
2782- .row-fluid .span4 {
2783- width: 31.491712705%;
2784- *width: 31.4385212156383%;
2785- }
2786- .row-fluid .span3 {
2787- width: 22.928176794%;
2788- *width: 22.874985304638297%;
2789- }
2790- .row-fluid .span2 {
2791- width: 14.364640883%;
2792- *width: 14.311449393638298%;
2793- }
2794- .row-fluid .span1 {
2795- width: 5.801104972%;
2796- *width: 5.747913482638298%;
2797- }
2798- input,
2799- textarea,
2800- .uneditable-input {
2801- margin-left: 0;
2802- }
2803- input.span12,
2804- textarea.span12,
2805- .uneditable-input.span12 {
2806- width: 714px;
2807- }
2808- input.span11,
2809- textarea.span11,
2810- .uneditable-input.span11 {
2811- width: 652px;
2812- }
2813- input.span10,
2814- textarea.span10,
2815- .uneditable-input.span10 {
2816- width: 590px;
2817- }
2818- input.span9,
2819- textarea.span9,
2820- .uneditable-input.span9 {
2821- width: 528px;
2822- }
2823- input.span8,
2824- textarea.span8,
2825- .uneditable-input.span8 {
2826- width: 466px;
2827- }
2828- input.span7,
2829- textarea.span7,
2830- .uneditable-input.span7 {
2831- width: 404px;
2832- }
2833- input.span6,
2834- textarea.span6,
2835- .uneditable-input.span6 {
2836- width: 342px;
2837- }
2838- input.span5,
2839- textarea.span5,
2840- .uneditable-input.span5 {
2841- width: 280px;
2842- }
2843- input.span4,
2844- textarea.span4,
2845- .uneditable-input.span4 {
2846- width: 218px;
2847- }
2848- input.span3,
2849- textarea.span3,
2850- .uneditable-input.span3 {
2851- width: 156px;
2852- }
2853- input.span2,
2854- textarea.span2,
2855- .uneditable-input.span2 {
2856- width: 94px;
2857- }
2858- input.span1,
2859- textarea.span1,
2860- .uneditable-input.span1 {
2861- width: 32px;
2862- }
2863-}
2864-
2865-@media (min-width: 1200px) {
2866- .row {
2867- margin-left: -30px;
2868- *zoom: 1;
2869- }
2870- .row:before,
2871- .row:after {
2872- display: table;
2873- content: "";
2874- }
2875- .row:after {
2876- clear: both;
2877- }
2878- [class*="span"] {
2879- float: left;
2880- margin-left: 30px;
2881- }
2882- .container,
2883- .navbar-fixed-top .container,
2884- .navbar-fixed-bottom .container {
2885- width: 1170px;
2886- }
2887- .span12 {
2888- width: 1170px;
2889- }
2890- .span11 {
2891- width: 1070px;
2892- }
2893- .span10 {
2894- width: 970px;
2895- }
2896- .span9 {
2897- width: 870px;
2898- }
2899- .span8 {
2900- width: 770px;
2901- }
2902- .span7 {
2903- width: 670px;
2904- }
2905- .span6 {
2906- width: 570px;
2907- }
2908- .span5 {
2909- width: 470px;
2910- }
2911- .span4 {
2912- width: 370px;
2913- }
2914- .span3 {
2915- width: 270px;
2916- }
2917- .span2 {
2918- width: 170px;
2919- }
2920- .span1 {
2921- width: 70px;
2922- }
2923- .offset12 {
2924- margin-left: 1230px;
2925- }
2926- .offset11 {
2927- margin-left: 1130px;
2928- }
2929- .offset10 {
2930- margin-left: 1030px;
2931- }
2932- .offset9 {
2933- margin-left: 930px;
2934- }
2935- .offset8 {
2936- margin-left: 830px;
2937- }
2938- .offset7 {
2939- margin-left: 730px;
2940- }
2941- .offset6 {
2942- margin-left: 630px;
2943- }
2944- .offset5 {
2945- margin-left: 530px;
2946- }
2947- .offset4 {
2948- margin-left: 430px;
2949- }
2950- .offset3 {
2951- margin-left: 330px;
2952- }
2953- .offset2 {
2954- margin-left: 230px;
2955- }
2956- .offset1 {
2957- margin-left: 130px;
2958- }
2959- .row-fluid {
2960- width: 100%;
2961- *zoom: 1;
2962- }
2963- .row-fluid:before,
2964- .row-fluid:after {
2965- display: table;
2966- content: "";
2967- }
2968- .row-fluid:after {
2969- clear: both;
2970- }
2971- .row-fluid [class*="span"] {
2972- display: block;
2973- float: left;
2974- width: 100%;
2975- min-height: 28px;
2976- margin-left: 2.564102564%;
2977- *margin-left: 2.510911074638298%;
2978- -webkit-box-sizing: border-box;
2979- -moz-box-sizing: border-box;
2980- -ms-box-sizing: border-box;
2981- box-sizing: border-box;
2982- }
2983- .row-fluid [class*="span"]:first-child {
2984- margin-left: 0;
2985- }
2986- .row-fluid .span12 {
2987- width: 100%;
2988- *width: 99.94680851063829%;
2989- }
2990- .row-fluid .span11 {
2991- width: 91.45299145300001%;
2992- *width: 91.3997999636383%;
2993- }
2994- .row-fluid .span10 {
2995- width: 82.905982906%;
2996- *width: 82.8527914166383%;
2997- }
2998- .row-fluid .span9 {
2999- width: 74.358974359%;
3000- *width: 74.30578286963829%;
3001- }
3002- .row-fluid .span8 {
3003- width: 65.81196581200001%;
3004- *width: 65.7587743226383%;
3005- }
3006- .row-fluid .span7 {
3007- width: 57.264957265%;
3008- *width: 57.2117657756383%;
3009- }
3010- .row-fluid .span6 {
3011- width: 48.717948718%;
3012- *width: 48.6647572286383%;
3013- }
3014- .row-fluid .span5 {
3015- width: 40.170940171000005%;
3016- *width: 40.117748681638304%;
3017- }
3018- .row-fluid .span4 {
3019- width: 31.623931624%;
3020- *width: 31.5707401346383%;
3021- }
3022- .row-fluid .span3 {
3023- width: 23.076923077%;
3024- *width: 23.0237315876383%;
3025- }
3026- .row-fluid .span2 {
3027- width: 14.529914530000001%;
3028- *width: 14.4767230406383%;
3029- }
3030- .row-fluid .span1 {
3031- width: 5.982905983%;
3032- *width: 5.929714493638298%;
3033- }
3034- input,
3035- textarea,
3036- .uneditable-input {
3037- margin-left: 0;
3038- }
3039- input.span12,
3040- textarea.span12,
3041- .uneditable-input.span12 {
3042- width: 1160px;
3043- }
3044- input.span11,
3045- textarea.span11,
3046- .uneditable-input.span11 {
3047- width: 1060px;
3048- }
3049- input.span10,
3050- textarea.span10,
3051- .uneditable-input.span10 {
3052- width: 960px;
3053- }
3054- input.span9,
3055- textarea.span9,
3056- .uneditable-input.span9 {
3057- width: 860px;
3058- }
3059- input.span8,
3060- textarea.span8,
3061- .uneditable-input.span8 {
3062- width: 760px;
3063- }
3064- input.span7,
3065- textarea.span7,
3066- .uneditable-input.span7 {
3067- width: 660px;
3068- }
3069- input.span6,
3070- textarea.span6,
3071- .uneditable-input.span6 {
3072- width: 560px;
3073- }
3074- input.span5,
3075- textarea.span5,
3076- .uneditable-input.span5 {
3077- width: 460px;
3078- }
3079- input.span4,
3080- textarea.span4,
3081- .uneditable-input.span4 {
3082- width: 360px;
3083- }
3084- input.span3,
3085- textarea.span3,
3086- .uneditable-input.span3 {
3087- width: 260px;
3088- }
3089- input.span2,
3090- textarea.span2,
3091- .uneditable-input.span2 {
3092- width: 160px;
3093- }
3094- input.span1,
3095- textarea.span1,
3096- .uneditable-input.span1 {
3097- width: 60px;
3098- }
3099- .thumbnails {
3100- margin-left: -30px;
3101- }
3102- .thumbnails > li {
3103- margin-left: 30px;
3104- }
3105- .row-fluid .thumbnails {
3106- margin-left: 0;
3107- }
3108-}
3109-
3110-@media (max-width: 979px) {
3111- body {
3112- padding-top: 0;
3113- }
3114- .navbar-fixed-top,
3115- .navbar-fixed-bottom {
3116- position: static;
3117- }
3118- .navbar-fixed-top {
3119- margin-bottom: 18px;
3120- }
3121- .navbar-fixed-bottom {
3122- margin-top: 18px;
3123- }
3124- .navbar-fixed-top .navbar-inner,
3125- .navbar-fixed-bottom .navbar-inner {
3126- padding: 5px;
3127- }
3128- .navbar .container {
3129- width: auto;
3130- padding: 0;
3131- }
3132- .navbar .brand {
3133- padding-right: 10px;
3134- padding-left: 10px;
3135- margin: 0 0 0 -5px;
3136- }
3137- .nav-collapse {
3138- clear: both;
3139- }
3140- .nav-collapse .nav {
3141- float: none;
3142- margin: 0 0 9px;
3143- }
3144- .nav-collapse .nav > li {
3145- float: none;
3146- }
3147- .nav-collapse .nav > li > a {
3148- margin-bottom: 2px;
3149- }
3150- .nav-collapse .nav > .divider-vertical {
3151- display: none;
3152- }
3153- .nav-collapse .nav .nav-header {
3154- color: #999999;
3155- text-shadow: none;
3156- }
3157- .nav-collapse .nav > li > a,
3158- .nav-collapse .dropdown-menu a {
3159- padding: 6px 15px;
3160- font-weight: bold;
3161- color: #999999;
3162- -webkit-border-radius: 3px;
3163- -moz-border-radius: 3px;
3164- border-radius: 3px;
3165- }
3166- .nav-collapse .btn {
3167- padding: 4px 10px 4px;
3168- font-weight: normal;
3169- -webkit-border-radius: 4px;
3170- -moz-border-radius: 4px;
3171- border-radius: 4px;
3172- }
3173- .nav-collapse .dropdown-menu li + li a {
3174- margin-bottom: 2px;
3175- }
3176- .nav-collapse .nav > li > a:hover,
3177- .nav-collapse .dropdown-menu a:hover {
3178- background-color: #222222;
3179- }
3180- .nav-collapse.in .btn-group {
3181- padding: 0;
3182- margin-top: 5px;
3183- }
3184- .nav-collapse .dropdown-menu {
3185- position: static;
3186- top: auto;
3187- left: auto;
3188- display: block;
3189- float: none;
3190- max-width: none;
3191- padding: 0;
3192- margin: 0 15px;
3193- background-color: transparent;
3194- border: none;
3195- -webkit-border-radius: 0;
3196- -moz-border-radius: 0;
3197- border-radius: 0;
3198- -webkit-box-shadow: none;
3199- -moz-box-shadow: none;
3200- box-shadow: none;
3201- }
3202- .nav-collapse .dropdown-menu:before,
3203- .nav-collapse .dropdown-menu:after {
3204- display: none;
3205- }
3206- .nav-collapse .dropdown-menu .divider {
3207- display: none;
3208- }
3209- .nav-collapse .navbar-form,
3210- .nav-collapse .navbar-search {
3211- float: none;
3212- padding: 9px 15px;
3213- margin: 9px 0;
3214- border-top: 1px solid #222222;
3215- border-bottom: 1px solid #222222;
3216- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
3217- -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
3218- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
3219- }
3220- .navbar .nav-collapse .nav.pull-right {
3221- float: none;
3222- margin-left: 0;
3223- }
3224- .nav-collapse,
3225- .nav-collapse.collapse {
3226- height: 0;
3227- overflow: hidden;
3228- }
3229- .navbar .btn-navbar {
3230- display: block;
3231- }
3232- .navbar-static .navbar-inner {
3233- padding-right: 10px;
3234- padding-left: 10px;
3235- }
3236-}
3237-
3238-@media (min-width: 980px) {
3239- .nav-collapse.collapse {
3240- height: auto !important;
3241- overflow: visible !important;
3242- }
3243-}
3244
3245=== added file 'app/assets/stylesheets/juju-bootstrap.css'
3246--- app/assets/stylesheets/juju-bootstrap.css 1970-01-01 00:00:00 +0000
3247+++ app/assets/stylesheets/juju-bootstrap.css 2013-06-26 14:26:26 +0000
3248@@ -0,0 +1,156 @@
3249+/*
3250+To help gradually move away from bootstrap when so much of the application
3251+relies on its styles, when you find a bootstrap specific class style copy the
3252+__not overridden__ styles into this file.
3253+*/
3254+.btn {
3255+ display: inline-block;
3256+ padding: 4px 10px 4px;
3257+ margin-bottom: 0;
3258+ font-size: 13px;
3259+ line-height: 18px;
3260+ color: #333333;
3261+ text-align: center;
3262+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
3263+ vertical-align: middle;
3264+ cursor: pointer;
3265+ background-color: #f5f5f5;
3266+ background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6);
3267+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
3268+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
3269+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
3270+ background-image: linear-gradient(top, #ffffff, #e6e6e6);
3271+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
3272+ background-repeat: repeat-x;
3273+ border: 1px solid #cccccc;
3274+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3275+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
3276+ border-bottom-color: #b3b3b3;
3277+ -webkit-border-radius: 4px;
3278+ -moz-border-radius: 4px;
3279+ border-radius: 4px;
3280+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
3281+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
3282+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3283+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3284+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
3285+}
3286+
3287+.btn:hover {
3288+ color: #333333;
3289+ text-decoration: none;
3290+ background-color: #e6e6e6;
3291+ background-position: 0 -15px;
3292+ -webkit-transition: background-position 0.1s linear;
3293+ -moz-transition: background-position 0.1s linear;
3294+ -ms-transition: background-position 0.1s linear;
3295+ -o-transition: background-position 0.1s linear;
3296+ transition: background-position 0.1s linear;
3297+}
3298+
3299+.btn-primary, .btn-primary:hover, .btn-warning, .btn-warning:hover, .btn-danger, .btn-danger:hover, .btn-success, .btn-success:hover, .btn-info, .btn-info:hover, .btn-inverse, .btn-inverse:hover {
3300+ color: #ffffff;
3301+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
3302+}
3303+
3304+.btn-danger {
3305+ background-color: #da4f49;
3306+ background-image: -ms-linear-gradient(top, #ee5f5b, #bd362f);
3307+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
3308+ background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
3309+ background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
3310+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
3311+ background-image: linear-gradient(top, #ee5f5b, #bd362f);
3312+ background-repeat: repeat-x;
3313+ border-color: #bd362f #bd362f #802420;
3314+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
3315+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);
3316+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
3317+}
3318+
3319+.btn-danger:hover, .btn-danger:active, .btn-danger.active, .btn-danger.disabled, .btn-danger[disabled] {
3320+ background-color: #bd362f;
3321+}
3322+
3323+.navbar .btn {
3324+ display: inline-block;
3325+ padding: 4px 10px 4px;
3326+ line-height: 18px;
3327+}
3328+
3329+.navbar .dropdown-menu:after {
3330+ position: absolute;
3331+ top: -6px;
3332+ left: 10px;
3333+ display: inline-block;
3334+ border-right: 6px solid transparent;
3335+ border-bottom: 6px solid #ffffff;
3336+ border-left: 6px solid transparent;
3337+ content: '';
3338+}
3339+
3340+.navbar .dropdown-menu:before {
3341+ position: absolute;
3342+ top: -7px;
3343+ left: 9px;
3344+ display: inline-block;
3345+ border-right: 7px solid transparent;
3346+ border-bottom: 7px solid #ccc;
3347+ border-left: 7px solid transparent;
3348+ border-bottom-color: rgba(0, 0, 0, 0.2);
3349+ content: '';
3350+}
3351+
3352+.dropdown-menu {
3353+ position: absolute;
3354+ top: 100%;
3355+ left: 0;
3356+ z-index: 1000;
3357+ display: none;
3358+ float: left;
3359+ min-width: 160px;
3360+ padding: 4px 0;
3361+ margin: 1px 0 0;
3362+ list-style: none;
3363+ background-color: #ffffff;
3364+ border: 1px solid #ccc;
3365+ border: 1px solid rgba(0, 0, 0, 0.2);
3366+ -webkit-border-radius: 5px;
3367+ -moz-border-radius: 5px;
3368+ border-radius: 5px;
3369+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
3370+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
3371+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
3372+ -webkit-background-clip: padding-box;
3373+ -moz-background-clip: padding;
3374+ background-clip: padding-box;
3375+}
3376+
3377+.open > .dropdown-menu {
3378+ display: block;
3379+}
3380+
3381+.dropdown-menu a {
3382+ display: block;
3383+ padding: 3px 15px;
3384+ clear: both;
3385+ font-weight: normal;
3386+ line-height: 18px;
3387+ color: #333333;
3388+ white-space: nowrap;
3389+}
3390+
3391+.icon-arrow-right {
3392+ background-position: -264px -96px;
3393+}
3394+
3395+[class^="icon-"], [class*=" icon-"] {
3396+ display: inline-block;
3397+ width: 14px;
3398+ height: 14px;
3399+ line-height: 14px;
3400+ vertical-align: text-top;
3401+ background-image: url("../images/glyphicons-halflings.png");
3402+ background-position: 14px 14px;
3403+ background-repeat: no-repeat;
3404+}
3405
3406=== modified file 'app/config-debug.js'
3407--- app/config-debug.js 2013-05-29 16:50:27 +0000
3408+++ app/config-debug.js 2013-06-26 14:26:26 +0000
3409@@ -48,5 +48,9 @@
3410 // There is also a hotkey to toggle the simulator.
3411 simulateEvents: true,
3412 readOnly: false,
3413+ // Enable Google Analytics usage and calls. Also implies using cookies.
3414+ // For the debug configuration, analytics should generally be false to
3415+ // prevent muddying the gathered statistics.
3416+ useAnalytics: false,
3417 login_help: 'For this demonstration, use the password "admin" to connect.'
3418 };
3419
3420=== modified file 'app/config-prod.js'
3421--- app/config-prod.js 2013-05-28 12:52:54 +0000
3422+++ app/config-prod.js 2013-06-26 14:26:26 +0000
3423@@ -47,6 +47,10 @@
3424 // You can also use the :flags:/simulateEvents feature flag.
3425 simulateEvents: false,
3426 readOnly: false,
3427+ // Enable Google Analytics usage and calls. Also implies using cookies.
3428+ // XXX: BradCrittenden 2013-06-10 bug=1189502: set to 'true' by default
3429+ // only after this bug is resolved, exposing the setting in the charm.
3430+ useAnalytics: false,
3431 login_help: (
3432 'The password is the admin-secret from the Juju environment. This can ' +
3433 'often be found by looking in ~/.juju/environments.yaml.')
3434
3435=== modified file 'app/index.html'
3436--- app/index.html 2013-05-28 18:53:33 +0000
3437+++ app/index.html 2013-06-26 14:26:26 +0000
3438@@ -42,15 +42,32 @@
3439 <link rel="stylesheet" href="/juju-ui/assets/combined-css/all-static.css">
3440 <link rel="stylesheet" href="/juju-ui/assets/juju-gui.css">
3441 <link rel="stylesheet" href="/juju-ui/assets/sprite.css">
3442+
3443 <!--[if lt IE 9]>
3444 <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
3445 <![endif]-->
3446+
3447+ <!-- Set up Google analytics async tracking.
3448+ If the domain is set to 'jujucharms.com' as you'd expect, the tracker
3449+ does not work for deployments outside of our demo site.
3450+ -->
3451+ <script type="text/javascript">
3452+ var _gaq = _gaq || [];
3453+ window._gaq = _gaq;
3454+ window.ga_id = 'UA-41463568-2';
3455+ _gaq.push(['_setAccount', window.ga_id]);
3456+ _gaq.push (['_gat._anonymizeIp']);
3457+ _gaq.push(['_setDomainName', 'none']);
3458+ _gaq.push(['_setAllowLinker', true]);
3459+ _gaq.push(['_trackPageview']);
3460+ </script>
3461+
3462 </head>
3463
3464 <body>
3465 <!-- This <img> tag is here just to force early loading of the background
3466- image so it displays more quickly. This makes a large improvment to the
3467- way the app looks while loading on a slow connection. -->
3468+ image so it displays more quickly. This makes a large improvement to
3469+ the way the app looks while loading on a slow connection. -->
3470 <img src="/juju-ui/assets/images/pattern_tile.png" style="display: none;">
3471
3472 <div id="full-screen-mask" class="crosshatch-background">
3473@@ -89,58 +106,49 @@
3474 </div>
3475 </div>
3476 <div id="notifier-box"></div>
3477+ <div class="cookie-policy" style="display:none;">
3478+ <div class="wrapper">
3479+ <a href="" class="link-cta">Close</a>
3480+ <p>
3481+ We use cookies to improve your experience. By your continued use of
3482+ this application you accept such use. To change your settings please
3483+ <a href="http://www.ubuntu.com/privacy-policy#cookies">see our policy</a>
3484+ </p>
3485+ </div>
3486+ </div>
3487 <div id="viewport-wrapper">
3488 <div id="vp-left-border"></div>
3489 <div id="viewport">
3490- <div class="navbar">
3491+ <div class="navbar">
3492+ <div class="navbar-inner">
3493 <div id="nav-brand-env">
3494- <span>
3495- <a class="brand" href="/">
3496- <i class="sprite juju_logo" title="Juju GUI"></i>
3497- </a>
3498- </span>
3499- <span class="nav-container">
3500- <span class="nav-section">
3501- <i class="sprite environment_icon"> </i>
3502- <span id="environment-name" draggable="true"></span>
3503- <span id="provider-type" class="provider-type"></span>
3504- </span>
3505- </span>
3506- </div>
3507- <div id="nav-alerts-charms">
3508- <span class="nav-container">
3509- <span class="nav-section">
3510- <i class="sprite alert_icon"></i>
3511- Alerts
3512- <span id="notifications"></span>
3513- </span>
3514- </span>
3515- <span class="nav-container">
3516- <span class="nav-section active-border">
3517- <div id="logout-trigger">Logout</div>
3518- </span>
3519- </span>
3520- <span class="nav-container" id="charm-search-trigger-container">
3521- <span class="nav-section active-border">
3522- <span id="charm-search-trigger">
3523- <i id="charm-search-icon" class="sprite charm_icon"></i>
3524- Charms
3525- <i id="charm-search-chevron" class="sprite chevron_down"></i>
3526- </span>
3527- <input type="text" id="charm-search-field"
3528- required="required" placeholder="Search for a charm" />
3529- </span>
3530- </span>
3531- </div>
3532- </div>
3533- <div id="content">
3534- <div id="shortcut-help" style="display:none"></div>
3535- <div id="subapp-browser-min" style="display: none;"></div>
3536- <div id="subapp-browser"></div>
3537- <div id="main">
3538- </div> <!-- /container -->
3539- </div>
3540-
3541+ <a class="brand" href="/">
3542+ <i class="sprite juju_logo_dark" title="Juju GUI"></i>
3543+ </a>
3544+ <div id="environment-switcher">
3545+ <i class="sprite environment_icon"> </i>
3546+ <span id="environment-name" draggable="true"></span>
3547+ <span id="provider-type" class="provider-type"></span>
3548+ </div>
3549+ </div>
3550+ <ul class="nav">
3551+ <li>
3552+ <span id="notifications"></span>
3553+ Alerts
3554+ </li>
3555+ <li>
3556+ <a href="" id="logout-trigger">Logout</a>
3557+ </li>
3558+ </ul>
3559+ </div>
3560+ </div>
3561+ <div id="content">
3562+ <div id="shortcut-help" style="display:none"></div>
3563+ <div id="subapp-browser-min" style="display: none;"></div>
3564+ <div id="subapp-browser"></div>
3565+ <div id="main">
3566+ </div> <!-- /container -->
3567+ </div>
3568 </div>
3569 <div id="vp-right-border"></div>
3570 </div>
3571@@ -349,8 +357,26 @@
3572 // We need to activate the hotkeys when running the application
3573 // in production. Unit tests should call it manually.
3574 app.activateHotkeys();
3575+
3576+ if (!juju_config.useAnalytics) {
3577+ window['ga-disable-' + window.ga_id] = true;
3578+ }
3579 });
3580+
3581 };
3582 </script>
3583+
3584+ <script type="text/javascript">
3585+ (function() {
3586+ var ga = document.createElement('script');
3587+ ga.type = 'text/javascript'; ga.async = true;
3588+ ga.src = ('https:' == document.location.protocol ?
3589+ 'https://ssl' : 'http://www') +
3590+ '.google-analytics.com/ga.js';
3591+ var s = document.getElementsByTagName('script')[0];
3592+ s.parentNode.insertBefore(ga, s);
3593+ })();
3594+ </script>
3595+
3596 </body>
3597 </html>
3598
3599=== modified file 'app/models/browser.js'
3600--- app/models/browser.js 2013-05-17 14:51:05 +0000
3601+++ app/models/browser.js 2013-06-26 14:26:26 +0000
3602@@ -30,7 +30,7 @@
3603 var models = Y.namespace('juju.models'),
3604 ns = Y.namespace('juju.models.browser');
3605
3606- /**
3607+ /*
3608 * The filters are hard coded for now but will need to be updated. The
3609 * *right* place for them to live isn't obvious at the moment so they may
3610 * move. There are notes for an API call to provide a list, but we don't
3611@@ -86,6 +86,31 @@
3612 },
3613
3614 /**
3615+ Clears all filter data
3616+
3617+ @method _clear
3618+ @private
3619+ */
3620+ _clear: function() {
3621+ this.setAttrs({
3622+ categories: [],
3623+ provider: [],
3624+ series: [],
3625+ text: '',
3626+ type: []
3627+ });
3628+ },
3629+
3630+ /**
3631+ Resets the filter
3632+
3633+ @method reset
3634+ */
3635+ reset: function() {
3636+ this._clear();
3637+ this._setDefaults();
3638+ },
3639+ /**
3640 Given the current filters, generate a query string to use for api
3641 calls.
3642
3643
3644=== modified file 'app/models/charm.js'
3645--- app/models/charm.js 2013-05-29 15:19:30 +0000
3646+++ app/models/charm.js 2013-06-26 14:26:26 +0000
3647@@ -323,18 +323,22 @@
3648
3649
3650 /**
3651- * Model to represent the Charms from the Charmworld1 Api.
3652+ * Model to represent the Charms from the Charmworld2 Api.
3653 *
3654 * @class BrowserCharm
3655 * @extends {Charm}
3656 *
3657 */
3658 models.BrowserCharm = Y.Base.create('browser-charm', Charm, [], {
3659+ // Only care about at most, this number of related charms per interface.
3660+ maxRelatedCharms: 5,
3661+
3662 /**
3663- * Load the recent commits into a format we can use nicely.
3664- *
3665- * @method _loadRecentCommits
3666- *
3667+
3668+ Load the recent commits into a format we can use nicely.
3669+
3670+ @method _loadRecentCommits
3671+
3672 */
3673 _loadRecentCommits: function() {
3674 var source = this.get('code_source'),
3675@@ -375,6 +379,28 @@
3676 },
3677
3678 /**
3679+ Given the set of data for relatedCharms, make it compatible with the
3680+ model api to be used in the charm-token widget, for example.
3681+
3682+ @method _convertRelatedData
3683+ @param {Object} data a related charm object.
3684+
3685+ */
3686+ _convertRelatedData: function(data) {
3687+ return {
3688+ // Only show the icon if it has one and the charm has been reviewed to
3689+ // have a safe icon.
3690+ shouldShowIcon: data.has_icon && data.is_approved,
3691+ id: data.id,
3692+ mainCategory: data.categories[0],
3693+ name: data.name,
3694+ recent_commit_count: data.commits_in_past_30_days,
3695+ recent_download_count: data.downloads_in_past_30_days,
3696+ weight: data.weight
3697+ };
3698+ },
3699+
3700+ /**
3701 * Initializer
3702 *
3703 * @method initializer
3704@@ -384,6 +410,57 @@
3705 if (cfg && cfg.downloads_in_past_30_days) {
3706 this.set('recent_download_count', cfg.downloads_in_past_30_days);
3707 }
3708+ },
3709+
3710+ /**
3711+ Build the relatedCharms attribute from api data
3712+
3713+ @method buildRelatedCharms
3714+ @param {Object} provides the list of provides interfaces/charms.
3715+ @param {Object} requires the list of requires interfaces/charms.
3716+
3717+ */
3718+ buildRelatedCharms: function(provides, requires) {
3719+ var charms = {
3720+ all: {},
3721+ provides: {},
3722+ requires: {}
3723+ };
3724+
3725+ var buildWeightedList = function(relationName, relationData, scope) {
3726+ Y.Object.each(relationData, function(face, key) {
3727+ // The relations are in the order of score, so we can limit them right
3728+ // off the bat.
3729+ charms[relationName][key] = face.slice(0, this.maxRelatedCharms);
3730+ charms[relationName][key].forEach(function(relation, idx) {
3731+ // Update the related object with the converted version so that it's
3732+ // follows the model ATTRS
3733+ charms[relationName][key][idx] = this._convertRelatedData(relation);
3734+ // Then track the highest provides charm to be in the running for
3735+ // overall most weighted related charm.
3736+ charms.all[relation.id] = charms[relationName][key][idx];
3737+ }, scope);
3738+ }, scope);
3739+ };
3740+
3741+ buildWeightedList('provides', provides, this);
3742+ buildWeightedList('requires', requires, this);
3743+
3744+ // Find the highest weight charms, but make sure there are no
3745+ // duplicates. We build the object to index on key and remove dupes,
3746+ // then we get a list of results and sort them by weight, grabbing the
3747+ // top set.
3748+ var allCharmsList = Y.Object.values(charms.all);
3749+
3750+ allCharmsList.sort(function(charm1, charm2) {
3751+ return charm2.weight - charm1.weight;
3752+ });
3753+
3754+ this.set('relatedCharms', {
3755+ overall: allCharmsList.slice(0, this.maxRelatedCharms),
3756+ provides: charms.provides,
3757+ requires: charms.requires
3758+ });
3759 }
3760 }, {
3761 ATTRS: {
3762@@ -461,19 +538,22 @@
3763 return tmp.join('/');
3764 }
3765 },
3766- /**
3767- Does this charm have an icon file. Helper used for template rendering
3768- decisions.
3769-
3770- */
3771- hasIcon: {
3772+ shouldShowIcon: {
3773 /**
3774- @method hasIcon.valueFn
3775- @return {Boolean} Does the Charm have an icon file.
3776+ Should this charm display its icon. Helper used for template
3777+ rendering decisions.
3778+
3779+ @method shouldShowIcon.valueFn
3780+ @return {Boolean} Does the charm have an icon that should be shown?
3781
3782 */
3783 valueFn: function() {
3784- return this.get('files').indexOf('icon.svg') !== -1 ? true : false;
3785+ if (this.get('files').indexOf('icon.svg') !== -1 &&
3786+ this.get('is_approved')) {
3787+ return true;
3788+ } else {
3789+ return false;
3790+ }
3791 }
3792 },
3793 is_approved: {},
3794@@ -493,8 +573,33 @@
3795 return val;
3796 }
3797 },
3798+ /**
3799+ The mainCategory is a helper since we can only show one icon per
3800+ charm, but we permit multiple categories. An initial pass just grabs
3801+ the first category to use as an icon if required.
3802+
3803+ @attribute mainCategory
3804+ @default null
3805+ @type {String}
3806+
3807+ */
3808+ mainCategory: {
3809+ /**
3810+ @method mainCategory.valueFn
3811+ @return {String|Null} If a category is found its value else null.
3812+
3813+ */
3814+ valueFn: function() {
3815+ var categories = this.get('categories');
3816+ if (categories.length > 0) {
3817+ return categories[0];
3818+ } else {
3819+ return null;
3820+ }
3821+ }
3822+ },
3823 maintainer: {},
3824- /**
3825+ /*
3826 API related metdata information for this charm object.
3827
3828 This includes information such as related charms calculated by the
3829@@ -521,7 +626,7 @@
3830 /**
3831 * This attr is a mapper to the relations ATTR in the new API. It's
3832 * provided for backwards compatibility with the original Charm model.
3833- * This can be removed when Charmworld1 is the one true model used in
3834+ * This can be removed when Charmworld2 is the one true model used in
3835 * all Juju Gui code.
3836 *
3837 * @attribute provides
3838@@ -605,13 +710,28 @@
3839 return 0;
3840 }
3841 },
3842+ /**
3843+ The related charms object is three parts for use in our situations.
3844+ The keys are
3845+ - overall: the top scored related charms regardless of interface or
3846+ provide/requires
3847+ - provides: a nested object of the related charms for each provide
3848+ interface
3849+ - requires: a nested object of the related charms for each require
3850+ interface
3851+ @attribute relatedCharms
3852+ @default undefined
3853+ @type {Object}
3854+
3855+ */
3856+ relatedCharms: {},
3857 relations: {},
3858
3859 /**
3860 * This attr is a mapper to the relations ATTR in the new API. It's
3861 * provided for backwards compatibility with the original Charm model.
3862 *
3863- * This can be removed when Charmworld1 is the one true model used in
3864+ * This can be removed when Charmworld2 is the one true model used in
3865 * all Juju Gui code.
3866 *
3867 * @attribute requires
3868
3869=== modified file 'app/models/handlers.js'
3870--- app/models/handlers.js 2013-05-17 14:51:05 +0000
3871+++ app/models/handlers.js 2013-06-26 14:26:26 +0000
3872@@ -97,7 +97,7 @@
3873 };
3874 models.utils = utils; // Exported for testing purposes.
3875
3876- /**
3877+ /*
3878 Each handler is called passing the db instance, the action to be
3879 performed ("add", "change" or "remove"), the change coming from
3880 the environment, and a (optional) kind identifying what will be
3881@@ -133,7 +133,7 @@
3882 } else {
3883 data = change;
3884 }
3885- modelList.process_delta(action, data);
3886+ modelList.process_delta(action, data, db);
3887 },
3888
3889 /**
3890@@ -164,7 +164,7 @@
3891 public_address: change.PublicAddress
3892 };
3893 db.units.process_delta(action, unitData);
3894- db.machines.process_delta(action, machineData);
3895+ db.machines.process_delta(action, machineData, db);
3896 },
3897
3898 /**
3899
3900=== modified file 'app/models/models.js'
3901--- app/models/models.js 2013-05-17 14:51:05 +0000
3902+++ app/models/models.js 2013-06-26 14:26:26 +0000
3903@@ -70,6 +70,8 @@
3904 Y.each(data, function(value, key) {
3905 instance[key] = value;
3906 });
3907+ // Lazy model lists don't fire change events
3908+ list.fire('change');
3909 }
3910 }
3911 }
3912@@ -80,6 +82,7 @@
3913 } else {
3914 console.warn('Unknown change kind in _process_delta:', action);
3915 }
3916+ return instance;
3917 };
3918
3919 /**
3920@@ -148,6 +151,9 @@
3921 /**
3922 Dynamically calculate a display name that accounts for Juju Core name
3923 prefixes.
3924+
3925+ @attribute displayName
3926+ @type {String}
3927 */
3928 getter: function() {
3929 return this.get('id').replace('service-', '');
3930@@ -253,8 +259,21 @@
3931 return name.replace('unit-', '').replace(/^(.+)-(\d+)$/, '$1/$2');
3932 },
3933
3934- process_delta: function(action, data) {
3935- _process_delta(this, action, data, {relation_errors: {}});
3936+ process_delta: function(action, data, db) {
3937+ var instance = _process_delta(this, action, data, {relation_errors: {}});
3938+ if (!instance || !db) {return;}
3939+ // Apply this action for this instance to all service models as well.
3940+ // In the future we can transition from using db.units to always
3941+ // looking at db.services[serviceId].units
3942+ var service = db.services.getById(instance.service);
3943+ if (!service) { return; }
3944+ // Get the unit list for this service. (lazy)
3945+ var unitList = service.get('units');
3946+ if (!unitList) {
3947+ unitList = new models.ServiceUnitList();
3948+ service.set('units', unitList);
3949+ }
3950+ _process_delta(unitList, action, data, {relation_errors: {}});
3951 },
3952
3953 _setDefaultsAndCalculatedValues: function(obj) {
3954@@ -321,8 +340,15 @@
3955 var aggregate = this.get_informative_states_for_service(service);
3956 var sum = Y.Array.reduce(
3957 Y.Object.values(aggregate), 0, function(a, b) {return a + b;});
3958+ var previous_unit_count = service.get('unit_count');
3959 service.set('unit_count', sum);
3960 service.set('aggregated_status', aggregate);
3961+
3962+ // Set Google Analytics tracking event.
3963+ if (previous_unit_count !== sum && window._gaq) {
3964+ window._gaq.push(['_trackEvent', 'Service Stats', 'Update',
3965+ service.get('id'), sum]);
3966+ }
3967 },
3968 ATTRS: {}
3969 });
3970
3971=== modified file 'app/modules-debug.js'
3972--- app/modules-debug.js 2013-05-24 14:10:58 +0000
3973+++ app/modules-debug.js 2013-06-26 14:26:26 +0000
3974@@ -42,6 +42,11 @@
3975 // Don't load YUI CSS from YUI servers.
3976 fetchCSS: false,
3977
3978+ // Do not attempt to dispatch a new route when an anchor tag appears in the
3979+ // url. This is intended to keep charm details from reloading on tab
3980+ // selection in the browser.
3981+ navigateOnHash: false,
3982+
3983 groups: {
3984 gallery: {
3985 modules: {
3986@@ -101,12 +106,18 @@
3987 }
3988 }
3989 },
3990-
3991 filesaver: {
3992 modules: {
3993 'FileSaver': {
3994 fullpath: '/juju-ui/assets/javascripts/FileSaver.js'
3995 }
3996+ },
3997+ observe: {
3998+ modules: {
3999+ 'observe': {
4000+ fullpath: '/juju-ui/assets/javascripts/Object.observe.poly.js'
4001+ }
4002+ }
4003 }
4004 },
4005
4006@@ -134,6 +145,10 @@
4007 fullpath: '/juju-ui/widgets/overlay-indicator.js'
4008 },
4009
4010+ 'browser-sharing-widget': {
4011+ fullpath: '/juju-ui/widgets/sharing-widget.js'
4012+ },
4013+
4014 'browser-search-widget': {
4015 fullpath: '/juju-ui/widgets/charm-search.js'
4016 },
4017@@ -145,7 +160,9 @@
4018 'juju-inspector-widget': {
4019 fullpath: '/juju-ui/widgets/inspector-widget.js'
4020 },
4021-
4022+ 'juju-databinding': {
4023+ fullpath: '/juju-ui/views/databinding.js'
4024+ },
4025 'reconnecting-websocket': {
4026 fullpath: '/juju-ui/assets/javascripts/reconnecting-websocket.js'
4027 },
4028@@ -158,6 +175,10 @@
4029 fullpath: '/juju-ui/assets/javascripts/app-subapp-extension.js'
4030 },
4031
4032+ 'app-cookies-extension': {
4033+ fullpath: '/juju-ui/assets/javascripts/app-cookies-extension.js'
4034+ },
4035+
4036 'sub-app': {
4037 fullpath: '/juju-ui/assets/javascripts/sub-app.js'
4038 },
4039@@ -243,6 +264,10 @@
4040 fullpath: '/juju-ui/templates.js'
4041 },
4042
4043+ 'juju-view-container': {
4044+ fullpath: '/juju-ui/views/view-container.js'
4045+ },
4046+
4047 'juju-views': {
4048 use: [
4049 'handlebars',
4050@@ -323,6 +348,10 @@
4051 fullpath: '/juju-ui/store/charm.js'
4052 },
4053
4054+ 'juju-websocket-logging': {
4055+ fullpath: '/juju-ui/websocket-logging.js'
4056+ },
4057+
4058 'juju-controllers': {
4059 use: [
4060 'juju-env',
4061
4062=== modified file 'app/store/charm.js'
4063--- app/store/charm.js 2013-05-29 17:42:37 +0000
4064+++ app/store/charm.js 2013-06-26 14:26:26 +0000
4065@@ -157,12 +157,12 @@
4066 /**
4067 * Api helper for the updated charmworld api v1.
4068 *
4069- * @class Charmworld1
4070+ * @class Charmworld2
4071 * @extends {Base}
4072 *
4073 */
4074- ns.Charmworld1 = Y.Base.create('charmworld1', Y.Base, [], {
4075- _apiRoot: 'api/1',
4076+ ns.Charmworld2 = Y.Base.create('charmworld2', Y.Base, [], {
4077+ _apiRoot: 'api/2',
4078
4079 /**
4080 * Send the actual request and handle response from the api.
4081@@ -370,6 +370,25 @@
4082 }
4083
4084 this._makeRequest('charms/interesting', callbacks);
4085+ },
4086+
4087+ /**
4088+ Fetch the related charm info from the charmworld api.
4089+
4090+ @method related
4091+ @param {String} charmID The charm to find related charms for.
4092+ @param {Object} callbacks The success/failure callbacks to use.
4093+ @param {Object} bindscope An object scope to perform callbacks in.
4094+ @return {Object} data loaded from the api call.
4095+
4096+ */
4097+ related: function(charmID, callbacks, bindScope) {
4098+ var endpoint = 'charm/' + charmID + '/related';
4099+ if (bindScope) {
4100+ callbacks.success = Y.bind(callbacks.success, bindScope);
4101+ callbacks.failure = Y.bind(callbacks.failure, bindScope);
4102+ }
4103+ this._makeRequest(endpoint, callbacks);
4104 }
4105 }, {
4106 ATTRS: {
4107@@ -384,6 +403,9 @@
4108 apiHost: {
4109 required: true,
4110 setter: function(val) {
4111+ if (val && !val.match(/\/$/)) {
4112+ val = val + '/';
4113+ }
4114 // Make sure we update the datasource if our apiHost changes.
4115 var source = val + this._apiRoot + '/';
4116 this.set('datasource', new Y.DataSource.IO({ source: source }));
4117
4118=== modified file 'app/store/env/fakebackend.js'
4119--- app/store/env/fakebackend.js 2013-05-22 14:14:04 +0000
4120+++ app/store/env/fakebackend.js 2013-06-26 14:26:26 +0000
4121@@ -263,7 +263,9 @@
4122 },
4123
4124 /**
4125- Log out. If already logged out, no error is raised.
4126+ Log out. If already logged out, no error is raised.
4127+ @method logout
4128+ @return {undefined} Nothing.
4129 */
4130 logout: function() {
4131 this.set('authenticated', false);
4132@@ -287,7 +289,7 @@
4133 configYAML: The charm configuration options, expressed as a YAML
4134 string. You may provide only one of config or configYAML.
4135 unitCount: The number of units to be deployed.
4136- @return {undefined} Get the result from the callback.
4137+ @return {undefined} All results are passed to the callback.
4138 */
4139 deploy: function(charmId, callback, options) {
4140 if (!this.get('authenticated')) {
4141@@ -300,9 +302,7 @@
4142 this._loadCharm(
4143 charmId,
4144 {
4145- /**
4146- Deploy the successfully-obtained charm.
4147- */
4148+ // On success deploy the successfully-obtained charm.
4149 success: function(charm) {
4150 self._deployFromCharm(charm, callback, options);
4151 },
4152@@ -312,6 +312,48 @@
4153 },
4154
4155 /**
4156+ Set the given service to use the given charm, optionally forcing units in
4157+ error state to use the charm.
4158+
4159+ @method setCharm
4160+ @param {String} serviceName The name of the service to set.
4161+ @param {String} charmId The charm to use.
4162+ @param {Boolean} force Whether or not to force the issue.
4163+ @param {Function} callback A call that will receive an object, potentially
4164+ with an "error" attribute containing a string describing the problem.
4165+ @return {undefined} All results are passed to the callback.
4166+ */
4167+ setCharm: function(serviceName, charmId, force, callback) {
4168+ if (!this.get('authenticated')) {
4169+ return callback(UNAUTHENTICATEDERROR);
4170+ }
4171+ var self = this;
4172+ var service = this.db.services.getById(serviceName);
4173+ if (!service) {
4174+ return callback({error: 'Service "' + serviceName + '" not found.'});
4175+ }
4176+ var serviceInError = this.db.units.get_units_for_service(service)
4177+ .some(function(unit) {
4178+ return (/error/).test(unit.agent_state);
4179+ });
4180+ if (serviceInError && !force) {
4181+ return callback({error: 'Cannot set charm on a service with units in ' +
4182+ 'error without the force flag.'});
4183+ }
4184+ this._loadCharm(
4185+ charmId,
4186+ {
4187+ success: function(charm) {
4188+ service.set('charm', charm.get('id'));
4189+ self.changes.services[service.get('id')] = [service, true];
4190+ callback({});
4191+ },
4192+ failure: callback
4193+ }
4194+ );
4195+ },
4196+
4197+ /**
4198 Get a charm from a URL, via charmStore and/or db. Uses callbacks.
4199
4200 @method _loadCharm
4201@@ -343,18 +385,15 @@
4202 this.get('charmStore').loadByPath(
4203 charmIdParts.charm_store_path,
4204 {
4205- /**
4206- Convert the charm data to a charm and use the success callback.
4207- */
4208+ // Convert the charm data to a charm and use the success
4209+ // callback.
4210 success: function(data) {
4211 var charm = self._getCharmFromData(data);
4212 if (callbacks.success) {
4213 callbacks.success(charm);
4214 }
4215 },
4216- /**
4217- Inform the caller of an error using the charm store.
4218- */
4219+ // Inform the caller of an error using the charm store.
4220 failure: function(e) {
4221 // This is most likely an IOError stemming from an
4222 // invalid charm pointing to a bad URL and a read of a
4223@@ -399,6 +438,7 @@
4224 /**
4225 Deploy a charm, given the charm, a callback, and options.
4226
4227+ @method _deployFromCharm
4228 @param {Object} charm The charm to be deployed, from the db.
4229 @param {Function} callback A call that will receive an object either
4230 with an "error" attribute containing a string describing the problem,
4231
4232=== modified file 'app/store/env/go.js'
4233--- app/store/env/go.js 2013-05-21 16:30:38 +0000
4234+++ app/store/env/go.js 2013-06-26 14:26:26 +0000
4235@@ -388,6 +388,59 @@
4236 },
4237
4238 /**
4239+ Set a service's charm.
4240+
4241+ @method setCharm
4242+ @param {String} charm_url The URL of the charm.
4243+ @param {String} service_name The name of the service to be deployed.
4244+ @param {Function} callback A callable that must be called once the
4245+ operation is performed.
4246+ @return {undefined} Sends a message to the server only.
4247+ */
4248+ setCharm: function(service_name, charm_url, force, callback) {
4249+ var intermediateCallback = null;
4250+ if (callback) {
4251+ intermediateCallback = Y.bind(this.handleSetCharm, this,
4252+ callback, service_name, charm_url);
4253+ }
4254+ this._send_rpc(
4255+ { Type: 'Client',
4256+ Request: 'ServiceSetCharm',
4257+ Params: {
4258+ ServiceName: service_name,
4259+ CharmUrl: charm_url,
4260+ Force: force
4261+ }
4262+ },
4263+ intermediateCallback
4264+ );
4265+ },
4266+
4267+ /**
4268+ Transform the data returned from juju-core 'setCharm' into a form which
4269+ is suitable for the user callback.
4270+
4271+ @method handleSetCharm
4272+ @param {Function} userCallback The callback originally submitted by the
4273+ call site.
4274+ @param {String} service_name The name of the service. Passed in since
4275+ it is not part of the response.
4276+ @param {String} charm_url The URL of the charm. Passed in since
4277+ it is not part of the response.
4278+ @param {Object} data The response returned by the server.
4279+ @return {undefined} Nothing.
4280+ */
4281+ handleSetCharm: function(userCallback, service_name, charm_url, data) {
4282+ var transformedData = {
4283+ err: data.Error,
4284+ service_name: service_name,
4285+ charm_url: charm_url
4286+ };
4287+ // Call the original user callback.
4288+ userCallback(transformedData);
4289+ },
4290+
4291+ /**
4292 * Add units to the provided service.
4293 *
4294 * @method add_unit
4295
4296=== modified file 'app/store/env/sandbox.js'
4297--- app/store/env/sandbox.js 2013-05-17 14:51:05 +0000
4298+++ app/store/env/sandbox.js 2013-06-26 14:26:26 +0000
4299@@ -19,7 +19,7 @@
4300 'use strict';
4301
4302 /**
4303-Sandbox APIs mimicking communications with the Go and Juju backends.
4304+Sandbox APIs mimicking communications with the Go and Python backends.
4305
4306 @module env
4307 @submodule env.sandbox
4308@@ -682,6 +682,236 @@
4309 });
4310
4311 sandboxModule.PyJujuAPI = PyJujuAPI;
4312+
4313+ /**
4314+ A sandbox Juju environment using the Go API.
4315+
4316+ @class GoJujuAPI
4317+ */
4318+ function GoJujuAPI(config) {
4319+ GoJujuAPI.superclass.constructor.apply(this, arguments);
4320+ }
4321+
4322+ GoJujuAPI.NAME = 'sandbox-go-juju-api';
4323+ GoJujuAPI.ATTRS = {
4324+ state: {},
4325+ client: {}
4326+ };
4327+
4328+ Y.extend(GoJujuAPI, Y.Base, {
4329+
4330+ /**
4331+ Initializes.
4332+
4333+ @method initializer
4334+ @return {undefined} Nothing.
4335+ */
4336+ initializer: function() {
4337+ this.connected = false;
4338+ },
4339+
4340+ /**
4341+ Opens the connection to the sandbox Juju environment.
4342+ Called by ClientConnection, which sends itself.
4343+
4344+ @method open
4345+ @param {Object} client A ClientConnection.
4346+ @return {undefined} Nothing.
4347+ */
4348+ open: function(client) {
4349+ if (!this.connected) {
4350+ this.connected = true;
4351+ this.set('client', client);
4352+ } else if (this.get('client') !== client) {
4353+ throw 'INVALID_STATE_ERR : Connection is open to another client.';
4354+ }
4355+ },
4356+
4357+ /**
4358+ Closes the connection to the sandbox Juju environment.
4359+ Called by ClientConnection.
4360+
4361+ @method close
4362+ @return {undefined} Nothing.
4363+ */
4364+ close: function() {
4365+ if (this.connected) {
4366+ this.connected = false;
4367+ this.set('client', undefined);
4368+ }
4369+ },
4370+
4371+ /**
4372+ Do any extra work to destroy the object.
4373+
4374+ @method destructor
4375+ @return {undefined} Nothing.
4376+ */
4377+ destructor: function() {
4378+ this.close();
4379+ },
4380+
4381+ /**
4382+ Receives messages from the client and dispatches them.
4383+
4384+ @method receive
4385+ @param {Object} data A hash of data sent from the client.
4386+ @return {undefined} Nothing.
4387+ */
4388+ receive: function(data) {
4389+ console.log('client message', data);
4390+ if (this.connected) {
4391+ this['handle' + data.Type + data.Request](data,
4392+ this.get('client'), this.get('state'));
4393+ } else {
4394+ throw CLOSEDERROR;
4395+ }
4396+ },
4397+
4398+ /**
4399+ Handle Login messages to the state object.
4400+
4401+ @method handleAdminLogin
4402+ @param {Object} data The contents of the API arguments.
4403+ @param {Object} client The active ClientConnection.
4404+ @param {Object} state An instance of FakeBackend.
4405+ @return {undefined} Side effects only.
4406+ */
4407+ handleAdminLogin: function(data, client, state) {
4408+ data.Error = !state.login(data.Params.AuthTag, data.Params.Password);
4409+ client.receive(data);
4410+ },
4411+
4412+ /**
4413+ Handle EnvironmentView messages.
4414+
4415+ @method handleClientServiceGet
4416+ @param {Object} data The contents of the API arguments.
4417+ @param {Object} client The active ClientConnection.
4418+ @param {Object} state An instance of FakeBackend.
4419+ @return {undefined} Side effects only.
4420+ */
4421+ handleClientEnvironmentInfo: function(data, client, state) {
4422+ client.receive({
4423+ ProviderType: state.get('providerType'),
4424+ DefaultSeries: state.get('defaultSeries'),
4425+ Name: 'Sandbox'
4426+ });
4427+ },
4428+
4429+ /**
4430+ Handle WatchAll messages.
4431+
4432+ @method handleClientWatchAll
4433+ @param {Object} data The contents of the API arguments.
4434+ @param {Object} client The active ClientConnection.
4435+ @param {Object} state An instance of FakeBackend.
4436+ @return {undefined} Side effects only.
4437+ */
4438+ handleClientWatchAll: function(data, client, state) {
4439+ // TODO wire up delta stream to "Next" responses here.
4440+ client.receive({Response: {AllWatcherId: '42'}});
4441+ },
4442+
4443+ /**
4444+ Handle AllWatcher Next messages.
4445+
4446+ @method handleAllWatcherNext
4447+ @param {Object} data The contents of the API arguments.
4448+ @param {Object} client The active ClientConnection.
4449+ @param {Object} state An instance of FakeBackend.
4450+ @return {undefined} Side effects only.
4451+ */
4452+ handleAllWatcherNext: function(data, client, state) {
4453+ // This is a noop for the moment because it must exist but the current
4454+ // functionality does not depend on it having any effect.
4455+ // TODO See if there are any changes and if so, send them.
4456+ },
4457+
4458+ /**
4459+ Handle ServiceDeploy messages
4460+
4461+ @method handleClientServiceDeploy
4462+ @param {Object} data The contents of the API arguments.
4463+ @param {Object} client The active ClientConnection.
4464+ @param {Object} state An instance of FakeBackend.
4465+ @return {undefined} Side effects only.
4466+ */
4467+ handleClientServiceDeploy: function(data, client, state) {
4468+ var callback = function(result) {
4469+ var response = {RequestId: data.RequestId};
4470+ if (result.error) {
4471+ response.Error = result.error;
4472+ }
4473+ client.receive(response);
4474+ };
4475+ state.deploy(data.Params.CharmUrl, callback, {
4476+ name: data.Params.ServiceName,
4477+ config: data.Params.Config,
4478+ configYAML: data.Params.ConfigYAML,
4479+ unitCount: data.Params.NumUnits
4480+ });
4481+ },
4482+
4483+ /**
4484+ Handle ServiceSetCharm messages
4485+
4486+ @method handleClientServiceSetCharm
4487+ @param {Object} data The contents of the API arguments.
4488+ @param {Object} client The active ClientConnection.
4489+ @param {Object} state An instance of FakeBackend.
4490+ @return {undefined} Side effects only.
4491+ */
4492+ handleClientServiceSetCharm: function(data, client, state) {
4493+ var callback = function(result) {
4494+ var response = {RequestId: data.RequestId};
4495+ if (result.error) {
4496+ response.Error = result.error;
4497+ }
4498+ client.receive(response);
4499+ };
4500+ state.setCharm(data.Params.ServiceName, data.Params.CharmUrl,
4501+ data.Params.Force, callback);
4502+ },
4503+
4504+ /**
4505+ Handle SetAnnotations messages
4506+
4507+ @method handleClientSetAnnotations
4508+ @param {Object} data The contents of the API arguments.
4509+ @param {Object} client The active ClientConnection.
4510+ @param {Object} state An instance of FakeBackend.
4511+ @return {undefined} Side effects only.
4512+ */
4513+ handleClientSetAnnotations: function(data, client, state) {
4514+ var serviceId = /service-([^ ]*)$/.exec(data.Params.Tag)[1];
4515+ var reply = state.updateAnnotations(serviceId, data.Params.Pairs);
4516+ client.receive({
4517+ RequestId: data.RequestId,
4518+ Error: reply.error});
4519+ },
4520+
4521+ /**
4522+ Handle ServiceGet messages
4523+
4524+ @method handleClientServiceGet
4525+ @param {Object} data The contents of the API arguments.
4526+ @param {Object} client The active ClientConnection.
4527+ @param {Object} state An instance of FakeBackend.
4528+ @return {undefined} Side effects only.
4529+ */
4530+ handleClientServiceGet: function(data, client, state) {
4531+ var reply = state.getService(data.Params.ServiceName);
4532+ // TODO Include the optional Config or Constraints in the response.
4533+ client.receive({
4534+ RequestId: data.RequestId,
4535+ Error: reply.error,
4536+ Response: {Service: data.Params.ServiceName}});
4537+ }
4538+
4539+ });
4540+
4541+ sandboxModule.GoJujuAPI = GoJujuAPI;
4542 }, '0.1.0', {
4543 requires: [
4544 'base',
4545
4546=== modified file 'app/subapps/browser/browser.js'
4547--- app/subapps/browser/browser.js 2013-05-29 18:00:54 +0000
4548+++ app/subapps/browser/browser.js 2013-06-26 14:26:26 +0000
4549@@ -34,6 +34,7 @@
4550
4551 @class Browser
4552 @extends {juju.SubApp}
4553+
4554 */
4555 ns.Browser = Y.Base.create('subapp-browser', Y.juju.SubApp, [], {
4556 // Mark the entire subapp has hidden.
4557@@ -41,10 +42,11 @@
4558 viewmodes: ['minimized', 'fullscreen', 'sidebar'],
4559
4560 /**
4561- Show or hide the details panel.
4562-
4563- @method _detailsVisible
4564- @param {Boolean} visible set the panel to hide or show.
4565+ Show or hide the details panel.
4566+
4567+ @method _detailsVisible
4568+ @param {Boolean} visible set the panel to hide or show.
4569+
4570 */
4571 _detailsVisible: function(visible) {
4572 var detailsNode = Y.one('.bws-view-data');
4573@@ -59,11 +61,12 @@
4574 },
4575
4576 /**
4577- Given the current subapp state, generate a url to pass up to the
4578- routing code to route to.
4579-
4580- @method _getStateUrl
4581- @param {Object} change the values to change in the current state.
4582+ Given the current subapp state, generate a url to pass up to the
4583+ routing code to route to.
4584+
4585+ @method _getStateUrl
4586+ @param {Object} change the values to change in the current state.
4587+
4588 */
4589 _getStateUrl: function(change) {
4590 var urlParts = [];
4591@@ -82,6 +85,9 @@
4592 urlParts.push(this._viewState.viewmode);
4593 if (this._viewState.search) {
4594 urlParts.push('search');
4595+ } else if (this._oldState.search) {
4596+ // We had a search, but are moving away; clear the old search.
4597+ this._filter.reset();
4598 }
4599 if (this._viewState.charmID) {
4600 urlParts.push(this._viewState.charmID);
4601@@ -97,11 +103,12 @@
4602 },
4603
4604 /**
4605- Generate a standard shared set of cfg all Views can expect to see.
4606-
4607- @method _getViewCfg
4608- @param {Object} cfg additional config to merge into the default view
4609- config.
4610+ Generate a standard shared set of cfg all Views can expect to see.
4611+
4612+ @method _getViewCfg
4613+ @param {Object} cfg additional config to merge into the default view
4614+ config.
4615+
4616 */
4617 _getViewCfg: function(cfg) {
4618 // We always add the _filter data to every request because most of them
4619@@ -123,6 +130,7 @@
4620 _initState: function() {
4621 this._oldState = {
4622 charmID: null,
4623+ hash: null,
4624 querystring: null,
4625 search: null,
4626 viewmode: null
4627@@ -149,6 +157,7 @@
4628 Determine if we should render the charm details based on the current
4629 state.
4630
4631+ @method _shouldShowCharm
4632 @return {Boolean} true if should show.
4633 */
4634 _shouldShowCharm: function() {
4635@@ -169,23 +178,34 @@
4636 Determine if we should render the editorial content based on the current
4637 state.
4638
4639+ @method _shouldShowEditorial
4640 @return {Boolean} true if should show.
4641 */
4642 _shouldShowEditorial: function() {
4643- if (
4644- !this._viewState.search &&
4645+ var should = false;
4646+ // If the viewmode has changed, and seach is not enabled then yes
4647+ if (!this._viewState.search &&
4648 this._hasStateChanged('viewmode')
4649 ) {
4650- return true;
4651- } else {
4652- return false;
4653- }
4654+ should = true;
4655+ }
4656+
4657+ // Even if viewmode hasn't changed, but search has changed and is false
4658+ // then yes
4659+ if (!this._viewState.search &&
4660+ this._hasStateChanged('search')
4661+ ) {
4662+ should = true;
4663+ }
4664+
4665+ return should;
4666 },
4667
4668 /**
4669 Determine if we should render the search results based on the current
4670 state.
4671
4672+ @method _shouldShowSearch
4673 @return {Boolean} true if should show.
4674 */
4675 _shouldShowSearch: function() {
4676@@ -231,17 +251,23 @@
4677 // Clear out any parts of /sidebar/search, /sidebar, or /search from the
4678 // id. See if we still really have an id.
4679 var match =
4680- /^(sidebar|fullscreen|minimized|search|test\/index\.html)\/?(search)?/;
4681+ /^\/?(sidebar|fullscreen|minimized|search|test\/index\.html)\/?(search)?\/?/;
4682
4683 if (id && id.match(match)) {
4684 // Strip it out.
4685 id = id.replace(match, '');
4686-
4687 // if the id is now empty, set it to null.
4688 if (id === '') {
4689 id = null;
4690 }
4691 }
4692+
4693+ if (id) {
4694+ // Strip any extra slashes off the start/end of the id.
4695+ id = id.replace(/^\//, '');
4696+ id = id.replace(/\/$/, '');
4697+ }
4698+
4699 return id;
4700 },
4701
4702@@ -260,6 +286,21 @@
4703 },
4704
4705 /**
4706+ Does our app instance have a valid store? If not, then we should ignore
4707+ a lot of work since we can't do it anyway. Sanity check our
4708+ information. During test running, for instance, we don't have a valid
4709+ store to work with and that's ok.
4710+
4711+ @method _hasValidStore
4712+ @return {Boolean} do we have a valid store or not.
4713+
4714+ */
4715+ _hasValidStore: function() {
4716+ var store = this.get('store');
4717+ return !store.get('noop');
4718+ },
4719+
4720+ /**
4721 Update the oldState with the viewState now that we're done processing
4722 the request.
4723
4724@@ -282,10 +323,15 @@
4725 // Update the viewmode. Every request has a viewmode.
4726 var path = req.path,
4727 params = req.params,
4728- query = req.query;
4729+ query = req.query,
4730+ hash = window.location.hash;
4731
4732 this._viewState.viewmode = params.viewmode;
4733
4734+ if (hash) {
4735+ this._viewState.hash = hash.replace('/', '');
4736+ }
4737+
4738 // Check for a charm id in the request.
4739 if (params.id && params.id !== 'search') {
4740 this._viewState.charmID = params.id;
4741@@ -387,11 +433,20 @@
4742 renderCharmDetails: function(req, res, next) {
4743 var charmID = this._viewState.charmID;
4744 var extraCfg = {
4745+ activeTab: this._viewState.hash,
4746 charmID: charmID,
4747 container: Y.Node.create('<div class="charmview"/>'),
4748 deploy: this.get('deploy')
4749 };
4750
4751+ // If the only thing that changed was the hash, then don't redraw. It's
4752+ // just someone clicking a tab in the UI.
4753+ if (this._details && this._hasStateChanged('hash') &&
4754+ !(this._hasStateChanged('charmID') ||
4755+ this._hasStateChanged('viewmode'))) {
4756+ return;
4757+ }
4758+
4759 // The details view needs to know if we're using a fullscreen template
4760 // or the sidebar version.
4761 if (this._viewState.viewmode === 'fullscreen') {
4762@@ -448,7 +503,6 @@
4763 extraCfg.activeID = this._viewState.charmID;
4764 }
4765
4766-
4767 this._editorial = new Y.juju.browser.views.EditorialView(
4768 this._getViewCfg(extraCfg));
4769
4770@@ -456,6 +510,16 @@
4771 // Add any sidebar charms to the running cache.
4772 this._cache = Y.merge(this._cache, ev.cache);
4773 }, this);
4774+ this._editorial.on(this._editorial.EV_CATEGORY_LINK_CLICKED,
4775+ function(ev) {
4776+ var change = {
4777+ search: true,
4778+ filter: {
4779+ categories: [ev.category]
4780+ }
4781+ };
4782+ this.fire('viewNavigate', {change: change});
4783+ });
4784
4785 this._editorial.render(this._cache.interesting);
4786 this._editorial.addTarget(this);
4787@@ -622,9 +686,6 @@
4788 if (this._search) {
4789 this._search.set('activeID', null);
4790 }
4791-
4792-
4793-
4794 }
4795
4796 // Sync that the state has changed.
4797@@ -633,6 +694,100 @@
4798 },
4799
4800 /**
4801+ When there's no charm or viewmode default to a sidebar view for all
4802+ pages.
4803+
4804+ @method routeSidebarDefault
4805+ @param {Request} req current request object.
4806+ @param {Response} res current response object.
4807+ @param {function} next callable for the next route in the chain.
4808+
4809+ */
4810+ routeSidebarDefault: function(req, res, next) {
4811+ // Check if there's any path. If there is, someone else will handle
4812+ // routing it. Just carry on.
4813+ if (req.path.replace(/\//, '') !== '') {
4814+ next();
4815+ return;
4816+ }
4817+
4818+ // For the * request there will be no req.params. Update it forcing
4819+ // sidebar default viewmode.
4820+ req.params = {
4821+ viewmode: 'sidebar'
4822+ };
4823+
4824+ // Update the state for the rest of things to figure out what to do.
4825+ this._updateState(req);
4826+
4827+ // Once the state is updated determine visibility of our Nodes.
4828+ this.updateVisible();
4829+
4830+ // Don't bother routing if we're hidden.
4831+ if (!this.hidden) {
4832+ this.sidebar(req, res, next);
4833+ } else {
4834+ // Let the next route go on.
4835+ next();
4836+ }
4837+ },
4838+
4839+ /**
4840+ A url direct to a charm id works, however it needs to default the
4841+ viewmode to sidebar in that case.
4842+
4843+ Almost any url with a component to it matches this route. We need to
4844+ check if there are exactly *two* parts and if so, check if they're a
4845+ valid id-able segment. (Not /sidebar/search for instance)
4846+
4847+ @method routeDirectCharmId
4848+ @param {Request} req current request object.
4849+ @param {Response} res current response object.
4850+ @param {function} next callable for the next route in the chain.
4851+
4852+ */
4853+ routeDirectCharmId: function(req, res, next) {
4854+ // If we don't have a valid store we can't do any work here.
4855+ if (!this._hasValidStore()) {
4856+ return;
4857+ }
4858+
4859+ // Check if we have exactly two url parts in our path.
4860+ // The best way to count the parts is to strip the start/end slash and
4861+ // then split on the rest. We only care if there are exactly two parts.
4862+ var idBits = req.path.replace(/^\//, '').replace(/\/$/, '').split('/'),
4863+ id = null;
4864+
4865+ if (idBits.length === 2) {
4866+ id = this._stripViewMode(req.path);
4867+ }
4868+ if (!id) {
4869+ next();
4870+ return;
4871+ } else {
4872+ // We've got a valid id. Setup the params for our view state.
4873+ req.params = {
4874+ id: id,
4875+ viewmode: 'sidebar'
4876+ };
4877+ }
4878+
4879+ // Update the state for the rest of things to figure out what to do.
4880+ this._updateState(req);
4881+
4882+ // Once the state is updated determine visibility of our Nodes.
4883+ this.updateVisible();
4884+
4885+ // Don't bother routing if we're hidden.
4886+ if (!this.hidden) {
4887+ this.sidebar(req, res, next);
4888+ } else {
4889+ // Let the next route go on.
4890+ next();
4891+ }
4892+ },
4893+
4894+ /**
4895 Dispatch to the correct viewmode based on the route that was hit.
4896
4897 @method routeView
4898@@ -681,6 +836,8 @@
4899 Based on the viewmode and the hidden check what divs we should be
4900 showing or hiding.
4901
4902+ @method updateVisible
4903+ @return {undefined} Nothing.
4904 */
4905 updateVisible: function() {
4906 var minview = this.get('minNode'),
4907@@ -723,8 +880,8 @@
4908
4909 /**
4910 @attribute store
4911- @default Charmworld1
4912- @type {Charmworld1}
4913+ @default Charmworld2
4914+ @type {Charmworld2}
4915 */
4916 store: {
4917 /**
4918@@ -733,7 +890,7 @@
4919 tests there's no config for talking to the api so we have to watch
4920 out in test runs and allow the store to be broken.
4921
4922- method store.valueFn
4923+ @method store.valueFn
4924 */
4925 valueFn: function() {
4926 var cfg = {
4927@@ -746,7 +903,7 @@
4928 } else {
4929 cfg.apiHost = window.juju_config.charmworldURL;
4930 }
4931- return new Y.juju.Charmworld1(cfg);
4932+ return new Y.juju.Charmworld2(cfg);
4933 }
4934 },
4935
4936@@ -759,8 +916,8 @@
4937 value: [
4938 // Show the sidebar on all places if its not manually shut off or
4939 // turned into a fullscreen route.
4940- { path: '*', callbacks: 'routeView'},
4941- { path: '/*id/', callbacks: 'routeView'},
4942+ { path: '*', callbacks: 'routeSidebarDefault'},
4943+ { path: '/*id/', callbacks: 'routeDirectCharmId'},
4944 { path: '/:viewmode/', callbacks: 'routeView' },
4945 { path: '/:viewmode/search/', callbacks: 'routeView' },
4946 { path: '/:viewmode/search/*id/', callbacks: 'routeView' },
4947@@ -797,6 +954,8 @@
4948 /**
4949 Find the minNode and cache it for later use.
4950
4951+ @attribute minNode
4952+ @readOnly
4953 */
4954 valueFn: function() {
4955 return Y.one('#subapp-browser-min');
4956
4957=== modified file 'app/subapps/browser/templates/browser_charm.handlebars'
4958--- app/subapps/browser/templates/browser_charm.handlebars 2013-05-29 14:11:48 +0000
4959+++ app/subapps/browser/templates/browser_charm.handlebars 2013-06-26 14:26:26 +0000
4960@@ -1,5 +1,5 @@
4961 <div class="charm yui3-g">
4962- <div class="content yui3-u-{{#if isFullscreen}}5-6{{else}}1{{/if}}">
4963+ <div class="content yui3-u-{{#if isFullscreen}}3-4{{else}}1{{/if}}">
4964 <div class="nav">
4965 <a href="" class="back">
4966 {{#if isFullscreen}}
4967@@ -9,11 +9,16 @@
4968 Close
4969 {{/if}}
4970 </a>
4971+ {{! The add class is used by the deploy method in
4972+ test/test_charm_running.py. If you change this name, change
4973+ the other one too! }}
4974 <a href="" class="add">Add</a>
4975- <a href="" class="share">
4976- <img src="/juju-ui/assets/images/browser_share_button_icon.png"
4977- alt="Share" />Share
4978- </a>
4979+ {{#if shareFlag}}
4980+ <a href="" class="share">
4981+ <img src="/juju-ui/assets/images/browser_share_button_icon.png"
4982+ alt="Share" />Share
4983+ </a>
4984+ {{/if}}
4985 </div>
4986
4987 {{#if failingProviders}}
4988@@ -33,11 +38,17 @@
4989 title="This charm has been reviewed."></span>
4990 {{/if}}
4991
4992- {{#if hasIcon}}
4993+ {{#if shouldShowIcon}}
4994 <img src="{{charmFilePath id 'icon.svg'}}" alt="{{ name }} icon" class="icon">
4995 {{else}}
4996- <div class="charm-icon">
4997- </div>
4998+ {{#if mainCategory}}
4999+ <div title="category {{mainCategory}}"
5000+ class="category-icon sprite charm-{{mainCategory}}-160">
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: