├── .gitignore
├── Cakefile
├── Gemfile
├── Gemfile.lock
├── Guardfile
├── LICENSE
├── README.md
├── Rakefile
├── TODO.md
├── app.rb
├── assets
├── images
│ ├── bg-grid.png
│ ├── blacky.png
│ ├── crosshairs.png
│ ├── grid.png
│ ├── multiple.png
│ └── whitey.png
├── javascripts
│ ├── app
│ │ ├── controllers
│ │ │ ├── element.module.coffee
│ │ │ ├── element
│ │ │ │ └── resizing.module.coffee
│ │ │ ├── elements
│ │ │ │ ├── button.module.coffee
│ │ │ │ ├── canvas.module.coffee
│ │ │ │ ├── ellipsis.module.coffee
│ │ │ │ ├── image.module.coffee
│ │ │ │ ├── input.module.coffee
│ │ │ │ ├── line.module.coffee
│ │ │ │ ├── rectangle.module.coffee
│ │ │ │ ├── text.module.coffee
│ │ │ │ └── triangle.module.coffee
│ │ │ ├── header.module.coffee
│ │ │ ├── inspector.module.coffee
│ │ │ ├── inspector
│ │ │ │ ├── background.module.coffee
│ │ │ │ ├── border.module.coffee
│ │ │ │ ├── border_radius.module.coffee
│ │ │ │ ├── box_shadow.module.coffee
│ │ │ │ ├── dimensions.module.coffee
│ │ │ │ ├── font.module.coffee
│ │ │ │ ├── opacity.module.coffee
│ │ │ │ ├── popup_menu.module.coffee
│ │ │ │ ├── stage.module.coffee
│ │ │ │ ├── text_position.module.coffee
│ │ │ │ ├── text_shadow.module.coffee
│ │ │ │ └── text_style.module.coffee
│ │ │ ├── stage.module.coffee
│ │ │ └── stage
│ │ │ │ ├── clipboard.module.coffee
│ │ │ │ ├── context_menu.module.coffee
│ │ │ │ ├── dragging.module.coffee
│ │ │ │ ├── drop_area.module.coffee
│ │ │ │ ├── history.module.coffee
│ │ │ │ ├── key_bindings.module.coffee
│ │ │ │ ├── resizing.module.coffee
│ │ │ │ ├── select_area.module.coffee
│ │ │ │ ├── selection.module.coffee
│ │ │ │ ├── snapping.module.coffee
│ │ │ │ └── zindex.module.coffee
│ │ ├── index.module.coffee
│ │ ├── models
│ │ │ ├── history.module.coffee
│ │ │ ├── properties.module.coffee
│ │ │ ├── properties
│ │ │ │ ├── background.module.coffee
│ │ │ │ ├── border.module.coffee
│ │ │ │ ├── color.module.coffee
│ │ │ │ └── shadow.module.coffee
│ │ │ ├── property.module.coffee
│ │ │ └── serialize.module.coffee
│ │ ├── parsers
│ │ │ ├── css.module.js
│ │ │ ├── css.pegjs
│ │ │ └── import.module.coffee
│ │ └── views
│ │ │ ├── context_menu.jst.eco
│ │ │ ├── header.jst.eco
│ │ │ ├── inspector.jst.eco
│ │ │ └── inspector
│ │ │ ├── background.jst.eco
│ │ │ ├── background
│ │ │ ├── linear_gradient.jst.eco
│ │ │ ├── list.jst.eco
│ │ │ ├── menu.jst.eco
│ │ │ └── url.jst.eco
│ │ │ ├── border.jst.eco
│ │ │ ├── border_radius.jst.eco
│ │ │ ├── box_shadow.jst.eco
│ │ │ ├── box_shadow
│ │ │ ├── list.jst.eco
│ │ │ └── menu.jst.eco
│ │ │ ├── dimensions.jst.eco
│ │ │ ├── font.jst.eco
│ │ │ ├── opacity.jst.eco
│ │ │ ├── text_position.jst.eco
│ │ │ └── text_shadow.jst.eco
│ ├── application.js
│ └── lib
│ │ ├── collection.module.coffee
│ │ ├── color_picker.module.coffee
│ │ ├── ext
│ │ └── jquery.coffee
│ │ ├── gradient_picker.module.coffee
│ │ ├── popup.module.coffee
│ │ ├── position_picker.module.coffee
│ │ ├── utils.module.coffee
│ │ └── views
│ │ └── color_picker.jst.eco
└── stylesheets
│ ├── app
│ ├── color_picker.css.styl
│ ├── context_menu.css.styl
│ ├── gradient_picker.css.styl
│ ├── header.css.styl
│ ├── index.css.styl
│ ├── inspector.css.styl
│ ├── inspector
│ │ ├── background.css.styl
│ │ ├── border.css.styl
│ │ ├── border_radius.css.styl
│ │ ├── box_shadow.css.styl
│ │ ├── dimensions.css.styl
│ │ ├── font.css.styl
│ │ ├── opacity.css.styl
│ │ ├── popup_menu.css.styl
│ │ ├── text_position.css.styl
│ │ └── text_shadow.css.styl
│ ├── popup.css.styl
│ └── stage.css.styl
│ ├── application.css
│ ├── mixins.styl
│ └── theme.css.styl
├── config.ru
├── public
├── application.icns
├── application.png
├── assets
│ ├── app
│ │ ├── controllers
│ │ │ ├── element.module.js
│ │ │ ├── element
│ │ │ │ └── resizing.module.js
│ │ │ ├── elements
│ │ │ │ ├── button.module.js
│ │ │ │ ├── canvas.module.js
│ │ │ │ ├── ellipsis.module.js
│ │ │ │ ├── image.module.js
│ │ │ │ ├── input.module.js
│ │ │ │ ├── line.module.js
│ │ │ │ ├── rectangle.module.js
│ │ │ │ ├── text.module.js
│ │ │ │ └── triangle.module.js
│ │ │ ├── header.module.js
│ │ │ ├── inspector.module.js
│ │ │ ├── inspector
│ │ │ │ ├── background.module.js
│ │ │ │ ├── border.module.js
│ │ │ │ ├── border_radius.module.js
│ │ │ │ ├── box_shadow.module.js
│ │ │ │ ├── dimensions.module.js
│ │ │ │ ├── font.module.js
│ │ │ │ ├── opacity.module.js
│ │ │ │ ├── popup_menu.module.js
│ │ │ │ ├── stage.module.js
│ │ │ │ ├── text_position.module.js
│ │ │ │ ├── text_shadow.module.js
│ │ │ │ └── text_style.module.js
│ │ │ ├── stage.module.js
│ │ │ └── stage
│ │ │ │ ├── clipboard.module.js
│ │ │ │ ├── context_menu.module.js
│ │ │ │ ├── dragging.module.js
│ │ │ │ ├── drop_area.module.js
│ │ │ │ ├── history.module.js
│ │ │ │ ├── key_bindings.module.js
│ │ │ │ ├── resizing.module.js
│ │ │ │ ├── select_area.module.js
│ │ │ │ ├── selection.module.js
│ │ │ │ ├── snapping.module.js
│ │ │ │ └── zindex.module.js
│ │ ├── index.module.js
│ │ ├── models
│ │ │ ├── history.module.js
│ │ │ ├── properties.module.js
│ │ │ ├── properties
│ │ │ │ ├── background.module.js
│ │ │ │ ├── border.module.js
│ │ │ │ ├── color.module.js
│ │ │ │ └── shadow.module.js
│ │ │ ├── property.module.js
│ │ │ └── serialize.module.js
│ │ └── parsers
│ │ │ ├── background.pegjs
│ │ │ ├── color.module.js
│ │ │ ├── color.pegjs
│ │ │ ├── css.module.js
│ │ │ ├── css.pegjs
│ │ │ ├── import.module.js
│ │ │ ├── json.pegjs
│ │ │ └── shadow.pegjs
│ ├── application.css
│ ├── application.js
│ ├── bg-grid.png
│ ├── blacky.png
│ ├── crosshairs.png
│ ├── grid.png
│ ├── lib
│ │ ├── collection.module.js
│ │ ├── color_picker.module.js
│ │ ├── gradient_picker.module.js
│ │ ├── popup.module.js
│ │ ├── position_picker.module.js
│ │ └── utils.module.js
│ ├── multiple.png
│ ├── parsers
│ │ ├── background.pegjs
│ │ ├── color.module.js
│ │ ├── color.pegjs
│ │ ├── css.module.js
│ │ ├── css.pegjs
│ │ ├── gradient.pegjs
│ │ ├── json.module.js
│ │ ├── json.pegjs
│ │ ├── shadow.peg
│ │ └── shadow.pegjs
│ ├── sprockets
│ │ └── commonjs.rb
│ └── whitey.png
└── index.html
└── vendor
└── assets
└── javascripts
├── gfx.coffee
├── gfx
└── effects.coffee
├── jquery.js
├── spine.coffee
└── spine
├── ajax.coffee
├── list.coffee
├── local.coffee
├── manager.coffee
├── relation.coffee
├── route.coffee
├── tabs.coffee
└── tmpl.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | build
3 | Stylo.app
4 |
--------------------------------------------------------------------------------
/Cakefile:
--------------------------------------------------------------------------------
1 | {print} = require 'util'
2 | {spawn} = require 'child_process'
3 |
4 | task 'build', 'Build lib/ from src/', ->
5 | coffee = spawn 'coffee', ['-c', '-o', 'lib', 'src']
6 | coffee.stderr.on 'data', (data) ->
7 | process.stderr.write data.toString()
8 | coffee.stdout.on 'data', (data) ->
9 | print data.toString()
10 | coffee.on 'exit', (code) ->
11 | callback?() if code is 0
12 |
13 | task 'watch', 'Watch src/ for changes', ->
14 | coffee = spawn 'coffee', ['-w', '-c', '-o', 'lib', 'src']
15 | coffee.stderr.on 'data', (data) ->
16 | process.stderr.write data.toString()
17 | coffee.stdout.on 'data', (data) ->
18 | print data.toString()
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gem 'sinatra', :require => 'sinatra/base'
4 | gem 'rack'
5 | gem 'sinatra-contrib'
6 | gem 'json'
7 | gem 'thin'
8 |
9 | gem 'coffee-script'
10 | gem 'eco'
11 | gem 'uglifier'
12 | gem 'sprockets'
13 | gem 'sprockets-commonjs', :git => 'git://github.com/maccman/sprockets-commonjs.git'
14 | gem 'stylus'
15 |
16 | group :test, :development do
17 | gem 'growl'
18 | gem 'guard-sprockets2', :git => 'git://github.com/maccman/guard-sprockets2.git'
19 | gem 'rb-fsevent'
20 | gem 'ruby-debug19', :require => 'ruby-debug'
21 | end
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: git://github.com/maccman/guard-sprockets2.git
3 | revision: e2883010e38007bb7e4161fe5f8e3e8db8069c2d
4 | specs:
5 | guard-sprockets2 (0.0.2)
6 | guard
7 | sprockets (~> 2.0)
8 |
9 | GIT
10 | remote: git://github.com/maccman/sprockets-commonjs.git
11 | revision: 36d8e9ff61653afbc3782d098c18075e7c3ce906
12 | specs:
13 | sprockets-commonjs (0.0.2)
14 | sprockets (~> 2.4.0)
15 |
16 | GEM
17 | remote: http://rubygems.org/
18 | specs:
19 | archive-tar-minitar (0.5.2)
20 | backports (2.5.1)
21 | coffee-script (2.2.0)
22 | coffee-script-source
23 | execjs
24 | coffee-script-source (1.3.1)
25 | columnize (0.3.6)
26 | daemons (1.1.8)
27 | eco (1.0.0)
28 | coffee-script
29 | eco-source
30 | execjs
31 | eco-source (1.1.0.rc.1)
32 | eventmachine (0.12.10)
33 | execjs (1.3.0)
34 | multi_json (~> 1.0)
35 | ffi (1.0.11)
36 | growl (1.0.3)
37 | guard (1.0.1)
38 | ffi (>= 0.5.0)
39 | thor (~> 0.14.6)
40 | hike (1.2.1)
41 | json (1.6.6)
42 | linecache19 (0.5.12)
43 | ruby_core_source (>= 0.1.4)
44 | multi_json (1.2.0)
45 | rack (1.4.1)
46 | rack-protection (1.2.0)
47 | rack
48 | rack-test (0.6.1)
49 | rack (>= 1.0)
50 | rb-fsevent (0.9.1)
51 | ruby-debug-base19 (0.11.25)
52 | columnize (>= 0.3.1)
53 | linecache19 (>= 0.5.11)
54 | ruby_core_source (>= 0.1.4)
55 | ruby-debug19 (0.11.6)
56 | columnize (>= 0.3.1)
57 | linecache19 (>= 0.5.11)
58 | ruby-debug-base19 (>= 0.11.19)
59 | ruby_core_source (0.1.5)
60 | archive-tar-minitar (>= 0.5.2)
61 | sinatra (1.3.2)
62 | rack (~> 1.3, >= 1.3.6)
63 | rack-protection (~> 1.2)
64 | tilt (~> 1.3, >= 1.3.3)
65 | sinatra-contrib (1.3.1)
66 | backports (>= 2.0)
67 | eventmachine
68 | rack-protection
69 | rack-test
70 | sinatra (~> 1.3.0)
71 | tilt (~> 1.3)
72 | sprockets (2.4.0)
73 | hike (~> 1.2)
74 | multi_json (~> 1.0)
75 | rack (~> 1.0)
76 | tilt (~> 1.1, != 1.3.0)
77 | stylus (0.5.0)
78 | execjs
79 | stylus-source
80 | stylus-source (0.24.0)
81 | thin (1.3.1)
82 | daemons (>= 1.0.9)
83 | eventmachine (>= 0.12.6)
84 | rack (>= 1.0.0)
85 | thor (0.14.6)
86 | tilt (1.3.3)
87 | uglifier (1.2.4)
88 | execjs (>= 0.3.0)
89 | multi_json (>= 1.0.2)
90 |
91 | PLATFORMS
92 | ruby
93 |
94 | DEPENDENCIES
95 | coffee-script
96 | eco
97 | growl
98 | guard-sprockets2!
99 | json
100 | rack
101 | rb-fsevent
102 | ruby-debug19
103 | sinatra
104 | sinatra-contrib
105 | sprockets
106 | sprockets-commonjs!
107 | stylus
108 | thin
109 | uglifier
110 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | require './app'
2 |
3 | guard 'sprockets2', :sprockets => App.sprockets, :precompile => App.precompile do
4 | watch(%r{^assets/.+$})
5 | watch('app.rb')
6 | end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Alex MacCaw (info@eribium.org)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Stylo
2 |
3 | Stylo is a web app designer written in [CoffeeScript](http://coffeescript.org) and [Spine](http://spinejs.com). It allows you to manipulate various HTML elements, add styles and edit text.
4 |
5 | You can find a [demo here](http://styloapp.com/) *(webkit only)*.
6 |
7 | [](http://styloapp.com/)
8 |
9 | ## Code examples
10 |
11 | The code should provide good examples of how to achieve the following:
12 |
13 | * [Element snapping](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/snapping.module.coffee), [resizing](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/resizing.module.coffee) and [drag/drop](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/dragging.module.coffee)
14 | * [Copy/paste](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/clipboard.module.coffee), [undo/redo](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/history.module.coffee) & [keyboard shortcuts](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/key_bindings.module.coffee)
15 | * [Context menus](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/context_menu.module.coffee) and [z-index ordering](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/controllers/stage/zindex.module.coffee)
16 | * [Color picker using canvas](https://github.com/maccman/stylo/blob/master/assets/javascripts/lib/color_picker.module.coffee)
17 | * [JSON object instance serialization](https://github.com/maccman/stylo/blob/master/assets/javascripts/app/models/serialize.module.coffee)
18 |
19 | ## More
20 |
21 | For more information, please see the [blog post](http://blog.alexmaccaw.com/stylo).
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | namespace :macgap do
2 | task :build do
3 | `macgap build --name Stylo ./public`
4 | end
5 | end
6 |
7 | namespace :pegjs do
8 | task :build do
9 | `pegjs assets/javascripts/app/parsers/css.pegjs assets/javascripts/app/parsers/css.module.js`
10 | end
11 | end
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | A Photoshop replacement. Inspired by Keynote. Democratizing design.
2 |
3 | ##TODO
4 |
5 | Phase 1:
6 |
7 | * ✓ Move
8 | * ✓ Resize
9 | * ✓ Select
10 | * ✓ Select multiple items
11 | * ✓ Toolbar menu
12 | * Inspector panel (Element/Text/Ruler)
13 | * ½ Background (direction)
14 | * ✓ Border
15 | * ✓ Shadow
16 | * ✓ Text-shadow
17 | * Rotation
18 | * ✓ Opacity
19 | * ✓ content editable
20 | * ✓ context menu
21 | * Font alignment
22 | * ✓ Font family/size/color
23 | * ✓ Text-shadow
24 | * ✓ Text-spacing (line height)
25 |
26 | Phase 2:
27 |
28 | * ✓ Stage snapping
29 | * ✓ Color picker
30 | * Stage inspector
31 | * ✓ Z-index
32 | * ✓ Copy paste
33 | * ✓ Undo/redo
34 | * ½ Saving/opening
35 |
36 | Phase 3:
37 |
38 | * Resizing snapping
39 | * ✓ Edge snapping
40 | * ✓ Element snapping
41 | * Pen tool
42 |
43 | Nice to have:
44 |
45 | * HTML import
46 | * ✓ Keyboard shortcuts
47 | * Versioning
48 | * ✓ Exporting CSS
49 | * Share (dropbox/email?)
50 | * WebFonts
51 | * Layers?
52 | * ✓ Context menu
53 | * Similar distance snapping
54 | * Bootstrap integration
55 |
56 | ##Elements
57 |
58 | * Triangle
59 | * ✓ Rectangle
60 | * ✓ Ellipsis
61 | * Checkbox input
62 | * ✓ Button input
63 | * ✓ Text Input
64 | * ✓ Text
65 | * ✓ Image
66 | * Line
67 |
68 | ##Fixes:
69 |
70 | * Background image
71 | * Background gradient rotation
72 | * Drop image needs background image set
73 | * Color inspector movable window
74 | * Dragging right and up doesn't select elements - offset by the header
75 | * We want the resize to be around the selection?
76 | * ✓ We want the inspector not to completely re-render on selection
77 | * ✓ You can have negative widths
78 |
79 | -------------------------------------------------------------------------------------------------------------------------------------
80 |
81 | #Save to png
82 |
83 | https://github.com/paulhammond/webkit2png/blob/master/webkit2png
84 | https://developer.apple.com/library/mac/#samplecode/ScreenSnapshot/Listings/ScreenSnapshot_ImageView_m.html#//apple_ref/doc/uid/DTS40011158-ScreenSnapshot_ImageView_m-DontLinkElementID_7
85 | http://www.cocoadev.com/index.pl?HowToAcquireScreenshots
86 | http://www.sticksoftware.com/developer/Screensnap.m.txt
87 | https://github.com/appcelerator/titanium_desktop/blob/master/modules/ti.Platform/PlatformMac.mm
88 |
89 | http://www.cssdesk.com/
90 |
91 | http://10k.aneventapart.com/2/Uploads/579/
92 |
93 | http://10k.aneventapart.com/2/Uploads/504/
94 |
95 | AN EASY WAY TO CHOOSE COLORS - I.E. BURN ETC
96 | Video of desiging various interfaces = include Stylo.
97 | Tool to generate noise.
98 |
99 | https://developers.google.com/webfonts
100 | https://typekit.com/
101 |
102 | Issue with border radius is that properties are being applied in the wrong order.
103 | Should 'selected' not be a property?
104 | Only save selection in history.
105 | We should have a benchmarking testing library.
106 |
107 | --------------
108 |
109 | # Style
110 | # Decoration
111 | # Capitalize
112 | # Alignment
--------------------------------------------------------------------------------
/app.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'bundler'
3 | require 'pathname'
4 | require 'sinatra/json'
5 | require 'sinatra/reloader'
6 |
7 | Bundler.require
8 |
9 | require 'sprockets/commonjs'
10 | require 'stylus/tilt'
11 | require 'stylus/import_processor'
12 |
13 | module AssetHelpers
14 | def asset_path(source)
15 | '/assets/' + settings.sprockets.find_asset(source).digest_path
16 | end
17 | end
18 |
19 | class App < Sinatra::Base
20 | set :root, Pathname(File.expand_path('../', __FILE__))
21 | set :raise_errors, true
22 | set :show_exceptions, true
23 | set :sprockets, Sprockets::Environment.new(root)
24 | set :precompile, [ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
25 | set :protection, false
26 |
27 | configure do
28 | sprockets.append_path(root.join('assets', 'javascripts'))
29 | sprockets.append_path(root.join('assets', 'stylesheets'))
30 | sprockets.append_path(root.join('assets', 'images'))
31 |
32 | sprockets.append_path(root.join('vendor', 'assets', 'javascripts'))
33 | sprockets.append_path(root.join('vendor', 'assets', 'stylesheets'))
34 |
35 | sprockets.register_engine '.styl', Tilt::StylusTemplate
36 | sprockets.register_preprocessor 'text/css', Stylus::ImportProcessor
37 | Stylus.paths.concat sprockets.paths
38 |
39 | sprockets.context_class.instance_eval do
40 | include AssetHelpers
41 | end
42 | end
43 |
44 | helpers Sinatra::JSON
45 |
46 | helpers do
47 | include AssetHelpers
48 |
49 | def url(path)
50 | base = "#{request.scheme}://#{request.env['HTTP_HOST']}"
51 | base + path
52 | end
53 | end
54 |
55 | # use Rack::Auth::Basic, "Protected Area" do |username, password|
56 | # (username == 'dragon' && password == 'tamer')
57 | # end
58 |
59 | get '/' do
60 | send_file File.join(settings.public_folder, 'index.html')
61 | end
62 | end
--------------------------------------------------------------------------------
/assets/images/bg-grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/bg-grid.png
--------------------------------------------------------------------------------
/assets/images/blacky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/blacky.png
--------------------------------------------------------------------------------
/assets/images/crosshairs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/crosshairs.png
--------------------------------------------------------------------------------
/assets/images/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/grid.png
--------------------------------------------------------------------------------
/assets/images/multiple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/multiple.png
--------------------------------------------------------------------------------
/assets/images/whitey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/images/whitey.png
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/element/resizing.module.coffee:
--------------------------------------------------------------------------------
1 | class Thumb extends Spine.Controller
2 | className: 'thumb'
3 |
4 | events:
5 | 'mousedown': 'listen'
6 |
7 | constructor: (@type) ->
8 | super()
9 | @el.addClass(@type)
10 |
11 | listen: (e) =>
12 | e.preventDefault()
13 | e.stopPropagation()
14 |
15 | @dragPosition = {left: e.pageX, top: e.pageY}
16 | $(document).mousemove(@drag)
17 | $(document).mouseup(@drop)
18 | @el.trigger('start.resize', [@type])
19 |
20 | drag: (e) =>
21 | difference =
22 | left: e.pageX - @dragPosition.left
23 | top: e.pageY - @dragPosition.top
24 |
25 | @dragPosition = {left: e.pageX, top: e.pageY}
26 | @el.trigger('drag.resize', [@type, difference, e.shiftKey])
27 |
28 | drop: (e) =>
29 | @el.trigger('end.resize')
30 |
31 | $(document).unbind('mousemove', @drag)
32 | $(document).unbind('mouseup', @drop)
33 |
34 | class Resizing extends Spine.Controller
35 | className: 'resizing'
36 |
37 | events:
38 | 'drag.resize': 'resize'
39 |
40 | constructor: (@element) ->
41 | super(el: @element.el)
42 |
43 | render: ->
44 | @thumbs = $('
')
45 | @thumbs.append(new Thumb('tl').el)
46 | @thumbs.append(new Thumb('tt').el)
47 | @thumbs.append(new Thumb('tr').el)
48 | @thumbs.append(new Thumb('rr').el)
49 | @thumbs.append(new Thumb('br').el)
50 | @thumbs.append(new Thumb('bb').el)
51 | @thumbs.append(new Thumb('bl').el)
52 | @thumbs.append(new Thumb('ll').el)
53 | @thumbs = @thumbs.children()
54 | @append(@thumbs)
55 |
56 | remove: ->
57 | @thumbs?.remove()
58 |
59 | toggle: (bool) ->
60 | if bool then @render() else @remove()
61 |
62 | resize: (e, type, position, lockAR) ->
63 | area = @element.area()
64 |
65 | switch type
66 | when 'tl'
67 | area.width -= position.left
68 | area.height -= position.top
69 | area.top += position.top
70 | area.left += position.left
71 |
72 | when 'tt'
73 | area.height -= position.top
74 | area.top += position.top
75 |
76 | when 'tr'
77 | area.width += position.left
78 | area.height -= position.top
79 | area.top += position.top
80 |
81 | when 'rr'
82 | area.width += position.left
83 |
84 | when 'br'
85 | area.width += position.left
86 | area.height += position.top
87 |
88 | when 'bb'
89 | area.height += position.top
90 |
91 | when 'bl'
92 | area.width -= position.left
93 | area.height += position.top
94 | area.left += position.left
95 |
96 | when 'll'
97 | area.width -= position.left
98 | area.left += position.left
99 |
100 | if lockAR
101 | # TODO - FIXME, this doesn't lock AR properly
102 | area.width = Math.max(area.width, area.height)
103 | area.height = area.width
104 |
105 | # Make sure we can't have negative widths/heights
106 | area.width = Math.max(0, area.width)
107 | area.height = Math.max(0, area.height)
108 |
109 | @element.resize(area)
110 |
111 | module.exports = Resizing
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/button.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 | Color = require('app/models/properties/color')
3 | Background = require('app/models/properties/background')
4 |
5 | class Button extends Element
6 | className: 'button'
7 | id: module.id
8 |
9 | events:
10 | 'resize.element': 'syncLineHeight'
11 |
12 | defaults: ->
13 | result =
14 | width: 100
15 | height: 40
16 | textAlign: 'center'
17 | lineHeight: 40
18 | borderRadius: 5
19 | borderWidth: 1
20 | borderStyle: 'solid'
21 | borderColor: new Color(166, 166, 166)
22 | backgroundImage: [new Background.LinearGradient(
23 | new Background.Position(270),
24 | [
25 | new Background.ColorStop(new Color.White, 0),
26 | new Background.ColorStop(new Color.White, 30),
27 | new Background.ColorStop(new Color(242,242,242), 100)
28 | ]
29 | )]
30 |
31 | syncLineHeight: ->
32 | @set(lineHeight: @get('height'))
33 |
34 | module.exports = Button
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/canvas.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 |
3 | class Canvas extends Element
4 | tag: 'canvas'
5 |
6 | points: []
7 |
8 | constructor: ->
9 | super
10 | @ctx = @el[0].getContext('2d')
11 |
12 | paint: ->
13 | first = @points[0]
14 | points = @points[1...@points.length]
15 | return unless first
16 |
17 | @ctx.beginPath()
18 | @ctx.moveTo(first...)
19 |
20 | for point in @points
21 | @ctx.lineTo(point...)
22 |
23 | @ctx.fill()
24 |
25 | width: (val) ->
26 |
27 | height: (val) ->
28 |
29 | backgroundImage: (val) ->
30 |
31 | backgroundColor: (val) ->
32 |
33 | borderBottom: (val) ->
34 |
35 | boxShadow: (val) ->
36 |
37 | borderRadius: (val) ->
38 |
39 | module.exports = Canvas
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/ellipsis.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 |
3 | class Ellipsis extends Element
4 | className: 'ellipsis'
5 | id: module.id
6 |
7 | constructor: ->
8 | super
9 | @properties['borderRadius'] = '50%'
10 | @paint()
11 |
12 | # Disable setting borderRadius in
13 | # the element inspector
14 | borderRadius: -> false
15 |
16 | module.exports = Ellipsis
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/image.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 | Color = require('app/models/properties/color')
3 |
4 | class Image extends Element
5 | className: 'image'
6 | id: module.id
7 |
8 | constructor: (attrs = {}) ->
9 | super
10 | @setSrc(attrs.src)
11 |
12 | setSrc: (@src) ->
13 | @set(
14 | backgroundColor: new Color.Transparent
15 | backgroundImage: "url(#{@src})"
16 | backgroundSize: '100% 100%'
17 | backgroundRepeat: 'no-repeat'
18 | backgroundPosition: 'center center'
19 | ) if @src
20 |
21 | toValue: ->
22 | result = super
23 | result.src = @src
24 | result
25 |
26 | module.exports = Image
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/input.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 | Color = require('app/models/properties/color')
3 | Shadow = require('app/models/properties/shadow')
4 |
5 | class Text extends Element
6 | className: 'textInput'
7 | id: module.id + '.Text'
8 |
9 | defaults: ->
10 | result =
11 | width: 125
12 | height: 20
13 | padding: 3
14 | borderWidth: 1
15 | borderStyle: 'solid'
16 | borderColor: new Color(155,155,155)
17 | boxShadow: [new Shadow(
18 | inset: true, x: 0, y: 1, blur: 2,
19 | color: new Color(0, 0, 0, 0.12)
20 | )]
21 | backgroundColor: new Color.White
22 |
23 | class CheckBox extends Element
24 |
25 | module.exports =
26 | Text: Text
27 | CheckBox: CheckBox
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/line.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 |
3 | class Line extends Element
4 | className: 'line'
5 |
6 | module.exports = Line
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/rectangle.module.coffee:
--------------------------------------------------------------------------------
1 | Element = require('../element')
2 |
3 | class Rectangle extends Element
4 | className: 'rectangle'
5 | id: module.id
6 |
7 | module.exports = Rectangle
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/text.module.coffee:
--------------------------------------------------------------------------------
1 | Color = require('app/models/properties/color')
2 | Rectangle = require('./rectangle')
3 |
4 | class Text extends Rectangle
5 | className: 'text'
6 | id: module.id
7 |
8 | events:
9 | 'dblclick .thumb.br': 'fitToText'
10 |
11 | defaults: ->
12 | result =
13 | height: 30
14 | fontSize: 18
15 | backgroundColor: new Color.Transparent
16 |
17 | $.extend({}, super, result)
18 |
19 | startEditing: ->
20 | return if @editing
21 | super
22 |
23 | @autoSize()
24 |
25 | stopEditing: ->
26 | return unless @editing
27 | super
28 |
29 | if @text()
30 | @fixSize()
31 | else
32 | # Remove the element if empty
33 | @remove()
34 |
35 | autoSize: ->
36 | @el.css(width: 'auto', height: 'auto')
37 |
38 | fixSize: ->
39 | @set(
40 | width: @el.outerWidth(),
41 | height: @el.outerHeight()
42 | )
43 |
44 | module.exports = Text
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/elements/triangle.module.coffee:
--------------------------------------------------------------------------------
1 | Canvas = require('./canvas')
2 |
3 | class Triangle extends Canvas
4 | className: 'triangle'
5 |
6 | points: [1, 2, 3]
7 |
8 | module.exports = Rectangle
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/header.module.coffee:
--------------------------------------------------------------------------------
1 | Rectangle = require('./elements/rectangle')
2 | Ellipsis = require('./elements/ellipsis')
3 | Text = require('./elements/text')
4 | Input = require('./elements/input')
5 | Button = require('./elements/button')
6 |
7 | class Header extends Spine.Controller
8 | tag: 'header'
9 | className: 'header'
10 |
11 | events:
12 | 'click .rectangle': 'addRectangle'
13 | 'click .ellipsis': 'addEllipsis'
14 | 'click .text': 'addText'
15 | 'click .textInput': 'addTextInput'
16 | 'click .button': 'addButton'
17 |
18 | render: ->
19 | @html JST['app/views/header'](this)
20 | this
21 |
22 | addRectangle: ->
23 | @addElement(new Rectangle)
24 |
25 | addEllipsis: ->
26 | @addElement(new Ellipsis)
27 |
28 | addText: ->
29 | @addElement(element = new Text)
30 | element.startEditing()
31 |
32 | addTextInput: ->
33 | @addElement(new Input.Text)
34 |
35 | addButton: ->
36 | @addElement(new Button)
37 |
38 | addElement: (element) ->
39 | @stage.history.record()
40 |
41 | position = @stage.center()
42 | position.left -= element.get('width') or 50
43 | position.top -= element.get('height') or 50
44 | element.set(position)
45 |
46 | @stage.add(element)
47 | @stage.selection.clear()
48 | @stage.selection.add(element)
49 |
50 | module.exports = Header
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector.module.coffee:
--------------------------------------------------------------------------------
1 | Background = require('./inspector/background')
2 | Border = require('./inspector/border')
3 | BorderRadius = require('./inspector/border_radius')
4 | Opacity = require('./inspector/opacity')
5 | BoxShadow = require('./inspector/box_shadow')
6 | TextShadow = require('./inspector/text_shadow')
7 | Dimensions = require('./inspector/dimensions')
8 | TextPosition = require('./inspector/text_position')
9 | Font = require('./inspector/font')
10 | Utils = require('lib/utils')
11 |
12 | class TextInspector extends Spine.Controller
13 | className: 'textInspector'
14 |
15 | constructor: ->
16 | super
17 | @append(@font = new Font(stage: @stage))
18 | @append(@textPosition = new TextPosition(stage: @stage))
19 | @append(@textShadow = new TextShadow(stage: @stage))
20 |
21 | render: ->
22 | @font.render()
23 | @textPosition.render()
24 | @textShadow.render()
25 | this
26 |
27 | release: ->
28 | @font?.release()
29 | @textPosition?.release()
30 | @textShadow?.release()
31 | super
32 |
33 | class DisplayInspector extends Spine.Controller
34 | className: 'displayInspector'
35 |
36 | constructor: ->
37 | super
38 |
39 | @append(@dimensions = new Dimensions(stage: @stage))
40 | @append(@background = new Background(stage: @stage))
41 | @append(@border = new Border(stage: @stage))
42 | @append(@borderRadius = new BorderRadius(stage: @stage))
43 | @append(@boxShadow = new BoxShadow(stage: @stage))
44 | @append(@opacity = new Opacity(stage: @stage))
45 |
46 | render: ->
47 | @dimensions.render()
48 | @background.render()
49 | @border.render()
50 | @borderRadius.render()
51 | @boxShadow.render()
52 | @opacity.render()
53 | this
54 |
55 | release: ->
56 | @dimensions?.release()
57 | @background?.release()
58 | @border?.release()
59 | @borderRadius?.release()
60 | @boxShadow?.release()
61 | @opacity?.release()
62 |
63 | class Inspector extends Spine.Controller
64 | className: 'inspector'
65 |
66 | events:
67 | 'click header [data-type]': 'changeInspector'
68 |
69 | elements:
70 | 'header div': '$headers'
71 |
72 | constructor: ->
73 | super
74 |
75 | @append(JST['app/views/inspector']())
76 | @append(@textInspector = new TextInspector(stage: @stage))
77 | @append(@displayInspector = new DisplayInspector(stage: @stage))
78 |
79 | # Make sure only one inspector is active
80 | @manager = new Spine.Manager
81 | @manager.add(@textInspector, @displayInspector)
82 | @manager.bind 'change', @changeHeader
83 |
84 | # Display the display inspector by default
85 | @displayInspector.active()
86 |
87 | # We can increase performance dramatically by using
88 | # requestAnimationFrame and rendering async
89 | @stage.selection.bind 'change', @paint
90 |
91 | @render()
92 |
93 | paint: =>
94 | return if @rendering
95 | @rendering = true
96 | Utils.requestAnimationFrame(@render)
97 |
98 | render: =>
99 | # Do update in one paint by hiding
100 | # before rendering inspectors
101 | @el.hide()
102 |
103 | # Render the currently active inspector
104 | @manager.current?.render()
105 |
106 | @el.show()
107 | @rendering = false
108 | this
109 |
110 | changeInspector: (e) ->
111 | name = $(e.target).data('type')
112 |
113 | if name is 'DisplayInspector'
114 | @displayInspector.render()
115 | @displayInspector.active()
116 |
117 | else if name is 'TextInspector'
118 | @textInspector.render()
119 | @textInspector.active()
120 |
121 | changeHeader: =>
122 | name = @manager.current.constructor.name
123 | @$headers.removeClass('active')
124 | @$headers.filter("[data-type=#{name}]").addClass('active')
125 |
126 | release: ->
127 | @textInspector.release()
128 | @displayInspector.release()
129 | @stage.selection.unbind 'change', @paint
130 | super
131 |
132 | module.exports = Inspector
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/border.module.coffee:
--------------------------------------------------------------------------------
1 | Border = require('app/models/properties/border')
2 | ColorPicker = require('lib/color_picker')
3 |
4 | class BorderController extends Spine.Controller
5 | className: 'border'
6 |
7 | events:
8 | 'click [data-border]': 'borderClick'
9 | 'change': 'inputChange'
10 |
11 | elements:
12 | '.borders div': '$borders'
13 | 'select[name=style]': '$style'
14 | 'input[name=width]': '$width'
15 | 'input, select': '$inputs'
16 |
17 | current: 'border'
18 |
19 | constructor: ->
20 | super
21 |
22 | @html JST['app/views/inspector/border'](this)
23 |
24 | # Color input
25 | @$color = new ColorPicker.Preview
26 | @$color.bind 'change', => @inputChange()
27 |
28 | @$('input[type=color]').replaceWith(@$color.el)
29 |
30 | render: =>
31 | @disabled = not @stage.selection.isAny()
32 | @disabled = true if @stage.selection.get('border') is false
33 |
34 | @change(@current)
35 |
36 | @el.toggleClass('disabled', @disabled)
37 | @$inputs.attr('disabled', @disabled)
38 | this
39 |
40 | # Private
41 |
42 | change: (@current) ->
43 | return if @disabled
44 |
45 | @$borders.removeClass('active')
46 | @$borders.filter("[data-border=#{@current}]").addClass('active')
47 |
48 | @currentBorder = @stage.selection.get(@current)
49 |
50 | unless @currentBorder
51 | # Base this border on the default one.
52 | @currentBorder = @stage.selection.get('border')?.clone()
53 | @currentBorder or= new Border
54 |
55 | @$width.val(@currentBorder.width)
56 | @$style.val(@currentBorder.style)
57 | @$color.val(@currentBorder.color)
58 |
59 | # Events
60 |
61 | borderClick: (e) ->
62 | @change($(e.currentTarget).data('border'))
63 |
64 | inputChange: ->
65 | @stage.history.record('border')
66 |
67 | @currentBorder.width = parseInt(@$width.val(), 10)
68 | @currentBorder.style = @$style.val()
69 | @currentBorder.color = @$color.val()
70 | @set()
71 |
72 | set: ->
73 | @stage.history.record('border')
74 |
75 | # Border overrides everything else
76 | if @current is 'border'
77 | @stage.selection.set(
78 | borderTop: null
79 | borderRight: null
80 | borderBottom: null
81 | borderLeft: null
82 | )
83 |
84 | @stage.selection.set(@current, @currentBorder)
85 |
86 | release: ->
87 | @$color?.release()
88 | super
89 |
90 | module.exports = BorderController
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/border_radius.module.coffee:
--------------------------------------------------------------------------------
1 | class BorderRadius extends Spine.Controller
2 | className: 'borderRadius'
3 |
4 | events:
5 | 'click [data-border-radius]': 'borderClick'
6 | 'change input': 'inputChange'
7 |
8 | elements:
9 | '.borders div': '$borders'
10 | 'input': '$inputs'
11 |
12 | current: 'borderRadius'
13 |
14 | constructor: ->
15 | super
16 | @html JST['app/views/inspector/border_radius'](this)
17 |
18 | render: =>
19 | # Disable unless elements are selected or if an
20 | # element, such as an ellipsis, is selected.
21 | @disabled = not @stage.selection.isAny()
22 | @disabled = true if @stage.selection.get('borderRadius') is false
23 |
24 | @change(@current)
25 |
26 | @el.toggleClass('disabled', @disabled)
27 | @$inputs.attr('disabled', @disabled)
28 |
29 | this
30 |
31 | # Private
32 |
33 | change: (@current) ->
34 | return if @disabled
35 |
36 | @$borders.removeClass('active')
37 | @$borders.filter("[data-border-radius=#{@current}]").addClass('active')
38 |
39 | @radius = @stage.selection.get(@current)
40 | @radius or= @stage.selection.get('borderRadius')
41 | @radius or= 0
42 |
43 | @$inputs.val(@radius)
44 |
45 | # Events
46 |
47 | borderClick: (e) ->
48 | @change($(e.currentTarget).data('border-radius'))
49 |
50 | inputChange: (e) ->
51 | val = parseInt($(e.currentTarget).val(), 10)
52 | @$inputs.val(val)
53 | @set(val)
54 |
55 | set: (val) ->
56 | @stage.history.record('borderRadius')
57 |
58 | # borderRadius overrides everything else
59 | if @current is 'borderRadius'
60 | @stage.selection.set(
61 | borderTopLeftRadius: null
62 | borderTopRightRadius: null
63 | borderBottomRightRadius: null
64 | borderBottomLeftRadius: null
65 | )
66 |
67 | @stage.selection.set(@current, val)
68 |
69 | module.exports = BorderRadius
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/dimensions.module.coffee:
--------------------------------------------------------------------------------
1 | class Dimensions extends Spine.Controller
2 | className: 'dimensions'
3 |
4 | events:
5 | 'change input': 'change'
6 |
7 | elements:
8 | 'input': '$inputs'
9 | 'input[name=width]': '$width'
10 | 'input[name=height]': '$height'
11 | 'input[name=x]': '$x'
12 | 'input[name=y]': '$y'
13 |
14 | constructor: ->
15 | super
16 | $(document).bind 'resize.element move.element', @update
17 | @html JST['app/views/inspector/dimensions'](this)
18 |
19 | render: =>
20 | @disabled = not @stage.selection.isSingle()
21 |
22 | @update()
23 |
24 | @el.toggleClass('disabled', @disabled)
25 | @$inputs.attr('disabled', @disabled)
26 | this
27 |
28 | update: =>
29 | @disabled = not @stage.selection.isSingle()
30 | return if @disabled
31 |
32 | @$width.val(@stage.selection.get('width'))
33 | @$height.val(@stage.selection.get('height'))
34 | @$x.val(@stage.selection.get('left'))
35 | @$y.val(@stage.selection.get('top'))
36 |
37 | change: (e) ->
38 | @stage.history.record('dimensions')
39 | @stage.selection.set('width', parseInt(@$width.val(), 10))
40 | @stage.selection.set('height', parseInt(@$height.val(), 10))
41 | @stage.selection.set('left', parseInt(@$x.val(), 10))
42 | @stage.selection.set('top', parseInt(@$y.val(), 10))
43 |
44 | release: ->
45 | $(document).unbind 'resize.element', @update
46 | $(document).unbind 'move.element', @update
47 | super
48 |
49 | module.exports = Dimensions
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/font.module.coffee:
--------------------------------------------------------------------------------
1 | Color = require('app/models/properties/color')
2 | ColorPicker = require('lib/color_picker')
3 |
4 | class Font extends Spine.Controller
5 | className: 'font'
6 |
7 | elements:
8 | 'input': '$inputs'
9 | 'input[name=size]': '$size'
10 | 'select[name=family]': '$family'
11 |
12 | events:
13 | 'change input': 'change'
14 | 'change select': 'change'
15 |
16 | constructor: ->
17 | super
18 | @html JST['app/views/inspector/font'](this)
19 |
20 | @$color = new ColorPicker.Preview
21 | @$color.bind 'change', => @change()
22 |
23 | @$('input[type=color]').replaceWith(@$color.el)
24 |
25 | render: =>
26 | @disabled = not @stage.selection.isAny()
27 | @$color.val(@stage.selection.get('color') or new Color.Black)
28 | @$size.val(@stage.selection.get('fontSize') ? 12)
29 | @$family.val(@stage.selection.get('fontFamily'))
30 |
31 | change: (e) ->
32 | @stage.history.record('font')
33 |
34 | @stage.selection.set('color', @$color.val())
35 | @stage.selection.set('fontSize', parseInt(@$size.val(), 10))
36 | @stage.selection.set('fontFamily', @$family.val())
37 |
38 | module.exports = Font
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/opacity.module.coffee:
--------------------------------------------------------------------------------
1 | class Opacity extends Spine.Controller
2 | className: 'opacity'
3 |
4 | events:
5 | 'change input': 'change'
6 | 'focus input': 'inputFocus'
7 |
8 | elements:
9 | 'input': '$inputs'
10 |
11 | render: =>
12 | @disabled = not @stage.selection.isAny()
13 | @opacity = @stage.selection.get('opacity') ? 1
14 |
15 | @html JST['app/views/inspector/opacity'](this)
16 |
17 | change: (e) ->
18 | @stage.history.record('opacity')
19 |
20 | val = parseFloat($(e.currentTarget).val())
21 | val = Math.round(val * 100) / 100
22 |
23 | @stage.history.record('opacity')
24 | @stage.selection.set('opacity', val)
25 | @$inputs.val(val)
26 |
27 | module.exports = Opacity
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/popup_menu.module.coffee:
--------------------------------------------------------------------------------
1 | class PopupMenu extends Spine.Controller
2 | @open: (position) ->
3 | (new this).open(position)
4 |
5 | popupMenuEvents:
6 | 'mousedown': 'cancelEvent'
7 |
8 | constructor: ->
9 | super
10 | @delegateEvents(@popupMenuEvents)
11 | @el.addClass('popupMenu')
12 | @el.css(position: 'absolute')
13 |
14 | open: (position = {}) ->
15 | @el.css(position)
16 | $('body').append(@el)
17 | $('body').bind('mousedown', @close)
18 | this
19 |
20 | close: =>
21 | @release()
22 | this
23 |
24 | release: ->
25 | $('body').unbind('mousedown', @close)
26 | super
27 |
28 | cancelEvent: (e) ->
29 | e.stopPropagation()
30 |
31 | module.exports = PopupMenu
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/stage.module.coffee:
--------------------------------------------------------------------------------
1 | class StageInspector extends Spine.Controller
2 | className: 'stageInspector'
3 | # Background
4 |
5 | module.exports = StageInspector
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/text_position.module.coffee:
--------------------------------------------------------------------------------
1 | class TextPosition extends Spine.Controller
2 | className: 'textPosition'
3 |
4 | types: [
5 | 'textIndent',
6 | 'lineHeight',
7 | 'letterSpacing',
8 | 'wordSpacing'
9 | ]
10 |
11 | elements:
12 | 'input[name=textIndent]': '$textIndent'
13 | 'input[name=lineHeight]': '$lineHeight'
14 | 'input[name=letterSpacing]': '$letterSpacing'
15 | 'input[name=wordSpacing]': '$wordSpacing'
16 |
17 | events:
18 | 'change input': 'change'
19 |
20 | constructor: ->
21 | super
22 | @html JST['app/views/inspector/text_position'](this)
23 |
24 | render: ->
25 | @disabled = not @stage.selection.isAny()
26 |
27 | @current = {}
28 |
29 | for type in @types
30 | value = @stage.selection.get(type)
31 | @['$' + type].val(value)
32 |
33 | change: ->
34 | for type in @types
35 | value = parseInt(@['$' + type].val(), 10)
36 | @stage.selection.set(type, value)
37 |
38 |
39 | module.exports = TextPosition
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/text_shadow.module.coffee:
--------------------------------------------------------------------------------
1 | Shadow = require('app/models/properties/shadow')
2 | ColorPicker = require('lib/color_picker')
3 | PositionPicker = require('lib/position_picker')
4 |
5 | class TextShadow extends Spine.Controller
6 | className: 'textShadow'
7 |
8 | events:
9 | 'change input': 'change'
10 |
11 | elements:
12 | 'input[name=x]': '$x'
13 | 'input[name=y]': '$y'
14 | 'input[name=blur]': '$blur'
15 |
16 | constructor: ->
17 | super
18 |
19 | @$position = new PositionPicker
20 |
21 | @$position.bind 'change', (position) =>
22 | @shadow.x = position.left
23 | @shadow.y = position.top
24 | @set()
25 | @update()
26 |
27 | @$color = new ColorPicker.Preview
28 | @$color.bind 'change', => @change()
29 |
30 | @html JST['app/views/inspector/text_shadow'](@)
31 | @$('input[type=color]').replaceWith(@$color.el)
32 | @$('input[type=position]').replaceWith(@$position.el)
33 |
34 | render: ->
35 | @disabled = not @stage.selection.isAny()
36 |
37 | @shadow = @stage.selection.get('textShadow')
38 | @shadow or= new Shadow
39 |
40 | @update()
41 |
42 | this
43 |
44 | update: ->
45 | @$('input').attr('disabled', @disabled)
46 |
47 | @$x.val @shadow.x
48 | @$y.val @shadow.y
49 | @$blur.val @shadow.blur
50 | @$color.val @shadow.color
51 |
52 | change: ->
53 | @shadow.x = parseFloat(@$x.val())
54 | @shadow.y = parseFloat(@$y.val())
55 | @shadow.blur = parseFloat(@$blur.val())
56 | @shadow.color = @$color.val()
57 |
58 | @$position.change(
59 | left: @shadow.x,
60 | top: @shadow.y
61 | )
62 |
63 | @set()
64 | @update()
65 |
66 | set: ->
67 | # Text shadows need a blur
68 | # to be formatted correctly
69 | @shadow.blur ?= 0
70 |
71 | @stage.history.record('textShadow')
72 | @stage.selection.set('textShadow', @shadow)
73 |
74 | release: ->
75 | @$position?.release()
76 | @$color?.release()
77 | super
78 |
79 | module.exports = TextShadow
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/inspector/text_style.module.coffee:
--------------------------------------------------------------------------------
1 | class TextStyle extends Spine.Controller
2 | className: 'textStyle'
3 |
4 | # Style
5 | # Decoration
6 | # Capitalize
7 | # Alignment
8 |
9 | module.exports = TextStyle
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/clipboard.module.coffee:
--------------------------------------------------------------------------------
1 | Serialize = require('app/models/serialize')
2 |
3 | class Clipboard
4 | constructor: (@stage) ->
5 | $(window).bind 'beforecopy', @cancel
6 | $(window).bind 'copy', @copy
7 |
8 | # Remove pasting for the moment, until
9 | # it's better supported in browsers
10 | #
11 | # $(window).bind 'beforepaste', @cancel
12 | # $(window).bind 'paste', @paste
13 |
14 | cancel: (e) =>
15 | return if 'value' of e.target
16 | return if $(e.target).attr('contenteditable')
17 |
18 | # We need to cancel the default to get
19 | # the 'copy' event to trigger
20 | e.preventDefault()
21 |
22 | copy: (e) =>
23 | return unless @stage.selection.isAny()
24 |
25 | e.preventDefault()
26 | e = e.originalEvent
27 |
28 | json = JSON.stringify(@stage.selection.elements)
29 | e.clipboardData.setData('json/x-stylo', json)
30 |
31 | styles = (el.outerCSS() for el in @stage.selection.elements)
32 | e.clipboardData.setData('text/plain', styles.join("\n\n"))
33 |
34 | paste: (e) =>
35 | return if 'value' of e.target
36 |
37 | e.preventDefault()
38 | e = e.originalEvent
39 |
40 | # Some browsers restrict the clipboard data types,
41 | # so we need to revert back to text/html
42 | json = e.clipboardData.getData('json/x-stylo')
43 | return unless json
44 |
45 | elements = Serialize.fromJSON(json)
46 |
47 | @stage.history.record()
48 | @stage.add(el) for el in elements
49 | @stage.selection.refresh(elements)
50 | @stage.selection.moveBy(left: 10, top: 10)
51 |
52 | data: null
53 |
54 | copyInternal: ->
55 | @data = (el.clone() for el in @stage.selection.elements)
56 |
57 | pasteInternal: (e) ->
58 | return unless @data
59 |
60 | e?.preventDefault()
61 |
62 | @stage.history.record()
63 | @stage.add(el) for el in @data
64 | @stage.selection.refresh(@data)
65 | @stage.selection.moveBy(left: 10, top: 10)
66 |
67 | # Re-clone the elements
68 | @copyInternal()
69 |
70 | release: ->
71 | $(window).unbind 'beforecopy', @cancel
72 | $(window).unbind 'copy', @copy
73 | $(window).unbind 'beforepaste', @cancel
74 | $(window).unbind 'paste', @paste
75 |
76 | module.exports = Clipboard
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/context_menu.module.coffee:
--------------------------------------------------------------------------------
1 | class Menu extends Spine.Controller
2 | className: 'contextMenu'
3 |
4 | events:
5 | 'mousedown': 'cancelEvent'
6 | 'click [data-type]': 'click'
7 |
8 | constructor: (@stage, position) ->
9 | super()
10 |
11 | @el.css(position)
12 | @html(JST['app/views/context_menu'](this))
13 |
14 | @selectDisabled = not @stage.selection.isAny()
15 | @$('[data-require=select]').toggleClass('disabled', @selectDisabled)
16 |
17 | click: (e) ->
18 | e.preventDefault()
19 | @release()
20 |
21 | item = $(e.currentTarget)
22 | type = item.data('type')
23 |
24 | unless item.hasClass('disabled')
25 | @[type]()
26 |
27 | cancelEvent: (e) ->
28 | # Stop the menu closing immediately
29 | e.preventDefault()
30 | e.stopPropagation()
31 |
32 | # Types
33 |
34 | copy: ->
35 | @stage.clipboard.copyInternal()
36 |
37 | paste: ->
38 | @stage.clipboard.pasteInternal()
39 |
40 | bringForward: ->
41 | @stage.history.record()
42 | @stage.bringForward()
43 |
44 | bringBack: ->
45 | @stage.history.record()
46 | @stage.bringBack()
47 |
48 | bringToFront: ->
49 | @stage.history.record()
50 | @stage.bringToFront()
51 |
52 | bringToBack: ->
53 | @stage.history.record()
54 | @stage.bringToBack()
55 |
56 | class ContextMenu extends Spine.Controller
57 | events:
58 | 'contextmenu': 'open'
59 |
60 | constructor: (@stage) ->
61 | super(el: @stage.el)
62 | $('body').bind('mousedown', @close)
63 |
64 | open: (e) ->
65 | return if e.metaKey
66 |
67 | e.preventDefault()
68 | @close()
69 |
70 | position =
71 | left: e.pageX + 1
72 | top: e.pageY + 1
73 |
74 | @menu = new Menu(@stage, position)
75 | $('body').append(@menu.el)
76 |
77 | close: =>
78 | @menu?.release()
79 | @menu = null
80 |
81 | release: ->
82 | $('body').unbind('mousedown', @close)
83 |
84 | module.exports = ContextMenu
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/dragging.module.coffee:
--------------------------------------------------------------------------------
1 | class CoordTitle extends Spine.Controller
2 | className: 'coordTitle'
3 |
4 | change: (area) ->
5 | @html("x: #{area.left}px y: #{area.top}px")
6 |
7 | move: (position) ->
8 | @el.css(left: position.left, top: position.top)
9 |
10 | class Dragging extends Spine.Controller
11 | events:
12 | 'mousedown .selected': 'listen'
13 |
14 | constructor: (@stage) ->
15 | super(el: @stage.el)
16 |
17 | listen: (e) =>
18 | e.preventDefault()
19 |
20 | # Copy elements when alt dragging
21 | if e.altKey
22 | clones = @stage.cloneSelected()
23 | @stage.selection.refresh(clones)
24 |
25 | @stage.history.record()
26 |
27 | @dragPosition = {left: e.pageX, top: e.pageY}
28 | @active = false
29 |
30 | $(document).mousemove(@drag)
31 | $(document).mouseup(@drop)
32 |
33 | drag: (e) =>
34 | if @active is false
35 | @trigger('start.dragging')
36 |
37 | @active = true
38 |
39 | difference =
40 | left: e.pageX - @dragPosition.left
41 | top: e.pageY - @dragPosition.top
42 |
43 | @dragPosition = {left: e.pageX, top: e.pageY}
44 | @stageArea = @stage.area()
45 | @selectionArea = @stage.selection.area()
46 |
47 | if e.altKey or e.metaKey
48 | @stage.snapping.release()
49 | else
50 | # Check vertical/center stage snapping
51 | difference = @stage.snapping.snap(@selectionArea, difference)
52 |
53 | # Setup CoordTitle
54 | @moveCoordTitle(e)
55 |
56 | @stage.selection.moveBy(difference)
57 | @el.trigger('move.dragging')
58 |
59 | drop: (e) =>
60 | $(document).unbind('mousemove', @drag)
61 | $(document).unbind('mouseup', @drop)
62 | @el.trigger('end.dragging') if @active
63 |
64 | # Reset coordTitle
65 | @coordTitle?.release()
66 | @coordTitle = null
67 |
68 | moveCoordTitle: ->
69 | unless @coordTitle
70 | @append(@coordTitle = new CoordTitle)
71 |
72 | @coordTitle.move(
73 | left: @dragPosition.left - @stageArea.left + 10,
74 | top: @dragPosition.top - @stageArea.top + 10
75 | )
76 |
77 | @coordTitle.change(@selectionArea)
78 |
79 | module.exports = Dragging
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/drop_area.module.coffee:
--------------------------------------------------------------------------------
1 | module.exports = DropArea
2 |
3 | Image = require('app/controllers/elements/image')
4 |
5 | class DropArea extends Spine.Controller
6 | events:
7 | 'dragover': 'cancel'
8 | 'drop': 'drop'
9 |
10 | constructor: (@stage) ->
11 | super(el: @stage.el)
12 | $('body').bind('dragover', @cancel)
13 |
14 | drop: (e) ->
15 | e.preventDefault()
16 | e = e.originalEvent
17 |
18 | for file in e.dataTransfer.files
19 | reader = new FileReader
20 | reader.onload = (e) =>
21 | @addImage(e.target.result)
22 | reader.readAsDataURL(file)
23 |
24 | addImage: (src) ->
25 | @stage.history.record()
26 |
27 | element = new Image(src: src)
28 |
29 | @stage.add(element)
30 | @stage.selection.clear()
31 | @stage.selection.add(element)
32 |
33 | cancel: (e) ->
34 | e.stopPropagation()
35 | e.preventDefault()
36 |
37 | release: ->
38 | $('body').unbind('dragover', @cancel)
39 |
40 | module.exports = DropArea
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/history.module.coffee:
--------------------------------------------------------------------------------
1 | Model = require('app/models/history')
2 | Serialize = require('app/models/serialize')
3 |
4 | class History extends Spine.Controller
5 | constructor: (@stage) ->
6 | super(el: @stage.el)
7 |
8 | undo: ->
9 | Model.undo()
10 |
11 | redo: ->
12 | Model.redo()
13 |
14 | record: (type) ->
15 | @recordState() unless @throttle(type)
16 |
17 | # Private
18 |
19 | recordState: (isUndo) ->
20 | elements = JSON.stringify(@stage.elements)
21 |
22 | action = (isUndo) =>
23 | # Record the opposite action (i.e. redo)
24 | @recordState( !isUndo )
25 |
26 | # Replace stage with previous state
27 | elements = Serialize.fromJSON(elements)
28 |
29 | # TODO: This is too simple, and should
30 | # be replaced with something that's more
31 | # performant.
32 | @stage.refresh(elements)
33 |
34 | Model.add(action, isUndo)
35 |
36 | # Throttling
37 |
38 | throttleLimit: 500
39 |
40 | throttle: (type) ->
41 | throttled = false
42 | current = new Date
43 |
44 | if type and @throttleType is type and
45 | (current - @throttleDate) <= @throttleLimit
46 | throttled = true
47 |
48 | @throttleType = type
49 | @throttleDate = current
50 |
51 | throttled
52 |
53 | release: ->
54 | Model.clear()
55 |
56 | module.exports = History
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/key_bindings.module.coffee:
--------------------------------------------------------------------------------
1 | class KeyBindings extends Spine.Module
2 | @include Spine.Log
3 |
4 | mapping:
5 | 8: 'backspace'
6 | 37: 'leftArrow'
7 | 38: 'upArrow'
8 | 39: 'rightArrow'
9 | 40: 'downArrow'
10 | 46: 'backspace'
11 | 65: 'aKey'
12 | 67: 'cKey'
13 | 68: 'dKey'
14 | 79: 'oKey'
15 | 83: 'sKey'
16 | 86: 'vKey'
17 | 90: 'zKey'
18 | 187: 'plusKey'
19 | 189: 'minusKey'
20 |
21 | constructor: (@stage) ->
22 | $(document).bind('keydown', @keypress)
23 |
24 | keypress: (e) =>
25 | # Disable keyboard shortcuts in inputs
26 | return if 'value' of e.target
27 | return if $(e.target).attr('contenteditable')
28 | @[@mapping[e.which]]?(e)
29 |
30 | backspace: (e) ->
31 | e.preventDefault()
32 | @stage.history.record()
33 | @stage.removeSelected()
34 |
35 | leftArrow: (e) ->
36 | e.preventDefault()
37 | amount = -1
38 | amount *= 10 if e.shiftKey
39 | @stage.history.record('leftArrow')
40 | @stage.selection.moveBy(left: amount, top: 0)
41 |
42 | upArrow: (e) ->
43 | e.preventDefault()
44 | amount = -1
45 | amount *= 10 if e.shiftKey
46 | @stage.history.record('upArrow')
47 | @stage.selection.moveBy(left: 0, top: amount)
48 |
49 | rightArrow: (e) ->
50 | e.preventDefault()
51 | amount = 1
52 | amount *= 10 if e.shiftKey
53 | @stage.history.record('rightArrow')
54 | @stage.selection.moveBy(left: amount, top: 0)
55 |
56 | downArrow: (e) ->
57 | e.preventDefault()
58 | amount = 1
59 | amount *= 10 if e.shiftKey
60 | @stage.history.record('downArrow')
61 | @stage.selection.moveBy(left: 0, top: amount)
62 |
63 | aKey: (e) ->
64 | return unless e.metaKey
65 | e.preventDefault()
66 | @stage.selectAll()
67 |
68 | dKey: (e) ->
69 | return unless e.metaKey
70 | e.preventDefault()
71 | @stage.selection.clear() if e.metaKey
72 |
73 | oKey: (e) ->
74 | return unless e.metaKey
75 | e.preventDefault()
76 | @stage.load()
77 |
78 | sKey: (e) ->
79 | return unless e.metaKey
80 | e.preventDefault()
81 | @stage.save()
82 |
83 | plusKey: (e) ->
84 | return unless e.metaKey
85 | e.preventDefault()
86 | @log('zoomIn')
87 |
88 | minusKey: (e) ->
89 | return unless e.metaKey
90 | e.preventDefault()
91 | @log('zoomOut')
92 |
93 | cKey: (e) ->
94 | return unless e.metaKey
95 | @stage.clipboard.copyInternal(e)
96 |
97 | vKey: (e) ->
98 | return unless e.metaKey
99 | @stage.clipboard.pasteInternal(e)
100 |
101 | zKey: (e) ->
102 | return unless e.metaKey
103 | e.preventDefault()
104 |
105 | if e.shiftKey
106 | @stage.history.redo()
107 | else
108 | @stage.history.undo()
109 |
110 | release: ->
111 | $(document).unbind('keydown', @keypress)
112 |
113 | module.exports = KeyBindings
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/resizing.module.coffee:
--------------------------------------------------------------------------------
1 | class AreaTitle extends Spine.Controller
2 | className: 'areaTitle'
3 |
4 | change: (area) ->
5 | @html("width: #{area.width}px height: #{area.height}px")
6 |
7 | move: (position) ->
8 | @el.css(left: position.left, top: position.top)
9 |
10 | class Resizing extends Spine.Controller
11 | events:
12 | 'start.resize': 'resizeStart'
13 | 'resize.element': 'resized'
14 | 'end.resize': 'resizeEnd'
15 |
16 | constructor: (@stage) ->
17 | super(el: @stage.el)
18 |
19 | resizeStart: ->
20 | @stage.history.record()
21 |
22 | resized: (e, element) ->
23 | area = element.area()
24 |
25 | unless @areaTitle
26 | @append(@areaTitle = new AreaTitle)
27 |
28 | @areaTitle.move(
29 | left: area.left + area.width + 10,
30 | top: area.top + area.height + 10
31 | )
32 |
33 | @areaTitle.change(area)
34 |
35 | resizeEnd: ->
36 | @areaTitle?.release()
37 | @areaTitle = null
38 |
39 | module.exports = Resizing
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/select_area.module.coffee:
--------------------------------------------------------------------------------
1 | class Area extends Spine.Controller
2 | className: 'selectArea'
3 |
4 | constructor: (@left, @top) ->
5 | super()
6 | @el.css(left: @left, top: @top)
7 |
8 | area: ->
9 | area = @el.position()
10 | area.height = @el.height()
11 | area.width = @el.width()
12 | area
13 |
14 | resize: (left, top) ->
15 | dimensions =
16 | width: left - @left
17 | height: top - @top
18 |
19 | # Support negative areas
20 | if dimensions.width < 0
21 | dimensions.left = @left + dimensions.width
22 | dimensions.width *= -1
23 |
24 | if dimensions.height < 0
25 | dimensions.top = @top + dimensions.height
26 | dimensions.height *= -1
27 |
28 | @el.css(dimensions)
29 |
30 | class SelectArea extends Spine.Controller
31 | events:
32 | 'mousedown': 'listen'
33 |
34 | constructor: (@stage) ->
35 | super(el: @stage.el)
36 |
37 | listen: (e) =>
38 | # Only listen to mousedown's on the stage
39 | return if e.target isnt e.currentTarget
40 |
41 | @offset = @el.offset()
42 | @offset.left -= @el.scrollLeft()
43 | @offset.top -= @el.scrollTop()
44 |
45 | @selectArea?.release()
46 |
47 | $(document).mousemove(@drag)
48 | $(document).mouseup(@drop)
49 |
50 | drag: (e) =>
51 | # We offset by 1, so it doesn't
52 | # mess up click events
53 | unless @selectArea
54 | @selectArea = new Area(
55 | e.pageX - @offset.left + 1,
56 | e.pageY - @offset.top + 1
57 | )
58 |
59 | @append(@selectArea)
60 |
61 | @selectArea.resize(
62 | e.pageX - @offset.left,
63 | e.pageY - @offset.top
64 | )
65 |
66 | area = @selectArea.area()
67 |
68 | for element in @stage.elements
69 | if element.inArea(area)
70 | @stage.selection.add(element)
71 | else
72 | @stage.selection.remove(element)
73 |
74 | drop: (e) =>
75 | @selectArea?.release()
76 | @selectArea = null
77 | $(document).unbind('mousemove', @drag)
78 | $(document).unbind('mouseup', @drop)
79 |
80 | module.exports = SelectArea
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/selection.module.coffee:
--------------------------------------------------------------------------------
1 | min = (a = 0, b = 0) ->
2 | return b if a is 0
3 | Math.min(a, b)
4 |
5 | max = (a = 0, b = 0) ->
6 | return b if a is 0
7 | Math.max(a, b)
8 |
9 | class Selection extends Spine.Module
10 | @include Spine.Events
11 |
12 | constructor: (@elements = []) ->
13 |
14 | # Returns a property for an element selection.
15 | # Returns null unless all of the elements have the same value.
16 | get: (key) ->
17 | return null unless @isAny()
18 |
19 | first = @elements[0]?.get(key)
20 | for el in @elements
21 | if el.get(key) isnt first
22 | return null
23 | first
24 |
25 | # Sets a property on all elements
26 | set: (key, value) ->
27 | el.set(key, value) for el in @elements
28 |
29 | isMultiple: ->
30 | @elements.length > 1
31 |
32 | isSingle: ->
33 | @elements.length is 1
34 |
35 | isAny: ->
36 | @elements.length > 0
37 |
38 | # Select an element
39 | add: (element) ->
40 | return if element in @elements
41 |
42 | @elements.push(element)
43 | element.setSelected(true)
44 |
45 | @trigger('change')
46 |
47 | # Remove a selected element
48 | remove: (element) ->
49 | return if element not in @elements
50 | element.setSelected(false)
51 |
52 | index = @elements.indexOf(element)
53 | elements = @elements.slice()
54 | elements.splice(index, 1)
55 | @elements = elements
56 |
57 | @trigger('change')
58 |
59 | refresh: (elements) ->
60 | @clear()
61 | @add(el) for el in elements
62 |
63 | clear: ->
64 | @remove(el) for el in @elements
65 |
66 | # Find total area of selected elements
67 | area: ->
68 |
69 | # Quick return for single elements
70 | if @elements.length is 1
71 | return @elements[0].area()
72 |
73 | area = {}
74 |
75 | # Loop through the elements, find the upper and
76 | # lower most ones to calculate the total area
77 | for element in @elements
78 | elementArea = element.area()
79 | area.left = min(area.left, elementArea.left)
80 | area.top = min(area.top, elementArea.top)
81 | area.right = max(area.right, elementArea.left + elementArea.width)
82 | area.bottom = max(area.bottom, elementArea.top + elementArea.height)
83 |
84 | area.width = area.right - area.left
85 | area.height = area.bottom - area.top
86 |
87 | delete area.right
88 | delete area.bottom
89 |
90 | area
91 |
92 | moveBy: (toPosition) ->
93 | # Shortcut to reduce calls on set() and paint()
94 | el.moveBy(toPosition) for el in @elements
95 |
96 | release: ->
97 | @elements = []
98 |
99 | module.exports = Selection
--------------------------------------------------------------------------------
/assets/javascripts/app/controllers/stage/zindex.module.coffee:
--------------------------------------------------------------------------------
1 | Collection = require('lib/collection')
2 |
3 | class ZIndex
4 | constructor: (@stage) ->
5 | @order = @stage.elements
6 |
7 | bringForward: (element) ->
8 | index = @order.indexOf(element)
9 |
10 | if index isnt -1 or index isnt (@order.length - 1)
11 | # Swap item forwards
12 | @order[index] = @order[index + 1]
13 | @order[index + 1] = element
14 |
15 | @set()
16 |
17 | bringBack: (element) ->
18 | index = @order.indexOf(element)
19 |
20 | if index isnt -1 or index isnt 0
21 | # Swap item backwards
22 | @order[index] = @order[index - 1]
23 | @order[index - 1] = element
24 |
25 | @set()
26 |
27 | bringToFront: (element) ->
28 | # Remove element
29 | index = @order.indexOf(element)
30 | @order.splice(index, 1)
31 |
32 | # Add it to the end
33 | @order.push(element)
34 |
35 | @set()
36 |
37 | bringToBack: (element) ->
38 | # Remove element
39 | index = @order.indexOf(element)
40 | @order.splice(index, 1)
41 |
42 | # Add it to the start
43 | @order.unshift(element)
44 |
45 | @set()
46 |
47 | set: ->
48 | for element, index in @order
49 | element.order(index)
50 |
51 | module.exports = ZIndex
--------------------------------------------------------------------------------
/assets/javascripts/app/index.module.coffee:
--------------------------------------------------------------------------------
1 | Stage = require('./controllers/stage')
2 | Header = require('./controllers/header')
3 | Inspector = require('./controllers/inspector')
4 |
5 | class App extends Spine.Controller
6 | className: 'app'
7 |
8 | constructor: ->
9 | super
10 | @stage = new Stage
11 | @header = new Header(stage: @stage)
12 | @inspector = new Inspector(stage: @stage)
13 |
14 | @append(
15 | @header.render(),
16 | @stage.render(),
17 | @inspector.render()
18 | )
19 |
20 | module.exports = App
--------------------------------------------------------------------------------
/assets/javascripts/app/models/history.module.coffee:
--------------------------------------------------------------------------------
1 | class History
2 | @undoStack: []
3 | @redoStack: []
4 | @max: 50
5 |
6 | @add: (action, isUndo) ->
7 | if isUndo is true
8 | # Push onto the undo
9 | # stack from a redo
10 | stack = @undoStack
11 |
12 | else if isUndo is false
13 | # Push onto the redo
14 | # stack from a undo
15 | stack = @redoStack
16 |
17 | else
18 | # By default, push onto
19 | # an undo stack
20 | stack = @undoStack
21 | stack.shift() if stack.length >= @max
22 | @redoStack = []
23 |
24 | stack.push(action)
25 |
26 | @undo: ->
27 | action = @undoStack.pop()
28 | if action
29 | action.call(this, true)
30 | else false
31 |
32 | @redo: ->
33 | action = @redoStack.pop()
34 | if action
35 | action.call(this, false)
36 | else false
37 |
38 | @clear: ->
39 | @undoStack = []
40 | @redoStack = []
41 |
42 | module.exports = History
--------------------------------------------------------------------------------
/assets/javascripts/app/models/properties.module.coffee:
--------------------------------------------------------------------------------
1 | Property = require('./property')
2 | Values = Property.Values
3 | Background = require('./properties/background')
4 |
5 | module.exports =
6 | Property: Property
7 | Values: Values
8 | Background: Background
--------------------------------------------------------------------------------
/assets/javascripts/app/models/properties/background.module.coffee:
--------------------------------------------------------------------------------
1 | Property = require('app/models/property')
2 | Color = require('./color')
3 |
4 | class Position extends Property
5 | id: "#{module.id}.Position"
6 |
7 | constructor: (@angle = 0) ->
8 |
9 | toString: ->
10 | "#{@angle}deg"
11 |
12 | toValue: -> @angle
13 |
14 | class ColorStop extends Property
15 | id: "#{module.id}.ColorStop"
16 |
17 | constructor: (@color, @length) ->
18 | @color or= new Color.Black
19 |
20 | toString: ->
21 | if @length
22 | "#{@color} #{@length}%"
23 | else
24 | "#{@color}"
25 |
26 | toValue: ->
27 | [@color, @length]
28 |
29 | class BackgroundImage extends Property
30 | id: "#{module.id}.BackgroundImage"
31 |
32 | class LinearGradient extends BackgroundImage
33 | id: "#{module.id}.LinearGradient"
34 |
35 | constructor: (@position = new Position, @stops = []) ->
36 |
37 | toString: ->
38 | stops = @stops.sort((a, b) -> a.length - b.length)
39 | "-webkit-linear-gradient(#{[@position, stops...].join(', ')})"
40 |
41 | toDisplayString: ->
42 | stops = @stops.sort((a, b) -> a.length - b.length)
43 | "linear-gradient(#{[@position, stops...].join(', ')})"
44 |
45 | toValue: ->
46 | [@position, @stops]
47 |
48 | addStop: (stop) ->
49 | @stops.push(stop)
50 |
51 | removeStop: (stop) ->
52 | index = @stops.indexOf(stop)
53 | @stops.splice(index, 1)
54 |
55 | class URL extends BackgroundImage
56 | id: "#{module.id}.URL"
57 |
58 | constructor: (@url) ->
59 |
60 | toString: ->
61 | "url('#{@url or ''}')"
62 |
63 | toValue: -> @url
64 |
65 | class Background
66 | id: module.id
67 |
68 | constructor: (@color, @images = []) ->
69 |
70 | toString: ->
71 | "#{@color} #{@images}"
72 |
73 | toValue: ->
74 | [@color, @images]
75 |
76 | module.exports = Background
77 | module.exports.BackgroundImage = BackgroundImage
78 | module.exports.LinearGradient = LinearGradient
79 | module.exports.URL = URL
80 | module.exports.Position = Position
81 | module.exports.ColorStop = ColorStop
82 | module.exports.Color = Color
--------------------------------------------------------------------------------
/assets/javascripts/app/models/properties/border.module.coffee:
--------------------------------------------------------------------------------
1 | Property = require('app/models/property')
2 | Color = require('./color')
3 |
4 | class Border extends Property
5 | id: module.id
6 |
7 | constructor: (@width = 0, @style = 'solid', @color = new Color.Black) ->
8 |
9 | toString: ->
10 | [@width + 'px', @style, @color].join(' ')
11 |
12 | toValue: ->
13 | [@width, @style, @color]
14 |
15 | module.exports = Border
--------------------------------------------------------------------------------
/assets/javascripts/app/models/properties/color.module.coffee:
--------------------------------------------------------------------------------
1 | Property = require('app/models/property')
2 |
3 | class Color extends Property
4 | @regex: /(?:#([0-9a-f]{3,6})|rgba?\(([^)]+)\))/i
5 |
6 | @fromHex: (hex) ->
7 | if hex[0] is '#'
8 | hex = hex.substring(1, 7)
9 |
10 | if hex.length is 3
11 | hex = hex.charAt(0) + hex.charAt(0) +
12 | hex.charAt(1) + hex.charAt(1) +
13 | hex.charAt(2) + hex.charAt(2)
14 |
15 | r = parseInt(hex.substring(0,2), 16)
16 | g = parseInt(hex.substring(2,4), 16)
17 | b = parseInt(hex.substring(4,6), 16)
18 |
19 | new this(r, g, b)
20 |
21 | @fromString: (str) ->
22 | match = str.match(@regex)
23 | return null unless match
24 |
25 | if hex = match[1]
26 | @fromHex(hex)
27 |
28 | else if rgba = match[2]
29 | [r, g, b, a] = rgba.split(/\s*,\s*/)
30 | new this(r, g, b, a)
31 |
32 | @White: (alpha) ->
33 | new Color(255, 255, 255, alpha)
34 |
35 | @Black: (alpha) ->
36 | new Color(0, 0, 0, alpha)
37 |
38 | @Transparent: ->
39 | new Color
40 |
41 | constructor: (r, g, b, a = 1) ->
42 | @r = parseInt(r, 10) if r?
43 | @g = parseInt(g, 10) if g?
44 | @b = parseInt(b, 10) if b?
45 | @a = parseFloat(a)
46 |
47 | toHex: ->
48 | unless @r? and @g? and @b?
49 | return 'transparent'
50 |
51 | a = (@b | @g << 8 | @r << 16).toString(16)
52 | a = '#' + '000000'.substr(0, 6 - a.length) + a
53 | a.toUpperCase()
54 |
55 | isTransparent: ->
56 | not @a
57 |
58 | set: (values) ->
59 | @[key] = value for key, value of values
60 | this
61 |
62 | rgb: ->
63 | result =
64 | r: @r
65 | g: @g
66 | b: @b
67 |
68 | rgba: ->
69 | result =
70 | r: @r
71 | g: @g
72 | b: @b
73 | a: @a
74 |
75 | clone: ->
76 | new @constructor(@r, @g, @b, @a)
77 |
78 | toString: ->
79 | if @r? and @g? and @b?
80 | if @a? and @a isnt 1
81 | "rgba(#{@r}, #{@g}, #{@b}, #{@a})"
82 | else
83 | @toHex()
84 | else
85 | 'transparent'
86 |
87 | id: module.id
88 |
89 | toValue: ->
90 | [@r, @g, @b, @a]
91 |
92 | module.exports = Color
--------------------------------------------------------------------------------
/assets/javascripts/app/models/properties/shadow.module.coffee:
--------------------------------------------------------------------------------
1 | Property = require('app/models/property')
2 | Color = require('./color')
3 |
4 | class Shadow extends Property
5 | id: module.id
6 |
7 | constructor: (properties = {}) ->
8 | @[k] = v for k, v of properties
9 |
10 | @x or= 0
11 | @y or= 0
12 | @color or= new Color.Black(0.3)
13 |
14 | toString: ->
15 | result = []
16 | result.push('inset') if @inset
17 | result.push(@x + 'px')
18 | result.push(@y + 'px')
19 | result.push(@blur + 'px') if @blur?
20 | result.push(@spread + 'px') if @spread?
21 | result.push(@color.toString())
22 | result.join(' ')
23 |
24 | toValue: ->
25 | value =
26 | inset: @inset
27 | x: @x
28 | y: @y
29 | blur: @blur
30 | spread: @spread
31 | color: @color
32 |
33 | module.exports = Shadow
--------------------------------------------------------------------------------
/assets/javascripts/app/models/property.module.coffee:
--------------------------------------------------------------------------------
1 | Collection = require('lib/collection')
2 | Serialize = require('./serialize').Serialize
3 |
4 | class Values extends Collection
5 | toString: ->
6 | @join(', ')
7 |
8 | class Property extends Spine.Module
9 | @include Serialize
10 |
11 | # We override valueOf to properties
12 | # can be easily compared with each other.
13 | valueOf: -> @toString()
14 |
15 | module.exports = Property
16 | module.exports.Values = Values
--------------------------------------------------------------------------------
/assets/javascripts/app/models/serialize.module.coffee:
--------------------------------------------------------------------------------
1 | # This class serializes objects into a JSON string,
2 | # and then can de-serialize JSON strings into instances.
3 | #
4 | # Serialized objects are in the following format:
5 | # {id: 'path/to/module.Exports', value: objectValue}
6 | #
7 | # You should override the object's value:
8 | # toValue: -> @properties
9 | #
10 | # The objects value will be passed to it's constructor
11 | # function upon instantiation.
12 |
13 | fromObject = (object) ->
14 | return object unless object?
15 |
16 | # If type is native, return
17 | unless typeof object is 'object'
18 | return object
19 |
20 | # If type is array, call recursively
21 | if Array.isArray(object)
22 | return (fromObject(o) for o in object)
23 |
24 | # If type is object, and we're not dealing
25 | # with a instance, resolve recursively
26 | unless object.id
27 | for k, v of object
28 | object[k] = fromObject(v)
29 | return object
30 |
31 | # Otherwise, we're dealing with an instance.
32 | # Find the instance by ID, and then instantiate
33 | # it, passing object.value
34 |
35 | [path, name] = object.id.split('.', 2)
36 | constructor = require(path)
37 | constructor = constructor[name] if name
38 |
39 | # Constructor supports a custom fromValue function
40 | if result = constructor.fromValue?(object)
41 | return result
42 |
43 | # Recursively find objects
44 | args = fromObject(object.value)
45 |
46 | if Array.isArray(args)
47 |
48 | # Constructor functions can't
49 | # use apply() or call(), so we have
50 | # to manually pass arguments.
51 | new constructor(
52 | args[0], args[1], args[2],
53 | args[3], args[4], args[5]
54 | )
55 |
56 | else
57 | new constructor(args)
58 |
59 | fromJSON = (object) ->
60 | if typeof object is 'string'
61 | object = JSON.parse(object)
62 | fromObject(object)
63 |
64 | Serialize =
65 | # ID Needs to be overridden in each
66 | # class Serialize is included in
67 | id: ->
68 | module.id
69 |
70 | toJSON: ->
71 | result =
72 | id: @id?() or @id
73 | value: @toValue?() or @toValue
74 |
75 | clone: ->
76 | fromJSON(JSON.stringify(this))
77 |
78 | module.exports.Serialize = Serialize
79 | module.exports.fromJSON = fromJSON
--------------------------------------------------------------------------------
/assets/javascripts/app/parsers/import.module.coffee:
--------------------------------------------------------------------------------
1 | importPage = (tree) ->
2 | # Iterate recursively over nodes.
3 | # Parse out some css properties, keep unknown ones
4 | # Set position to absolute
5 | # Return array of elements
6 |
7 | module.exports = importPage
--------------------------------------------------------------------------------
/assets/javascripts/app/views/context_menu.jst.eco:
--------------------------------------------------------------------------------
1 | Copy
2 | Paste
3 | Bring Forward
4 | Bring to Front
5 | Send Backward
6 | Send To Back
--------------------------------------------------------------------------------
/assets/javascripts/app/views/header.jst.eco:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector.jst.eco:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/background.jst.eco:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/background/linear_gradient.jst.eco:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/assets/javascripts/app/views/inspector/background/linear_gradient.jst.eco
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/background/list.jst.eco:
--------------------------------------------------------------------------------
1 |
2 | <% for background in @backgrounds: %>
3 |
4 |
5 |
<%= background.toDisplayString?() or background.toString() %>
6 |
7 | <% end %>
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/background/menu.jst.eco:
--------------------------------------------------------------------------------
1 | Add Background Color
2 | Add Linear Gradient
3 |
4 | Add Image URL
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/background/url.jst.eco:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/border.jst.eco:
--------------------------------------------------------------------------------
1 | Border
2 |
3 |
4 |
11 |
12 |
27 |
28 |
29 |
33 |
34 |
38 |
39 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/border_radius.jst.eco:
--------------------------------------------------------------------------------
1 | Border Radius
2 |
3 |
4 |
11 |
12 |
22 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/box_shadow.jst.eco:
--------------------------------------------------------------------------------
1 |
2 |
29 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/box_shadow/list.jst.eco:
--------------------------------------------------------------------------------
1 |
2 | <% for shadow in @shadows: %>
3 |
4 |
5 |
<%= shadow.toString() %>
6 |
7 | <% end %>
8 |
9 |
10 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/box_shadow/menu.jst.eco:
--------------------------------------------------------------------------------
1 | Add Drop Shadow
2 | Add Inner Shadow
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/dimensions.jst.eco:
--------------------------------------------------------------------------------
1 | Dimensions
2 |
3 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
17 |
21 |
22 |
26 |
27 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/font.jst.eco:
--------------------------------------------------------------------------------
1 | Font
2 |
3 |
4 |
8 |
9 |
13 |
14 |
24 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/opacity.jst.eco:
--------------------------------------------------------------------------------
1 | Opacity
2 |
3 |
4 |
18 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/text_position.jst.eco:
--------------------------------------------------------------------------------
1 | Text Position
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
--------------------------------------------------------------------------------
/assets/javascripts/app/views/inspector/text_shadow.jst.eco:
--------------------------------------------------------------------------------
1 | Text Shadow
2 |
3 |
4 |
31 |
--------------------------------------------------------------------------------
/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | //= require sprockets/commonjs
2 | //= require jquery
3 | //= require spine
4 | //= require spine/manager
5 | //= require spine/route
6 | //= require spine/relation
7 | //= require gfx
8 | //= require gfx/effects
9 |
10 | //= require_tree ./lib
11 | //= require_tree ./app
--------------------------------------------------------------------------------
/assets/javascripts/lib/collection.module.coffee:
--------------------------------------------------------------------------------
1 | class Collection extends Array
2 | # Include events
3 | for k,v of Spine.Events
4 | @::[k] = v
5 |
6 | constructor: (value) ->
7 | if Array.isArray(value)
8 | super()
9 | @push(value...)
10 | else
11 | super
12 |
13 | refresh: (records) ->
14 | @splice(0, @length)
15 | @push(r) for r in records
16 | @trigger 'refresh'
17 | @change()
18 |
19 | push: ->
20 | res = super
21 | @trigger 'append'
22 | @change()
23 | res
24 |
25 | remove: (record) ->
26 | index = @indexOf(record)
27 | @splice(index, 1)
28 | @trigger 'remove'
29 | @change()
30 |
31 | change: (func) ->
32 | if typeof func is 'function'
33 | @bind 'change', arguments...
34 | else
35 | @trigger 'change', arguments...
36 | this
37 |
38 | include: (value) ->
39 | @indexOf(value) isnt -1
40 |
41 | first: ->
42 | @[0]
43 |
44 | last: ->
45 | @[@length - 1]
46 |
47 | valueOf: ->
48 | @slice(0)
49 |
50 | module.exports = Collection
--------------------------------------------------------------------------------
/assets/javascripts/lib/ext/jquery.coffee:
--------------------------------------------------------------------------------
1 | # We want 'px' to be added to lineHeight,
2 | # for example in properties/button.module
3 | delete jQuery.cssNumber.lineHeight
--------------------------------------------------------------------------------
/assets/javascripts/lib/gradient_picker.module.coffee:
--------------------------------------------------------------------------------
1 | Popup = require('./popup')
2 | ColorPicker = require('./color_picker')
3 | Color = ColorPicker.Color
4 |
5 | # TODO - abstract from properties, remove assets/grid.png dependency
6 | Background = require('app/models/properties/background')
7 | LinearGradient = Background.LinearGradient
8 | ColorStop = Background.ColorStop
9 |
10 | class Slider extends Spine.Controller
11 | className: 'slider'
12 |
13 | events:
14 | 'mousedown': 'listen'
15 | 'mouseup': 'openColorPicker'
16 |
17 | constructor: (@colorStop = new ColorStop) ->
18 | super()
19 |
20 | @inner = $('').addClass('inner')
21 | @append @inner
22 | @render()
23 |
24 | render: ->
25 | @move(@colorStop.length)
26 | @inner.css(background: @colorStop.color)
27 |
28 | listen: (e) =>
29 | e.preventDefault()
30 | e.stopPropagation()
31 |
32 | @width = @el.parent().width()
33 | @offset = @el.parent().offset()
34 | @remove = false
35 | @moved = false
36 |
37 | $(document).mousemove(@drag)
38 | $(document).mouseup(@drop)
39 |
40 | drag: (e) =>
41 | @moved = true
42 | @picker?.close?()
43 | @picker = false
44 |
45 | top = e.pageY - @offset.top
46 | @remove = top > 100 or top < -100
47 | @el.toggleClass('remove', @remove)
48 |
49 | left = e.pageX - @offset.left
50 | length = (left / @width) * 100
51 | @move(length)
52 |
53 | drop: (e) =>
54 | $(document).unbind('mousemove', @drag)
55 | $(document).unbind('mouseup', @drop)
56 | @release() if @remove
57 |
58 | move: (@length = 0) ->
59 | @length = Math.max(Math.min(@length, 100), 0)
60 | @length = Math.round(@length)
61 | @colorStop.length = @length
62 |
63 | @el.css(left: "#{@length}%")
64 | @el.trigger('change', [this])
65 |
66 | release: ->
67 | @el.trigger('removed', [this])
68 | @el.trigger('change', [this])
69 | super
70 |
71 | openColorPicker: ->
72 | if @moved
73 | return
74 |
75 | @picker = new ColorPicker(color: @colorStop.color)
76 |
77 | @picker.bind 'change', (color) =>
78 | @colorStop.color = color
79 | @el.trigger('change', [this])
80 | @render()
81 |
82 | @picker.open(@el.offset())
83 |
84 | class GradientPicker extends Spine.Controller
85 | className: 'gradientPicker'
86 |
87 | events:
88 | 'removed': 'removeSlider'
89 | 'change': 'set'
90 | 'click': 'createSlider'
91 |
92 | constructor: ->
93 | super
94 |
95 | @gradient or= new LinearGradient
96 | @append(new Slider(stop)) for stop in @gradient.stops
97 |
98 | # Add default stops
99 | unless @gradient.stops.length
100 | @addSlider(new ColorStop(new Color.White, 0))
101 | @addSlider(new ColorStop(new Color.Black, 100))
102 |
103 | @el.css(background: "#{@gradient.toString()}, url(assets/grid.png) repeat")
104 |
105 | addSlider: (colorStop = new ColorStop) ->
106 | @gradient.addStop(colorStop)
107 | @append(new Slider(colorStop))
108 | @set()
109 |
110 | removeSlider: (e, slider) ->
111 | @gradient.removeStop(slider.colorStop)
112 |
113 | set: ->
114 | @el.css(background: "#{@gradient.toString()}, url(assets/grid.png) repeat")
115 | @trigger('change', @gradient)
116 |
117 | createSlider: (e) ->
118 | # Only create sliders for clicks directly on the picker
119 | return unless e.target is e.currentTarget
120 |
121 | left = e.pageX - @el.offset().left
122 | length = (left / @el.width()) * 100
123 |
124 | @addSlider(new ColorStop(new Color.White, length))
125 |
126 | module.exports = GradientPicker
--------------------------------------------------------------------------------
/assets/javascripts/lib/popup.module.coffee:
--------------------------------------------------------------------------------
1 | class Popup extends Spine.Controller
2 | @open: ->
3 | (new this).open(arguments...)
4 |
5 | width: 400
6 |
7 | popupEvents:
8 | 'click .close': 'close'
9 | 'mousedown': 'cancelEvent'
10 |
11 | constructor: ->
12 | super
13 | @delegateEvents(@popupEvents)
14 | @el.addClass('popup')
15 | @el.css(position: 'absolute')
16 |
17 | open: (position = {left: 0, top: 0}) =>
18 | left = position.left or position.clientX
19 | top = position.top or position.clientY
20 |
21 | left -= @width + 17
22 | top -= 5
23 |
24 | @el.css(left: left, top: top).hide()
25 | $('body').append(@el)
26 | $('body').bind('mousedown', @close)
27 |
28 | @el.gfxRaisedIn()
29 |
30 | close: =>
31 | @el.gfxRaisedOut()
32 | @el.queueNext =>
33 | @release()
34 | @trigger 'close'
35 |
36 | release: ->
37 | $('body').unbind('mousedown', @close)
38 | super
39 |
40 | isOpen: ->
41 | !!@el.parent().length
42 |
43 | cancelEvent: (e) ->
44 | e.stopPropagation()
45 |
46 | module.exports = Popup
--------------------------------------------------------------------------------
/assets/javascripts/lib/position_picker.module.coffee:
--------------------------------------------------------------------------------
1 | class PositionPicker extends Spine.Controller
2 | className: 'positionPicker'
3 |
4 | events:
5 | 'mousedown': 'drag'
6 |
7 | width: 40
8 | height: 40
9 |
10 | constructor: ->
11 | super
12 |
13 | @el.css
14 | width: @width
15 | height: @height
16 |
17 | @ball = $('').addClass('ball')
18 | @append @ball
19 |
20 | @ball.css
21 | left: '50%'
22 | top: '50%'
23 |
24 | change: (position) ->
25 | # Offset to center
26 | left = position.left + @width / 2
27 | left = Math.max(Math.min(left, @width), 0)
28 |
29 | top = position.top + @height / 2
30 | top = Math.max(Math.min(top, @height), 0)
31 |
32 | @ball.css(left: left, top: top)
33 |
34 | drag: (e) ->
35 | return if @disabled
36 |
37 | # Center of picker on page
38 | @offset = $(@el).offset()
39 |
40 | $(document).mousemove(@over)
41 | $(document).mouseup(@drop)
42 | @over(e)
43 |
44 | over: (e) =>
45 | e.preventDefault()
46 |
47 | difference =
48 | left: e.pageX - @offset.left - (@width / 2)
49 | top: e.pageY - @offset.top - (@height / 2)
50 |
51 | @change difference
52 | @trigger('change', difference)
53 |
54 | drop: =>
55 | $(document).unbind('mousemove', @over)
56 | $(document).unbind('mouseup', @drop)
57 |
58 | module.exports = PositionPicker
--------------------------------------------------------------------------------
/assets/javascripts/lib/utils.module.coffee:
--------------------------------------------------------------------------------
1 | dasherize = (str) ->
2 | str.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
3 | .replace(/([a-z\d])([A-Z])/g, '$1-$2')
4 | .toLowerCase()
5 |
6 | # Differentiate Safari from Chrome
7 | $.browser.chrome = /chrome/.test(
8 | navigator.userAgent.toLowerCase()
9 | )
10 |
11 | requestAnimationFrame = do ->
12 | request =
13 | window.requestAnimationFrame or
14 | window.webkitRequestAnimationFrame or
15 | window.mozRequestAnimationFrame or
16 | window.oRequestAnimationFrame or
17 | window.msRequestAnimationFrame or
18 | (callback) ->
19 | window.setTimeout(callback, 1000 / 60)
20 |
21 | (callback) ->
22 | request.call(window, callback)
23 |
24 | module.exports =
25 | dasherize: dasherize
26 | browser: $.browser
27 | requestAnimationFrame: requestAnimationFrame
--------------------------------------------------------------------------------
/assets/javascripts/lib/views/color_picker.jst.eco:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/stylesheets/app/color_picker.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | .colorPicker {
4 | -webkit-user-select: none
5 | overflow: hidden
6 | hbox()
7 | padding: 6px
8 | width: 425px
9 |
10 | canvas {
11 | display: block
12 | }
13 |
14 | label {
15 | display: block
16 | overflow: hidden
17 |
18 | span {
19 | display: block
20 | float: left
21 | width: 30px
22 | line-height: 25px
23 |
24 | &:after {
25 | content: ':'
26 | }
27 | }
28 |
29 | input {
30 | width: 60px
31 | }
32 | }
33 |
34 | .spectrum, .gradient, .alpha {
35 | cursor: pointer
36 | }
37 |
38 | .preview {
39 | background: url(grid.png) repeat
40 | }
41 |
42 | .alpha {
43 | background: url(grid.png) repeat
44 | margin: 0 6px 0 0
45 | }
46 |
47 | .preview {
48 | margin: 5px 0 10px
49 | }
50 |
51 | .preview, .preview .inner {
52 | height: 55px
53 | }
54 |
55 | .original {
56 | height: 5px
57 | }
58 |
59 | button.save {
60 | float: right
61 | }
62 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/context_menu.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | .contextMenu {
4 | position: absolute
5 | min-width: 150px
6 | background: rgba(255, 255, 255, 0.95)
7 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2)
8 | border-radius: 3px
9 | border: 1px solid rgba(0, 0, 0, 0.2)
10 | border-bottom-color: rgba(0, 0, 0, 0.25)
11 |
12 | color: #000
13 | font-size: 13px
14 | z-index: 10002
15 | overflow: hidden
16 |
17 | div {
18 | padding: 8px 10px
19 | border-bottom: 1px solid rgba(0, 0, 0, 0.1)
20 | cursor: pointer
21 |
22 | &:last-child {
23 | border-bottom: 0
24 | }
25 |
26 | &.disabled {
27 | color: rgba(0, 0, 0, 0.3)
28 | }
29 |
30 | &:not(.disabled):hover {
31 | color: #FFF
32 | text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.2)
33 | vbg-gradient(#BAC4D8, #9AA8C7)
34 |
35 | &:active {
36 | vbg-gradient(#9AA8C7, #BAC4D8)
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/gradient_picker.css.styl:
--------------------------------------------------------------------------------
1 | .gradientPicker {
2 | position: relative
3 | height: 20px
4 | border-radius: 3px
5 | border: 1px solid darken(#CCC, 10%)
6 | box-shadow: 0 1px 1px #FFF
7 |
8 | .slider {
9 | position: absolute
10 | width: 10px
11 | height: 16px
12 | bottom: 0
13 |
14 | margin: 0 0 -8px -5px
15 | border-radius: 3px
16 | border: 1px solid rgba(0, 0, 0, 0.3)
17 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2)
18 |
19 | background: url(grid.png) repeat
20 | cursor: pointer
21 |
22 | .inner {
23 | border-radius: 3px
24 | height: 16px
25 | }
26 | }
27 |
28 | .colorPreview {
29 | border: 0
30 | width: auto
31 | margin: 0
32 | border: 0
33 |
34 | .inner {
35 | width: auto
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/header.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .header {
4 | position: absolute
5 | top: 0
6 | bottom: 0
7 | left: 0
8 | width: 45px
9 |
10 | // hbg-gradient(#5D646D, #8F9398)
11 | // hbg-gradient(#DDDDDD, #CCCCCC)
12 | hbg-gradient(#F1F1F1, #DDDDDD)
13 |
14 | box-shadow: inset -1px 0 2px rgba(0, 0, 0, 0.2)
15 | vbox()
16 | -webkit-user-select: none
17 |
18 | h1 {
19 | display: block
20 | margin: 0 10px
21 | font: 23px arial,lucida,helvetica,arial,sans-serif
22 | font-weight: normal
23 | color: #FFF
24 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3)
25 | line-height: 33px
26 | box-flex(1)
27 | }
28 |
29 | nav {
30 | vbox()
31 |
32 | div {
33 | cursor: pointer
34 | padding: 8px 12px
35 | border-bottom: 1px solid rgba(0, 0, 0, 0.2)
36 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4)
37 |
38 | &:active {
39 | box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.3)
40 | }
41 | }
42 |
43 | span {
44 | height: 20px
45 | width: 20px
46 | line-height: 20px
47 |
48 | display: block
49 | background: rgba(0, 0, 0, 0.2)
50 | box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.35), 0 1px 0 rgba(255, 255, 255, 0.2)
51 |
52 | color: rgba(0, 0, 0, 0.4)
53 | text-shadow: 0 1px 0 #FFF
54 | font-weight: bold
55 | text-transform: uppercase
56 | font-size: 20px
57 | text-align: center
58 |
59 | border-box()
60 | }
61 |
62 | .ellipsis span {
63 | border-radius: 50%
64 | }
65 |
66 | .text span {
67 | background: none
68 | box-shadow: none
69 | }
70 |
71 | .textInput span {
72 | background: #FFF
73 | text-shadow: none
74 | font-size: 8px
75 | text-transform: none
76 | padding-left: 1px
77 | }
78 |
79 | .button span {
80 | border-radius: 5px
81 | border: 1px solid #A6A6A6;
82 | box-shadow: none
83 | background: -webkit-linear-gradient(270deg, #FFF, #FFF 30%, #F2F2F2 100%)
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/index.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app {
4 | position: absolute
5 | top: 0
6 | right: 0
7 | bottom: 0
8 | left: 0
9 |
10 | .hbox {
11 | box-flex(1)
12 | hbox()
13 | }
14 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/background.css.styl:
--------------------------------------------------------------------------------
1 | #app .inspector .background {
2 | .edit {
3 | margin: 10px
4 | }
5 |
6 | .gradientPicker {
7 | margin: 0px 5px 5px 5px
8 | }
9 |
10 | &.disabled {
11 | .list {
12 | opacity: 0.6
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/border.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .inspector .border {
4 | label {
5 | margin-bottom: 3px
6 |
7 | span {
8 | width: 35px
9 | }
10 |
11 | input {
12 | margin-right: 10px
13 | }
14 |
15 | .colorPreview {
16 | margin: 3px 0 -5px 0
17 | }
18 |
19 | select {
20 | width: 130px
21 | }
22 | }
23 |
24 | .hbox label {
25 | margin-bottom: 0px
26 | }
27 |
28 | .borders {
29 | hbox()
30 | margin: 2px auto 10px auto
31 | vbg-gradient(lighten(rgba(238, 238, 238, 1), 80%), rgba(238, 238, 238, 0.9))
32 | border: 1px solid #CCC
33 | border-radius: 5px
34 | box-shadow: 0 1px 0 #FFF
35 | -webkit-box-pack: center
36 | width: 185px
37 |
38 | div {
39 | overflow: hidden
40 | border-right: 1px solid #CCC
41 |
42 | &:last-child {
43 | border-right: none
44 | }
45 |
46 | &:active, &.active {
47 | box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2)
48 | }
49 | }
50 |
51 | span {
52 | display: block
53 | border-box()
54 | width: 12px
55 | height: 12px
56 | margin: 5px 12px
57 | border: 1px solid #777
58 | cursor: pointer
59 | }
60 |
61 | .borderTop span {
62 | border-top-width: 3px
63 | }
64 |
65 | .borderRight span {
66 | border-right-width: 3px
67 | }
68 |
69 | .borderBottom span {
70 | border-bottom-width: 3px
71 | }
72 |
73 | .borderLeft span {
74 | border-left-width: 3px
75 | }
76 | }
77 |
78 | &.disabled {
79 | .borders, input, select {
80 | opacity: 0.6
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/border_radius.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .inspector .borderRadius {
4 | .borders {
5 | hbox()
6 | margin: 3px auto 10px auto
7 | vbg-gradient(lighten(rgba(238, 238, 238, 1), 80%), rgba(238, 238, 238, 0.9))
8 | border: 1px solid #CCC
9 | border-radius: 5px
10 | box-shadow: 0 1px 0 #FFF
11 | -webkit-box-pack: center
12 | width: 185px
13 |
14 | div {
15 | overflow: hidden
16 | border-right: 1px solid #CCC
17 |
18 | &:last-child {
19 | border-right: none
20 | }
21 |
22 | &:active, &.active {
23 | box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.2)
24 | }
25 | }
26 |
27 | span {
28 | display: block
29 | border-box()
30 | width: 12px
31 | height: 12px
32 | margin: 5px 12px
33 | border: 1px solid #777
34 | cursor: pointer
35 | }
36 |
37 | .borderRadius span {
38 | border-radius: 4px
39 | }
40 |
41 | .borderRadiusTopLeft span {
42 | border-top-left-radius: 4px
43 | }
44 |
45 | .borderRadiusTopRight span {
46 | border-top-right-radius: 4px
47 | }
48 |
49 | .borderRadiusBottomRight span {
50 | border-bottom-right-radius: 4px
51 | }
52 |
53 | .borderRadiusBottomLeft span {
54 | border-bottom-left-radius: 4px
55 | }
56 | }
57 |
58 | label {
59 | hbox()
60 |
61 | input {
62 | display: block
63 | }
64 |
65 | input[type=range] {
66 | margin: 5px
67 | box-flex(1)
68 | }
69 |
70 | input[type=number] {
71 | width: 40px
72 | }
73 | }
74 |
75 | &.disabled {
76 | .borders, input {
77 | opacity: 0.6
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/box_shadow.css.styl:
--------------------------------------------------------------------------------
1 | #app .inspector .boxShadow {
2 | .positionPicker {
3 | margin: 3px 5px 3px 0
4 | }
5 |
6 | label {
7 | span {
8 | margin-left: 3px
9 | width: auto
10 | }
11 | }
12 |
13 | .blur span, .color span {
14 | width: 33px
15 | }
16 |
17 | .color span {
18 | position relative
19 | top: -6px
20 | }
21 |
22 | &.disabled {
23 | .list {
24 | opacity: 0.6
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/dimensions.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .inspector .dimensions {
4 | label {
5 | margin-right: 10px
6 |
7 | span {
8 | width: 38px
9 | }
10 |
11 | input {
12 | width: 45px
13 | }
14 |
15 | &:last-child span {
16 | width: 15px
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/font.css.styl:
--------------------------------------------------------------------------------
1 | #app .inspector .font {
2 | label span {
3 | width: 45px
4 | }
5 |
6 | select {
7 | width: 120px
8 | }
9 |
10 | .color span {
11 | position relative
12 | top: -6px
13 | }
14 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/opacity.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .inspector .opacity {
4 | label {
5 | hbox()
6 |
7 | input {
8 | display: block
9 | }
10 |
11 | input[type=range] {
12 | width: 135px
13 | margin: 5px
14 | box-flex(1)
15 | }
16 |
17 | input[type=number] {
18 | width: 40px
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/popup_menu.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | .popupMenu {
4 | -webkit-user-select: none
5 |
6 | position: absolute
7 | min-width: 130px
8 | background: rgba(255, 255, 255, 0.95)
9 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2)
10 | border-radius: 3px
11 | border: 1px solid rgba(0, 0, 0, 0.2)
12 | border-bottom-color: rgba(0, 0, 0, 0.25)
13 |
14 | color: #000
15 | font-size: 13px
16 | z-index: 10002
17 | overflow: hidden
18 |
19 | div {
20 | padding: 8px 10px
21 | border-bottom: 1px solid rgba(0, 0, 0, 0.1)
22 | cursor: pointer
23 |
24 | &:last-child {
25 | border-bottom: 0
26 | }
27 |
28 | &.disabled {
29 | color: rgba(0, 0, 0, 0.3)
30 | }
31 |
32 | &:not(.disabled):hover {
33 | color: #FFF
34 | text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.2)
35 | vbg-gradient(#BAC4D8, #9AA8C7)
36 |
37 | &:active {
38 | vbg-gradient(#9AA8C7, #BAC4D8)
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/text_position.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .inspector .textPosition {
4 | label span {
5 | width: 85px
6 | }
7 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/inspector/text_shadow.css.styl:
--------------------------------------------------------------------------------
1 | #app .inspector .textShadow {
2 | .positionPicker {
3 | margin: 3px 5px 3px 0
4 | }
5 |
6 | label {
7 | span {
8 | margin-left: 3px
9 | width: auto
10 | }
11 | }
12 |
13 | .blur span, .color span {
14 | width: 33px
15 | }
16 |
17 | .color span {
18 | position relative
19 | top: -6px
20 | }
21 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/popup.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | .popup {
4 | position: absolute
5 |
6 | border-radius: 4px
7 | border: 1px solid rgba(181,181,181, 1)
8 | border-bottom-color: rgba(167,167,167,1)
9 | border-top-color: rgba(216,216,216,1)
10 |
11 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.24)
12 | background: #FFF
13 | min-width: 400px
14 | color: #000
15 | z-index: 10000
16 |
17 | article {
18 | padding: 5px
19 | }
20 |
21 | footer {
22 | border-top: 1px solid rgba(0, 0, 0, 0.15)
23 | inset-line(0.8)
24 | vbg-gradient(lighten(rgba(238, 238, 238, 1), 80%), rgba(238, 238, 238, 0.9))
25 | -webkit-border-radius: 0 0 4px 4px
26 |
27 | button {
28 | margin: 0
29 |
30 | color: #717A81;
31 | font-size: 11px;
32 | font-weight: bold
33 | text-transform: uppercase
34 | text-shadow: 0 1px 1px #FFF;
35 | padding: 8px 13px
36 | box-shadow: inset -1px 0 1px rgba(255, 255, 255, 0.5)
37 | border: none
38 | border-right: 1px solid rgba(0, 0, 0, 0.15)
39 | background: none
40 | }
41 | }
42 | }
43 |
44 | .popup.list {
45 | .items {
46 | min-height: 180px
47 | }
48 |
49 | .item {
50 | padding: 10px
51 | cursor: pointer
52 |
53 | &.selected {
54 | color: #FFF
55 | vbg-gradient(#BAC4D8, #9AA8C7)
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/assets/stylesheets/app/stage.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | #app .stage {
4 | position: absolute
5 | top: 0
6 | right: 220px
7 | bottom: 0
8 | left: 45px
9 |
10 | // background: url('bg-grid.png')
11 | background: #FFF
12 | overflow: auto
13 | -webkit-user-select: none
14 |
15 | .element {
16 | border-box()
17 | color: #000
18 | outline: none
19 | position: absolute
20 | left: 0
21 | top: 0
22 | }
23 |
24 | .element.editing {
25 | -webkit-user-select: text
26 | }
27 |
28 | .element.text {
29 | white-space: nowrap
30 | padding: 5px
31 |
32 | &.editing, &.selected {
33 | outline: 1px solid rgba(0, 0, 0, 0.2)
34 | }
35 | }
36 |
37 | .resizing .thumb {
38 | position: absolute
39 | width: 6px
40 | height: 6px
41 | background: #FFF
42 | border: 1px solid #000
43 | cursor: pointer
44 |
45 | &.tl {
46 | left: -3px
47 | top: -3px
48 | cursor: nw-resize
49 | }
50 |
51 | &.tt {
52 | margin-left: -3px
53 | left: 50%
54 | top:-3px
55 | cursor: n-resize
56 | }
57 |
58 | &.tr {
59 | right: -3px
60 | top: -3px
61 | cursor: ne-resize
62 | }
63 |
64 | &.rr {
65 | right: -3px
66 | top: 50%
67 | margin-top: -3px
68 | cursor: e-resize
69 | }
70 |
71 | &.br {
72 | right: -3px
73 | bottom: -3px
74 | cursor: se-resize
75 | }
76 |
77 | &.bb {
78 | left: 50%
79 | bottom: -3px
80 | margin-left: -3px
81 | cursor: s-resize
82 | }
83 |
84 | &.bl {
85 | left: -3px
86 | bottom: -3px
87 | cursor: sw-resize
88 | }
89 |
90 | &.ll {
91 | top: 50%
92 | left: -3px
93 | margin-top: -3px
94 | cursor: w-resize
95 | }
96 | }
97 |
98 | .selectArea {
99 | position: absolute
100 | border: 1px solid rgba(0, 0, 0, 0.2)
101 | background: rgba(0, 0, 0, 0.1)
102 | z-index: 1000
103 | }
104 |
105 | .snapLine {
106 | background: #FBFF40
107 | position: absolute
108 | z-index: 9999
109 |
110 | &.vertical {
111 | width: 1px
112 | }
113 |
114 | &.horizontal {
115 | height: 1px
116 | }
117 | }
118 |
119 | .coordTitle, .areaTitle {
120 | color: #000
121 | position: absolute
122 | padding: 3px
123 | background: rgba(255, 249, 215, 0.85)
124 | box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3)
125 | z-index: 10001
126 | }
127 | }
--------------------------------------------------------------------------------
/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | = require theme
3 | = require_tree ./app
4 | */
--------------------------------------------------------------------------------
/assets/stylesheets/mixins.styl:
--------------------------------------------------------------------------------
1 | border-radius()
2 | -moz-border-radius: arguments
3 | -webkit-border-radius: arguments
4 | border-radius: arguments
5 |
6 | /* Vertical Background Gradient */
7 | vbg-gradient(fc = #FFF, tc = #FFF)
8 | background: fc
9 | background: -webkit-gradient(linear, left top, left bottom, from(fc), to(tc))
10 | background: -webkit-linear-gradient(top, fc, tc)
11 | background: -moz-linear-gradient(top, fc, tc)
12 | background: linear-gradient(top, fc, tc)
13 |
14 | /* Horizontal Background Gradient */
15 | hbg-gradient(fc = #FFF, tc = #FFF)
16 | background: fc
17 | background: -webkit-gradient(linear, left top, right top, from(fc), to(tc))
18 | background: -webkit-linear-gradient(left, fc, tc)
19 | background: -moz-linear-gradient(left, fc, tc)
20 | background: linear-gradient(left, fc, tc)
21 |
22 | box-shadow()
23 | -moz-box-shadow: arguments
24 | -webkit-box-shadow: arguments
25 | box-shadow: arguments
26 |
27 | inset-box-shadow()
28 | -moz-box-shadow: inset arguments
29 | -webkit-box-shadow: inset arguments
30 | box-shadow: inset arguments
31 |
32 | box-flex(s = 0)
33 | -webkit-box-flex: s
34 | -moz-box-flex: s
35 | box-flex: s
36 |
37 | hbox()
38 | display: -webkit-box
39 | -webkit-box-orient: horizontal
40 | -webkit-box-align: stretch
41 | -webkit-box-pack: start
42 |
43 | display: -moz-box
44 | -moz-box-orient: horizontal
45 | -moz-box-align: stretch
46 | -moz-box-pack: start
47 |
48 | vbox()
49 | display: -webkit-box
50 | -webkit-box-orient: vertical
51 | -webkit-box-align: stretch
52 |
53 | display: -moz-box
54 | -moz-box-orient: vertical
55 | -moz-box-align: stretch
56 |
57 | border-box()
58 | -webkit-box-sizing: border-box
59 | -moz-box-sizing: border-box
60 | box-sizing: border-box
61 |
62 | transition(s = 0.3s, o = opacity, t = linear)
63 | -webkit-transition: s o t
64 | -moz-transition: s o t
65 | transition: s o t
66 |
67 | ellipsis()
68 | text-overflow: ellipsis
69 | overflow: hidden
70 | white-space:nowrap
71 |
72 | inset-line(opacity = 0.4, size = 1px)
73 | inset-box-shadow(0, size, 0, rgba(255, 255, 255, opacity))
74 |
75 | outset-line(opacity = 0.4, size = 1px)
76 | box-shadow(0, size, 0, rgba(255, 255, 255, opacity))
77 |
78 | box-pack(type = center)
79 | -webkit-box-pack: type
80 | -moz-box-pack: type
81 | box-pack: type
82 |
83 | transform(tr)
84 | -webkit-transform: tr
85 | -moz-transform: tr
86 | -ms-transform: tr
87 | -o-transform: tr
88 | transform: tr
89 |
90 | hacel()
91 | transform(translate3d(0,0,0))
--------------------------------------------------------------------------------
/assets/stylesheets/theme.css.styl:
--------------------------------------------------------------------------------
1 | @import 'mixins'
2 |
3 | body, html {
4 | height: 100%
5 | width: 100%
6 | margin: 0
7 | padding: 0
8 | overflow: hidden
9 | // -webkit-user-select: none
10 | }
11 |
12 | body {
13 | text-rendering: optimizeLegibility
14 | -webkit-font-smoothing: antialiased
15 | color: #626262;
16 | font-family: 'Helvetica Neue', Helvetica, Arial Geneva, sans-serif;
17 | font-size: 12px;
18 | }
19 |
20 | // * {
21 | // -moz-box-sizing: border-box;
22 | // -webkit-box-sizing: border-box;
23 | // box-sizing: border-box;
24 | // }
25 | //
26 | // p {
27 | // margin: 0 0 10px 0
28 | // line-height: 1.5em
29 | // }
30 | //
31 | // a {
32 | // color: #2D81C5
33 | // text-shadow: 0 1px 0 #FFF
34 | // text-decoration: none
35 | // cursor: pointer
36 | // }
37 | //
38 | // ::selection {
39 | // background: #E0EDF8
40 | // text-shadow: none
41 | // }
42 | //
43 | // hr {
44 | // border: 1px solid lighten(#D1D1D1, 20%)
45 | // border-width: 1px 0 0 0
46 | // margin: 10px 0 20px 0
47 | // }
48 | //
49 | // input[type=text], input[type=url], textarea {
50 | // padding: 3px
51 | // margin: 0
52 | // border: 1px solid rgba(0, 0, 0, 0.2)
53 | // inset-box-shadow(0, 1px, 2px, rgba(0, 0, 0, 0.1))
54 | // }
55 | //
56 | // input[type=text], input[type=url], textarea, select {
57 | // &:focus {
58 | // outline: none
59 | // border-color: rgba(104, 189, 244, 0.8)
60 | //
61 | // -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(104, 189, 244, 0.6)
62 | // -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(104, 189, 244, 0.6)
63 | // box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(104, 189, 244, 0.6)
64 | // }
65 | // }
66 | //
67 | // textarea {
68 | // padding: 5px
69 | // height: 80px
70 | // }
71 | //
72 |
73 | .right {
74 | float: right
75 | }
76 |
77 | .left {
78 | float: left
79 | }
80 |
81 | .hidden {
82 | display: none !important
83 | }
84 |
85 | .vbox {
86 | vbox()
87 | }
88 |
89 | .hbox {
90 | hbox()
91 | }
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require './app'
2 |
3 | map "/assets" do
4 | run App.sprockets
5 | end
6 |
7 | map "/" do
8 | run App
9 | end
--------------------------------------------------------------------------------
/public/application.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/application.icns
--------------------------------------------------------------------------------
/public/application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/application.png
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/button.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/button":function(exports, require, module){(function() {
62 | var Background, Button, Color, Element,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Color = require('app/models/properties/color');
69 |
70 | Background = require('app/models/properties/background');
71 |
72 | Button = (function(_super) {
73 |
74 | __extends(Button, _super);
75 |
76 | Button.name = 'Button';
77 |
78 | function Button() {
79 | return Button.__super__.constructor.apply(this, arguments);
80 | }
81 |
82 | Button.prototype.className = 'button';
83 |
84 | Button.prototype.id = module.id;
85 |
86 | Button.prototype.events = {
87 | 'resize.element': 'syncLineHeight'
88 | };
89 |
90 | Button.prototype.defaults = function() {
91 | var result;
92 | return result = {
93 | width: 100,
94 | height: 40,
95 | textAlign: 'center',
96 | lineHeight: 40,
97 | borderRadius: 5,
98 | borderWidth: 1,
99 | borderStyle: 'solid',
100 | borderColor: new Color(166, 166, 166),
101 | backgroundImage: [new Background.LinearGradient(new Background.Position(270), [new Background.ColorStop(new Color.White, 0), new Background.ColorStop(new Color.White, 30), new Background.ColorStop(new Color(242, 242, 242), 100)])]
102 | };
103 | };
104 |
105 | Button.prototype.syncLineHeight = function() {
106 | return this.set({
107 | lineHeight: this.get('height')
108 | });
109 | };
110 |
111 | return Button;
112 |
113 | })(Element);
114 |
115 | module.exports = Button;
116 |
117 | }).call(this);
118 | ;}});
119 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/canvas.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/canvas":function(exports, require, module){(function() {
62 | var Canvas, Element,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Canvas = (function(_super) {
69 |
70 | __extends(Canvas, _super);
71 |
72 | Canvas.name = 'Canvas';
73 |
74 | Canvas.prototype.tag = 'canvas';
75 |
76 | Canvas.prototype.points = [];
77 |
78 | function Canvas() {
79 | Canvas.__super__.constructor.apply(this, arguments);
80 | this.ctx = this.el[0].getContext('2d');
81 | }
82 |
83 | Canvas.prototype.paint = function() {
84 | var first, point, points, _i, _len, _ref, _ref1, _ref2;
85 | first = this.points[0];
86 | points = this.points.slice(1, this.points.length);
87 | if (!first) {
88 | return;
89 | }
90 | this.ctx.beginPath();
91 | (_ref = this.ctx).moveTo.apply(_ref, first);
92 | _ref1 = this.points;
93 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
94 | point = _ref1[_i];
95 | (_ref2 = this.ctx).lineTo.apply(_ref2, point);
96 | }
97 | return this.ctx.fill();
98 | };
99 |
100 | Canvas.prototype.width = function(val) {};
101 |
102 | Canvas.prototype.height = function(val) {};
103 |
104 | Canvas.prototype.backgroundImage = function(val) {};
105 |
106 | Canvas.prototype.backgroundColor = function(val) {};
107 |
108 | Canvas.prototype.borderBottom = function(val) {};
109 |
110 | Canvas.prototype.boxShadow = function(val) {};
111 |
112 | Canvas.prototype.borderRadius = function(val) {};
113 |
114 | return Canvas;
115 |
116 | })(Element);
117 |
118 | module.exports = Canvas;
119 |
120 | }).call(this);
121 | ;}});
122 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/ellipsis.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/ellipsis":function(exports, require, module){(function() {
62 | var Element, Ellipsis,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Ellipsis = (function(_super) {
69 |
70 | __extends(Ellipsis, _super);
71 |
72 | Ellipsis.name = 'Ellipsis';
73 |
74 | Ellipsis.prototype.className = 'ellipsis';
75 |
76 | Ellipsis.prototype.id = module.id;
77 |
78 | function Ellipsis() {
79 | Ellipsis.__super__.constructor.apply(this, arguments);
80 | this.properties['borderRadius'] = '50%';
81 | this.paint();
82 | }
83 |
84 | Ellipsis.prototype.borderRadius = function() {
85 | return false;
86 | };
87 |
88 | return Ellipsis;
89 |
90 | })(Element);
91 |
92 | module.exports = Ellipsis;
93 |
94 | }).call(this);
95 | ;}});
96 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/image.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/image":function(exports, require, module){(function() {
62 | var Color, Element, Image,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Color = require('app/models/properties/color');
69 |
70 | Image = (function(_super) {
71 |
72 | __extends(Image, _super);
73 |
74 | Image.name = 'Image';
75 |
76 | Image.prototype.className = 'image';
77 |
78 | Image.prototype.id = module.id;
79 |
80 | function Image(attrs) {
81 | if (attrs == null) {
82 | attrs = {};
83 | }
84 | Image.__super__.constructor.apply(this, arguments);
85 | this.setSrc(attrs.src);
86 | }
87 |
88 | Image.prototype.setSrc = function(src) {
89 | this.src = src;
90 | if (this.src) {
91 | return this.set({
92 | backgroundColor: new Color.Transparent,
93 | backgroundImage: "url(" + this.src + ")",
94 | backgroundSize: '100% 100%',
95 | backgroundRepeat: 'no-repeat',
96 | backgroundPosition: 'center center'
97 | });
98 | }
99 | };
100 |
101 | Image.prototype.toValue = function() {
102 | var result;
103 | result = Image.__super__.toValue.apply(this, arguments);
104 | result.src = this.src;
105 | return result;
106 | };
107 |
108 | return Image;
109 |
110 | })(Element);
111 |
112 | module.exports = Image;
113 |
114 | }).call(this);
115 | ;}});
116 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/input.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/input":function(exports, require, module){(function() {
62 | var CheckBox, Color, Element, Shadow, Text,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Color = require('app/models/properties/color');
69 |
70 | Shadow = require('app/models/properties/shadow');
71 |
72 | Text = (function(_super) {
73 |
74 | __extends(Text, _super);
75 |
76 | Text.name = 'Text';
77 |
78 | function Text() {
79 | return Text.__super__.constructor.apply(this, arguments);
80 | }
81 |
82 | Text.prototype.className = 'textInput';
83 |
84 | Text.prototype.id = module.id + '.Text';
85 |
86 | Text.prototype.defaults = function() {
87 | var result;
88 | return result = {
89 | width: 125,
90 | height: 20,
91 | padding: 3,
92 | borderWidth: 1,
93 | borderStyle: 'solid',
94 | borderColor: new Color(155, 155, 155),
95 | boxShadow: [
96 | new Shadow({
97 | inset: true,
98 | x: 0,
99 | y: 1,
100 | blur: 2,
101 | color: new Color(0, 0, 0, 0.12)
102 | })
103 | ],
104 | backgroundColor: new Color.White
105 | };
106 | };
107 |
108 | return Text;
109 |
110 | })(Element);
111 |
112 | CheckBox = (function(_super) {
113 |
114 | __extends(CheckBox, _super);
115 |
116 | CheckBox.name = 'CheckBox';
117 |
118 | function CheckBox() {
119 | return CheckBox.__super__.constructor.apply(this, arguments);
120 | }
121 |
122 | return CheckBox;
123 |
124 | })(Element);
125 |
126 | module.exports = {
127 | Text: Text,
128 | CheckBox: CheckBox
129 | };
130 |
131 | }).call(this);
132 | ;}});
133 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/line.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/line":function(exports, require, module){(function() {
62 | var Element, Line,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Line = (function(_super) {
69 |
70 | __extends(Line, _super);
71 |
72 | Line.name = 'Line';
73 |
74 | function Line() {
75 | return Line.__super__.constructor.apply(this, arguments);
76 | }
77 |
78 | Line.prototype.className = 'line';
79 |
80 | return Line;
81 |
82 | })(Element);
83 |
84 | module.exports = Line;
85 |
86 | }).call(this);
87 | ;}});
88 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/rectangle.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/rectangle":function(exports, require, module){(function() {
62 | var Element, Rectangle,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Element = require('../element');
67 |
68 | Rectangle = (function(_super) {
69 |
70 | __extends(Rectangle, _super);
71 |
72 | Rectangle.name = 'Rectangle';
73 |
74 | function Rectangle() {
75 | return Rectangle.__super__.constructor.apply(this, arguments);
76 | }
77 |
78 | Rectangle.prototype.className = 'rectangle';
79 |
80 | Rectangle.prototype.id = module.id;
81 |
82 | return Rectangle;
83 |
84 | })(Element);
85 |
86 | module.exports = Rectangle;
87 |
88 | }).call(this);
89 | ;}});
90 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/elements/triangle.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/elements/triangle":function(exports, require, module){(function() {
62 | var Canvas, Triangle,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Canvas = require('./canvas');
67 |
68 | Triangle = (function(_super) {
69 |
70 | __extends(Triangle, _super);
71 |
72 | Triangle.name = 'Triangle';
73 |
74 | function Triangle() {
75 | return Triangle.__super__.constructor.apply(this, arguments);
76 | }
77 |
78 | Triangle.prototype.className = 'triangle';
79 |
80 | Triangle.prototype.points = [1, 2, 3];
81 |
82 | return Triangle;
83 |
84 | })(Canvas);
85 |
86 | module.exports = Rectangle;
87 |
88 | }).call(this);
89 | ;}});
90 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/inspector/opacity.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/inspector/opacity":function(exports, require, module){(function() {
62 | var Opacity,
63 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
64 | __hasProp = {}.hasOwnProperty,
65 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
66 |
67 | Opacity = (function(_super) {
68 |
69 | __extends(Opacity, _super);
70 |
71 | Opacity.name = 'Opacity';
72 |
73 | function Opacity() {
74 | this.render = __bind(this.render, this);
75 | return Opacity.__super__.constructor.apply(this, arguments);
76 | }
77 |
78 | Opacity.prototype.className = 'opacity';
79 |
80 | Opacity.prototype.events = {
81 | 'change input': 'change',
82 | 'focus input': 'inputFocus'
83 | };
84 |
85 | Opacity.prototype.elements = {
86 | 'input': '$inputs'
87 | };
88 |
89 | Opacity.prototype.render = function() {
90 | var _ref;
91 | this.disabled = !this.stage.selection.isAny();
92 | this.opacity = (_ref = this.stage.selection.get('opacity')) != null ? _ref : 1;
93 | return this.html(JST['app/views/inspector/opacity'](this));
94 | };
95 |
96 | Opacity.prototype.change = function(e) {
97 | var val;
98 | this.stage.history.record('opacity');
99 | val = parseFloat($(e.currentTarget).val());
100 | val = Math.round(val * 100) / 100;
101 | this.stage.history.record('opacity');
102 | this.stage.selection.set('opacity', val);
103 | return this.$inputs.val(val);
104 | };
105 |
106 | return Opacity;
107 |
108 | })(Spine.Controller);
109 |
110 | module.exports = Opacity;
111 |
112 | }).call(this);
113 | ;}});
114 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/inspector/popup_menu.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/inspector/popup_menu":function(exports, require, module){(function() {
62 | var PopupMenu,
63 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
64 | __hasProp = {}.hasOwnProperty,
65 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
66 |
67 | PopupMenu = (function(_super) {
68 |
69 | __extends(PopupMenu, _super);
70 |
71 | PopupMenu.name = 'PopupMenu';
72 |
73 | PopupMenu.open = function(position) {
74 | return (new this).open(position);
75 | };
76 |
77 | PopupMenu.prototype.popupMenuEvents = {
78 | 'mousedown': 'cancelEvent'
79 | };
80 |
81 | function PopupMenu() {
82 | this.close = __bind(this.close, this);
83 | PopupMenu.__super__.constructor.apply(this, arguments);
84 | this.delegateEvents(this.popupMenuEvents);
85 | this.el.addClass('popupMenu');
86 | this.el.css({
87 | position: 'absolute'
88 | });
89 | }
90 |
91 | PopupMenu.prototype.open = function(position) {
92 | if (position == null) {
93 | position = {};
94 | }
95 | this.el.css(position);
96 | $('body').append(this.el);
97 | $('body').bind('mousedown', this.close);
98 | return this;
99 | };
100 |
101 | PopupMenu.prototype.close = function() {
102 | this.release();
103 | return this;
104 | };
105 |
106 | PopupMenu.prototype.release = function() {
107 | $('body').unbind('mousedown', this.close);
108 | return PopupMenu.__super__.release.apply(this, arguments);
109 | };
110 |
111 | PopupMenu.prototype.cancelEvent = function(e) {
112 | return e.stopPropagation();
113 | };
114 |
115 | return PopupMenu;
116 |
117 | })(Spine.Controller);
118 |
119 | module.exports = PopupMenu;
120 |
121 | }).call(this);
122 | ;}});
123 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/inspector/stage.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/inspector/stage":function(exports, require, module){(function() {
62 | var StageInspector,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | StageInspector = (function(_super) {
67 |
68 | __extends(StageInspector, _super);
69 |
70 | StageInspector.name = 'StageInspector';
71 |
72 | function StageInspector() {
73 | return StageInspector.__super__.constructor.apply(this, arguments);
74 | }
75 |
76 | StageInspector.prototype.className = 'stageInspector';
77 |
78 | return StageInspector;
79 |
80 | })(Spine.Controller);
81 |
82 | module.exports = StageInspector;
83 |
84 | }).call(this);
85 | ;}});
86 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/inspector/text_style.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/inspector/text_style":function(exports, require, module){(function() {
62 | var TextStyle,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | TextStyle = (function(_super) {
67 |
68 | __extends(TextStyle, _super);
69 |
70 | TextStyle.name = 'TextStyle';
71 |
72 | function TextStyle() {
73 | return TextStyle.__super__.constructor.apply(this, arguments);
74 | }
75 |
76 | TextStyle.prototype.className = 'textStyle';
77 |
78 | return TextStyle;
79 |
80 | })(Spine.Controller);
81 |
82 | module.exports = TextStyle;
83 |
84 | }).call(this);
85 | ;}});
86 |
--------------------------------------------------------------------------------
/public/assets/app/controllers/stage/zindex.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/controllers/stage/zindex":function(exports, require, module){(function() {
62 | var Collection, ZIndex;
63 |
64 | Collection = require('lib/collection');
65 |
66 | ZIndex = (function() {
67 |
68 | ZIndex.name = 'ZIndex';
69 |
70 | function ZIndex(stage) {
71 | this.stage = stage;
72 | this.order = this.stage.elements;
73 | }
74 |
75 | ZIndex.prototype.bringForward = function(element) {
76 | var index;
77 | index = this.order.indexOf(element);
78 | if (index !== -1 || index !== (this.order.length - 1)) {
79 | this.order[index] = this.order[index + 1];
80 | this.order[index + 1] = element;
81 | }
82 | return this.set();
83 | };
84 |
85 | ZIndex.prototype.bringBack = function(element) {
86 | var index;
87 | index = this.order.indexOf(element);
88 | if (index !== -1 || index !== 0) {
89 | this.order[index] = this.order[index - 1];
90 | this.order[index - 1] = element;
91 | }
92 | return this.set();
93 | };
94 |
95 | ZIndex.prototype.bringToFront = function(element) {
96 | var index;
97 | index = this.order.indexOf(element);
98 | this.order.splice(index, 1);
99 | this.order.push(element);
100 | return this.set();
101 | };
102 |
103 | ZIndex.prototype.bringToBack = function(element) {
104 | var index;
105 | index = this.order.indexOf(element);
106 | this.order.splice(index, 1);
107 | this.order.unshift(element);
108 | return this.set();
109 | };
110 |
111 | ZIndex.prototype.set = function() {
112 | var element, index, _i, _len, _ref, _results;
113 | _ref = this.order;
114 | _results = [];
115 | for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
116 | element = _ref[index];
117 | _results.push(element.order(index));
118 | }
119 | return _results;
120 | };
121 |
122 | return ZIndex;
123 |
124 | })();
125 |
126 | module.exports = ZIndex;
127 |
128 | }).call(this);
129 | ;}});
130 |
--------------------------------------------------------------------------------
/public/assets/app/index.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/index":function(exports, require, module){(function() {
62 | var App, Header, Inspector, Stage,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Stage = require('./controllers/stage');
67 |
68 | Header = require('./controllers/header');
69 |
70 | Inspector = require('./controllers/inspector');
71 |
72 | App = (function(_super) {
73 |
74 | __extends(App, _super);
75 |
76 | App.name = 'App';
77 |
78 | App.prototype.className = 'app';
79 |
80 | function App() {
81 | App.__super__.constructor.apply(this, arguments);
82 | this.stage = new Stage;
83 | this.header = new Header({
84 | stage: this.stage
85 | });
86 | this.inspector = new Inspector({
87 | stage: this.stage
88 | });
89 | this.append(this.header.render(), this.stage.render(), this.inspector.render());
90 | }
91 |
92 | return App;
93 |
94 | })(Spine.Controller);
95 |
96 | module.exports = App;
97 |
98 | }).call(this);
99 | ;}});
100 |
--------------------------------------------------------------------------------
/public/assets/app/models/history.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/models/history":function(exports, require, module){(function() {
62 | var History;
63 |
64 | History = (function() {
65 |
66 | History.name = 'History';
67 |
68 | function History() {}
69 |
70 | History.undoStack = [];
71 |
72 | History.redoStack = [];
73 |
74 | History.max = 50;
75 |
76 | History.add = function(action, isUndo) {
77 | var stack;
78 | if (isUndo === true) {
79 | stack = this.undoStack;
80 | } else if (isUndo === false) {
81 | stack = this.redoStack;
82 | } else {
83 | stack = this.undoStack;
84 | if (stack.length >= this.max) {
85 | stack.shift();
86 | }
87 | this.redoStack = [];
88 | }
89 | return stack.push(action);
90 | };
91 |
92 | History.undo = function() {
93 | var action;
94 | action = this.undoStack.pop();
95 | if (action) {
96 | return action.call(this, true);
97 | } else {
98 | return false;
99 | }
100 | };
101 |
102 | History.redo = function() {
103 | var action;
104 | action = this.redoStack.pop();
105 | if (action) {
106 | return action.call(this, false);
107 | } else {
108 | return false;
109 | }
110 | };
111 |
112 | History.clear = function() {
113 | this.undoStack = [];
114 | return this.redoStack = [];
115 | };
116 |
117 | return History;
118 |
119 | })();
120 |
121 | module.exports = History;
122 |
123 | }).call(this);
124 | ;}});
125 |
--------------------------------------------------------------------------------
/public/assets/app/models/properties.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/models/properties":function(exports, require, module){(function() {
62 | var Background, Property, Values;
63 |
64 | Property = require('./property');
65 |
66 | Values = Property.Values;
67 |
68 | Background = require('./properties/background');
69 |
70 | module.exports = {
71 | Property: Property,
72 | Values: Values,
73 | Background: Background
74 | };
75 |
76 | }).call(this);
77 | ;}});
78 |
--------------------------------------------------------------------------------
/public/assets/app/models/properties/border.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/models/properties/border":function(exports, require, module){(function() {
62 | var Border, Color, Property,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Property = require('app/models/property');
67 |
68 | Color = require('./color');
69 |
70 | Border = (function(_super) {
71 |
72 | __extends(Border, _super);
73 |
74 | Border.name = 'Border';
75 |
76 | Border.prototype.id = module.id;
77 |
78 | function Border(width, style, color) {
79 | this.width = width != null ? width : 0;
80 | this.style = style != null ? style : 'solid';
81 | this.color = color != null ? color : new Color.Black;
82 | }
83 |
84 | Border.prototype.toString = function() {
85 | return [this.width + 'px', this.style, this.color].join(' ');
86 | };
87 |
88 | Border.prototype.toValue = function() {
89 | return [this.width, this.style, this.color];
90 | };
91 |
92 | return Border;
93 |
94 | })(Property);
95 |
96 | module.exports = Border;
97 |
98 | }).call(this);
99 | ;}});
100 |
--------------------------------------------------------------------------------
/public/assets/app/models/properties/shadow.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/models/properties/shadow":function(exports, require, module){(function() {
62 | var Color, Property, Shadow,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Property = require('app/models/property');
67 |
68 | Color = require('./color');
69 |
70 | Shadow = (function(_super) {
71 |
72 | __extends(Shadow, _super);
73 |
74 | Shadow.name = 'Shadow';
75 |
76 | Shadow.prototype.id = module.id;
77 |
78 | function Shadow(properties) {
79 | var k, v;
80 | if (properties == null) {
81 | properties = {};
82 | }
83 | for (k in properties) {
84 | v = properties[k];
85 | this[k] = v;
86 | }
87 | this.x || (this.x = 0);
88 | this.y || (this.y = 0);
89 | this.color || (this.color = new Color.Black(0.3));
90 | }
91 |
92 | Shadow.prototype.toString = function() {
93 | var result;
94 | result = [];
95 | if (this.inset) {
96 | result.push('inset');
97 | }
98 | result.push(this.x + 'px');
99 | result.push(this.y + 'px');
100 | if (this.blur != null) {
101 | result.push(this.blur + 'px');
102 | }
103 | if (this.spread != null) {
104 | result.push(this.spread + 'px');
105 | }
106 | result.push(this.color.toString());
107 | return result.join(' ');
108 | };
109 |
110 | Shadow.prototype.toValue = function() {
111 | var value;
112 | return value = {
113 | inset: this.inset,
114 | x: this.x,
115 | y: this.y,
116 | blur: this.blur,
117 | spread: this.spread,
118 | color: this.color
119 | };
120 | };
121 |
122 | return Shadow;
123 |
124 | })(Property);
125 |
126 | module.exports = Shadow;
127 |
128 | }).call(this);
129 | ;}});
130 |
--------------------------------------------------------------------------------
/public/assets/app/models/property.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/models/property":function(exports, require, module){(function() {
62 | var Collection, Property, Serialize, Values,
63 | __hasProp = {}.hasOwnProperty,
64 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };
65 |
66 | Collection = require('lib/collection');
67 |
68 | Serialize = require('./serialize').Serialize;
69 |
70 | Values = (function(_super) {
71 |
72 | __extends(Values, _super);
73 |
74 | Values.name = 'Values';
75 |
76 | function Values() {
77 | return Values.__super__.constructor.apply(this, arguments);
78 | }
79 |
80 | Values.prototype.toString = function() {
81 | return this.join(', ');
82 | };
83 |
84 | return Values;
85 |
86 | })(Collection);
87 |
88 | Property = (function(_super) {
89 |
90 | __extends(Property, _super);
91 |
92 | Property.name = 'Property';
93 |
94 | function Property() {
95 | return Property.__super__.constructor.apply(this, arguments);
96 | }
97 |
98 | Property.include(Serialize);
99 |
100 | Property.prototype.valueOf = function() {
101 | return this.toString();
102 | };
103 |
104 | return Property;
105 |
106 | })(Spine.Module);
107 |
108 | module.exports = Property;
109 |
110 | module.exports.Values = Values;
111 |
112 | }).call(this);
113 | ;}});
114 |
--------------------------------------------------------------------------------
/public/assets/app/parsers/background.pegjs:
--------------------------------------------------------------------------------
1 | URI "uri"
2 | = "url(" value:string / .* ")"
3 |
4 | px
5 | = num "px"
6 |
7 | num
8 | = float
9 | / integer
10 |
11 | integer
12 | = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
13 |
14 | float
15 | = before:[0-9]* "." after:[0-9]+ {
16 | return parseFloat(before.join("") + "." + after.join(""));
17 | }
18 |
19 | string1
20 | = '"' chars:([^\n\r\f\\"] / "\\" nl:nl { return nl } / escape)* '"' {
21 | return chars.join("");
22 | }
23 |
24 | string2
25 | = "'" chars:([^\n\r\f\\'] / "\\" nl:nl { return nl } / escape)* "'" {
26 | return chars.join("");
27 | }
28 |
29 | string
30 | = string1
31 | / string2
32 |
33 | _ "whitespace"
34 | = whitespace*
35 |
36 | whitespace
37 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/app/parsers/color.pegjs:
--------------------------------------------------------------------------------
1 | // CSS Color parser
2 |
3 | start
4 | = hexcolor / rgba / shortcut
5 |
6 | hexcolor "hexcolor"
7 | = hex:(hexcolorshort / hexcolorlong) {
8 | var r = parseInt(hex.substring(0, 2), 16);
9 | var g = parseInt(hex.substring(2, 4), 16);
10 | var b = parseInt(hex.substring(4, 6), 16);
11 |
12 | var Color = require("app/models/properties/color");
13 | return new Color(r, g, b);
14 | }
15 |
16 | hexcolorlong
17 | = "#" hexes:hex* {
18 | return hexes.join('');
19 | }
20 |
21 | hexcolorshort
22 | = "#" h1:hex h2:hex h3:hex {
23 | return h1 + h1 + h2 + h2 + h3 + h3;
24 | }
25 |
26 | hex
27 | = [0-9a-fA-F]
28 |
29 | rgba "rgba"
30 | = ("rgb(" elements:elements ")" / "rgba(" elements:elements ")") {
31 | elements = elements.map(function(i){ return parseFloat(i) });
32 |
33 | var Color = require('app/models/properties/color');
34 | return new Color(elements[0], elements[1], elements[2], elements[3]);
35 | }
36 |
37 | shortcut "shortcut"
38 | = "red" / "tan" / "grey" / "gray" / "lime" / "navy" / "blue" /
39 | "teal" / "aqua" / "cyan" / "gold" / "peru" / "pink" / "plum" / "snow"
40 |
41 | // Utilities
42 |
43 | elements
44 | = head:.* tail:("," .*)* {
45 | var result = [head];
46 | for (var i = 0; i < tail.length; i++) {
47 | result.push(tail[i][2]);
48 | }
49 | return result;
50 | }
--------------------------------------------------------------------------------
/public/assets/app/parsers/import.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"app/parsers/import":function(exports, require, module){(function() {
62 | var importPage;
63 |
64 | importPage = function(tree) {};
65 |
66 | module.exports = importPage;
67 |
68 | }).call(this);
69 | ;}});
70 |
--------------------------------------------------------------------------------
/public/assets/app/parsers/json.pegjs:
--------------------------------------------------------------------------------
1 | /* JSON parser based on the grammar described at http://json.org/. */
2 |
3 | /* ===== Syntactical Elements ===== */
4 |
5 | start
6 | = _ object:object { return object; }
7 |
8 | object
9 | = "{" _ "}" _ { return {}; }
10 | / "{" _ members:members "}" _ { return members; }
11 |
12 | members
13 | = head:pair tail:("," _ pair)* {
14 | var result = {};
15 | result[head[0]] = head[1];
16 | for (var i = 0; i < tail.length; i++) {
17 | result[tail[i][2][0]] = tail[i][2][1];
18 | }
19 | return result;
20 | }
21 |
22 | pair
23 | = name:string ":" _ value:value { return [name, value]; }
24 |
25 | array
26 | = "[" _ "]" _ { return []; }
27 | / "[" _ elements:elements "]" _ { return elements; }
28 |
29 | elements
30 | = head:value tail:("," _ value)* {
31 | var result = [head];
32 | for (var i = 0; i < tail.length; i++) {
33 | result.push(tail[i][2]);
34 | }
35 | return result;
36 | }
37 |
38 | value
39 | = string
40 | / number
41 | / object
42 | / array
43 | / "true" _ { return true; }
44 | / "false" _ { return false; }
45 | // FIXME: We can't return null here because that would mean parse failure.
46 | / "null" _ { return "null"; }
47 |
48 | /* ===== Lexical Elements ===== */
49 |
50 | string "string"
51 | = '"' '"' _ { return ""; }
52 | / '"' chars:chars '"' _ { return chars; }
53 |
54 | chars
55 | = chars:char+ { return chars.join(""); }
56 |
57 | char
58 | // In the original JSON grammar: "any-Unicode-character-except-"-or-\-or-control-character"
59 | = [^"\\\0-\x1F\x7f]
60 | / '\\"' { return '"'; }
61 | / "\\\\" { return "\\"; }
62 | / "\\/" { return "/"; }
63 | / "\\b" { return "\b"; }
64 | / "\\f" { return "\f"; }
65 | / "\\n" { return "\n"; }
66 | / "\\r" { return "\r"; }
67 | / "\\t" { return "\t"; }
68 | / "\\u" h1:hexDigit h2:hexDigit h3:hexDigit h4:hexDigit {
69 | return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4));
70 | }
71 |
72 | number "number"
73 | = int_:int frac:frac exp:exp _ { return parseFloat(int_ + frac + exp); }
74 | / int_:int frac:frac _ { return parseFloat(int_ + frac); }
75 | / int_:int exp:exp _ { return parseFloat(int_ + exp); }
76 | / int_:int _ { return parseFloat(int_); }
77 |
78 | int
79 | = digit19:digit19 digits:digits { return digit19 + digits; }
80 | / digit:digit
81 | / "-" digit19:digit19 digits:digits { return "-" + digit19 + digits; }
82 | / "-" digit:digit { return "-" + digit; }
83 |
84 | frac
85 | = "." digits:digits { return "." + digits; }
86 |
87 | exp
88 | = e:e digits:digits { return e + digits; }
89 |
90 | digits
91 | = digits:digit+ { return digits.join(""); }
92 |
93 | e
94 | = e:[eE] sign:[+-]? { return e + sign; }
95 |
96 | /*
97 | * The following rules are not present in the original JSON gramar, but they are
98 | * assumed to exist implicitly.
99 | *
100 | * FIXME: Define them according to ECMA-262, 5th ed.
101 | */
102 |
103 | digit
104 | = [0-9]
105 |
106 | digit19
107 | = [1-9]
108 |
109 | hexDigit
110 | = [0-9a-fA-F]
111 |
112 | /* ===== Whitespace ===== */
113 |
114 | _ "whitespace"
115 | = whitespace*
116 |
117 | // Whitespace is undefined in the original JSON grammar, so I assume a simple
118 | // conventional definition consistent with ECMA-262, 5th ed.
119 | whitespace
120 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/app/parsers/shadow.pegjs:
--------------------------------------------------------------------------------
1 | // CSS Shadow parser
2 |
3 | start
4 | = inset:"inset"? color:color (values:px _ *) {
5 | var Shadow = require('app/models/properties/shadow');
6 | var props = {};
7 | props.inset = !!inset;
8 | props.x = values[0];
9 | props.y = values[1];
10 | props.blur = values[2];
11 | props.spread = values[3];
12 | return new Shadow(options);
13 | }
14 |
15 | px
16 | = num "px"
17 |
18 | num
19 | = float
20 | / integer
21 |
22 | integer
23 | = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
24 |
25 | float
26 | = before:[0-9]* "." after:[0-9]+ {
27 | return parseFloat(before.join("") + "." + after.join(""));
28 | }
29 |
30 | _ "whitespace"
31 | = whitespace*
32 |
33 | whitespace
34 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/bg-grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/bg-grid.png
--------------------------------------------------------------------------------
/public/assets/blacky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/blacky.png
--------------------------------------------------------------------------------
/public/assets/crosshairs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/crosshairs.png
--------------------------------------------------------------------------------
/public/assets/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/grid.png
--------------------------------------------------------------------------------
/public/assets/lib/utils.module.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!this.require) {
3 | var modules = {}, cache = {};
4 |
5 | var require = function(name, root) {
6 | var path = expand(root, name), indexPath = expand(path, './index'), module, fn;
7 | module = cache[path] || cache[indexPath];
8 | if (module) {
9 | return module;
10 | } else if (fn = modules[path] || modules[path = indexPath]) {
11 | module = {id: path, exports: {}};
12 | cache[path] = module.exports;
13 | fn(module.exports, function(name) {
14 | return require(name, dirname(path));
15 | }, module);
16 | return cache[path] = module.exports;
17 | } else {
18 | throw 'module ' + name + ' not found';
19 | }
20 | };
21 |
22 | var expand = function(root, name) {
23 | var results = [], parts, part;
24 | // If path is relative
25 | if (/^\.\.?(\/|$)/.test(name)) {
26 | parts = [root, name].join('/').split('/');
27 | } else {
28 | parts = name.split('/');
29 | }
30 | for (var i = 0, length = parts.length; i < length; i++) {
31 | part = parts[i];
32 | if (part == '..') {
33 | results.pop();
34 | } else if (part != '.' && part != '') {
35 | results.push(part);
36 | }
37 | }
38 | return results.join('/');
39 | };
40 |
41 | var dirname = function(path) {
42 | return path.split('/').slice(0, -1).join('/');
43 | };
44 |
45 | this.require = function(name) {
46 | return require(name, '');
47 | };
48 |
49 | this.require.define = function(bundle) {
50 | for (var key in bundle) {
51 | modules[key] = bundle[key];
52 | }
53 | };
54 |
55 | this.require.modules = modules;
56 | this.require.cache = cache;
57 | }
58 |
59 | return this.require;
60 | }).call(this);
61 | this.require.define({"lib/utils":function(exports, require, module){(function() {
62 | var dasherize, requestAnimationFrame;
63 |
64 | dasherize = function(str) {
65 | return str.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2').replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase();
66 | };
67 |
68 | $.browser.chrome = /chrome/.test(navigator.userAgent.toLowerCase());
69 |
70 | requestAnimationFrame = (function() {
71 | var request;
72 | request = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
73 | return window.setTimeout(callback, 1000 / 60);
74 | };
75 | return function(callback) {
76 | return request.call(window, callback);
77 | };
78 | })();
79 |
80 | module.exports = {
81 | dasherize: dasherize,
82 | browser: $.browser,
83 | requestAnimationFrame: requestAnimationFrame
84 | };
85 |
86 | }).call(this);
87 | ;}});
88 |
--------------------------------------------------------------------------------
/public/assets/multiple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/multiple.png
--------------------------------------------------------------------------------
/public/assets/parsers/background.pegjs:
--------------------------------------------------------------------------------
1 | URI "uri"
2 | = "url(" value:string / .* ")"
3 |
4 | px
5 | = num "px"
6 |
7 | num
8 | = float
9 | / integer
10 |
11 | integer
12 | = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
13 |
14 | float
15 | = before:[0-9]* "." after:[0-9]+ {
16 | return parseFloat(before.join("") + "." + after.join(""));
17 | }
18 |
19 | string1
20 | = '"' chars:([^\n\r\f\\"] / "\\" nl:nl { return nl } / escape)* '"' {
21 | return chars.join("");
22 | }
23 |
24 | string2
25 | = "'" chars:([^\n\r\f\\'] / "\\" nl:nl { return nl } / escape)* "'" {
26 | return chars.join("");
27 | }
28 |
29 | string
30 | = string1
31 | / string2
32 |
33 | _ "whitespace"
34 | = whitespace*
35 |
36 | whitespace
37 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/parsers/color.pegjs:
--------------------------------------------------------------------------------
1 | // CSS Color parser
2 |
3 | start
4 | = hexcolor / rgba / shortcut
5 |
6 | hexcolor "hexcolor"
7 | = hex:(hexcolorshort / hexcolorlong) {
8 | var r = parseInt(hex.substring(0, 2), 16);
9 | var g = parseInt(hex.substring(2, 4), 16);
10 | var b = parseInt(hex.substring(4, 6), 16);
11 |
12 | var Color = require("app/models/properties/color");
13 | return new Color(r, g, b);
14 | }
15 |
16 | hexcolorlong
17 | = "#" hexes:hex* {
18 | return hexes.join('');
19 | }
20 |
21 | hexcolorshort
22 | = "#" h1:hex h2:hex h3:hex {
23 | return h1 + h1 + h2 + h2 + h3 + h3;
24 | }
25 |
26 | hex
27 | = [0-9a-fA-F]
28 |
29 | rgba "rgba"
30 | = ("rgb(" elements:elements ")" / "rgba(" elements:elements ")") {
31 | elements = elements.map(function(i){ return parseFloat(i) });
32 |
33 | var Color = require('app/models/properties/color');
34 | return new Color(elements[0], elements[1], elements[2], elements[3]);
35 | }
36 |
37 | shortcut "shortcut"
38 | = "red" / "tan" / "grey" / "gray" / "lime" / "navy" / "blue" /
39 | "teal" / "aqua" / "cyan" / "gold" / "peru" / "pink" / "plum" / "snow"
40 |
41 | // Utilities
42 |
43 | elements
44 | = head:.* tail:("," .*)* {
45 | var result = [head];
46 | for (var i = 0; i < tail.length; i++) {
47 | result.push(tail[i][2]);
48 | }
49 | return result;
50 | }
--------------------------------------------------------------------------------
/public/assets/parsers/gradient.pegjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/parsers/gradient.pegjs
--------------------------------------------------------------------------------
/public/assets/parsers/json.pegjs:
--------------------------------------------------------------------------------
1 | /* JSON parser based on the grammar described at http://json.org/. */
2 |
3 | /* ===== Syntactical Elements ===== */
4 |
5 | start
6 | = _ object:object { return object; }
7 |
8 | object
9 | = "{" _ "}" _ { return {}; }
10 | / "{" _ members:members "}" _ { return members; }
11 |
12 | members
13 | = head:pair tail:("," _ pair)* {
14 | var result = {};
15 | result[head[0]] = head[1];
16 | for (var i = 0; i < tail.length; i++) {
17 | result[tail[i][2][0]] = tail[i][2][1];
18 | }
19 | return result;
20 | }
21 |
22 | pair
23 | = name:string ":" _ value:value { return [name, value]; }
24 |
25 | array
26 | = "[" _ "]" _ { return []; }
27 | / "[" _ elements:elements "]" _ { return elements; }
28 |
29 | elements
30 | = head:value tail:("," _ value)* {
31 | var result = [head];
32 | for (var i = 0; i < tail.length; i++) {
33 | result.push(tail[i][2]);
34 | }
35 | return result;
36 | }
37 |
38 | value
39 | = string
40 | / number
41 | / object
42 | / array
43 | / "true" _ { return true; }
44 | / "false" _ { return false; }
45 | // FIXME: We can't return null here because that would mean parse failure.
46 | / "null" _ { return "null"; }
47 |
48 | /* ===== Lexical Elements ===== */
49 |
50 | string "string"
51 | = '"' '"' _ { return ""; }
52 | / '"' chars:chars '"' _ { return chars; }
53 |
54 | chars
55 | = chars:char+ { return chars.join(""); }
56 |
57 | char
58 | // In the original JSON grammar: "any-Unicode-character-except-"-or-\-or-control-character"
59 | = [^"\\\0-\x1F\x7f]
60 | / '\\"' { return '"'; }
61 | / "\\\\" { return "\\"; }
62 | / "\\/" { return "/"; }
63 | / "\\b" { return "\b"; }
64 | / "\\f" { return "\f"; }
65 | / "\\n" { return "\n"; }
66 | / "\\r" { return "\r"; }
67 | / "\\t" { return "\t"; }
68 | / "\\u" h1:hexDigit h2:hexDigit h3:hexDigit h4:hexDigit {
69 | return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4));
70 | }
71 |
72 | number "number"
73 | = int_:int frac:frac exp:exp _ { return parseFloat(int_ + frac + exp); }
74 | / int_:int frac:frac _ { return parseFloat(int_ + frac); }
75 | / int_:int exp:exp _ { return parseFloat(int_ + exp); }
76 | / int_:int _ { return parseFloat(int_); }
77 |
78 | int
79 | = digit19:digit19 digits:digits { return digit19 + digits; }
80 | / digit:digit
81 | / "-" digit19:digit19 digits:digits { return "-" + digit19 + digits; }
82 | / "-" digit:digit { return "-" + digit; }
83 |
84 | frac
85 | = "." digits:digits { return "." + digits; }
86 |
87 | exp
88 | = e:e digits:digits { return e + digits; }
89 |
90 | digits
91 | = digits:digit+ { return digits.join(""); }
92 |
93 | e
94 | = e:[eE] sign:[+-]? { return e + sign; }
95 |
96 | /*
97 | * The following rules are not present in the original JSON gramar, but they are
98 | * assumed to exist implicitly.
99 | *
100 | * FIXME: Define them according to ECMA-262, 5th ed.
101 | */
102 |
103 | digit
104 | = [0-9]
105 |
106 | digit19
107 | = [1-9]
108 |
109 | hexDigit
110 | = [0-9a-fA-F]
111 |
112 | /* ===== Whitespace ===== */
113 |
114 | _ "whitespace"
115 | = whitespace*
116 |
117 | // Whitespace is undefined in the original JSON grammar, so I assume a simple
118 | // conventional definition consistent with ECMA-262, 5th ed.
119 | whitespace
120 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/parsers/shadow.peg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/parsers/shadow.peg
--------------------------------------------------------------------------------
/public/assets/parsers/shadow.pegjs:
--------------------------------------------------------------------------------
1 | // CSS Shadow parser
2 |
3 | start
4 | = inset:"inset"? color:color (values:px _ *) {
5 | var Shadow = require('app/models/properties/shadow');
6 | var props = {};
7 | props.inset = !!inset;
8 | props.x = values[0];
9 | props.y = values[1];
10 | props.blur = values[2];
11 | props.spread = values[3];
12 | return new Shadow(options);
13 | }
14 |
15 | px
16 | = num "px"
17 |
18 | num
19 | = float
20 | / integer
21 |
22 | integer
23 | = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
24 |
25 | float
26 | = before:[0-9]* "." after:[0-9]+ {
27 | return parseFloat(before.join("") + "." + after.join(""));
28 | }
29 |
30 | _ "whitespace"
31 | = whitespace*
32 |
33 | whitespace
34 | = [ \t\n\r]
--------------------------------------------------------------------------------
/public/assets/sprockets/commonjs.rb:
--------------------------------------------------------------------------------
1 | require 'sprockets'
2 | require 'tilt'
3 |
4 | module Sprockets
5 | class CommonJS < Tilt::Template
6 | self.default_mime_type = 'application/javascript'
7 |
8 | def self.default_namespace
9 | 'this.require'
10 | end
11 |
12 | def prepare
13 | @namespace = self.class.default_namespace
14 | end
15 |
16 | attr_reader :namespace
17 |
18 | def evaluate(scope, locals, &block)
19 | if File.extname(scope.logical_path) == '.module'
20 | path = scope.logical_path.chomp('.module').inspect
21 |
22 | scope.require_asset 'sprockets/commonjs'
23 |
24 | code = ''
25 | code << "#{namespace}.define({#{path}:"
26 | code << 'function(exports, require, module){'
27 | code << data
28 | code << ";}});\n"
29 | code
30 | else
31 | data
32 | end
33 | end
34 | end
35 |
36 | register_postprocessor 'application/javascript', CommonJS
37 | append_path File.expand_path('../..', __FILE__)
38 | end
--------------------------------------------------------------------------------
/public/assets/whitey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maccman/stylo/71aecf096e4ebad05404d5a659e2ac9caae94f75/public/assets/whitey.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Stylo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/gfx.coffee:
--------------------------------------------------------------------------------
1 | $ = jQuery ? require('jqueryify')
2 |
3 | throw 'jQuery required' unless $
4 |
5 | $.support.transition or= do ->
6 | style = (new Image).style
7 | 'transition' of style or
8 | 'webkitTransition' of style or
9 | 'MozTransition' of style
10 |
11 | vendor = if $.browser.mozilla then 'moz'
12 | vendor or= 'webkit'
13 | prefix = "-#{vendor}-"
14 |
15 | vendorNames = n =
16 | transition: "#{prefix}transition"
17 | transform: "#{prefix}transform"
18 | transitionEnd: "#{vendor}TransitionEnd"
19 |
20 | defaults =
21 | duration: 400
22 | queue: true
23 | easing: ''
24 | enabled: $.support.transition
25 |
26 | transformTypes = [
27 | 'scale', 'scaleX', 'scaleY', 'scale3d',
28 | 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'rotate3d',
29 | 'translate', 'translateX', 'translateY', 'translateZ', 'translate3d',
30 | 'skew', 'skewX', 'skewY',
31 | 'matrix', 'matrix3d', 'perspective'
32 | ]
33 |
34 | # Internal helper functions
35 |
36 | $.fn.queueNext = (callback, type) ->
37 | type or= "fx";
38 |
39 | @queue ->
40 | callback.apply(this, arguments)
41 | redraw = this.offsetHeight
42 | jQuery.dequeue(this, type)
43 |
44 | $.fn.emulateTransitionEnd = (duration) ->
45 | called = false
46 | $(@).one(n.transitionEnd, -> called = true)
47 | callback = => $(@).trigger(n.transitionEnd) unless called
48 | setTimeout(callback, duration)
49 |
50 | # Helper function for easily adding transforms
51 |
52 | $.fn.transform = (properties, options) ->
53 | opts = $.extend({}, defaults, options)
54 | return this unless opts.enabled
55 |
56 | transforms = []
57 |
58 | for key, value of properties when key in transformTypes
59 | transforms.push("#{key}(#{value})")
60 | delete properties[key]
61 |
62 | if transforms.length
63 | properties[n.transform] = transforms.join(' ')
64 |
65 | if opts.origin
66 | properties["#{prefix}transform-origin"] = opts.origin
67 |
68 | $(@).css(properties)
69 |
70 | $.fn.gfx = (properties, options) ->
71 | opts = $.extend({}, defaults, options)
72 | return this unless opts.enabled
73 |
74 | properties[n.transition] = "all #{opts.duration}ms #{opts.easing}"
75 |
76 | callback = ->
77 | $(@).css(n.transition, '')
78 | opts.complete?.apply(this, arguments)
79 | $(@).dequeue()
80 |
81 | @[ if opts.queue is false then 'each' else 'queue' ] ->
82 | $(@).one(n.transitionEnd, callback)
83 | $(@).transform(properties)
84 |
85 | # Sometimes the event doesn't fire, so we have to fire it manually
86 | $(@).emulateTransitionEnd(opts.duration + 50)
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/list.coffee:
--------------------------------------------------------------------------------
1 | Spine = @Spine or require('spine')
2 | $ = Spine.$
3 |
4 | class Spine.List extends Spine.Controller
5 | events:
6 | 'click .item': 'click'
7 |
8 | selectFirst: false
9 |
10 | constructor: ->
11 | super
12 | @bind 'change', @change
13 |
14 | template: ->
15 | throw 'Override template'
16 |
17 | change: (item) =>
18 | @current = item
19 |
20 | unless @current
21 | @children().removeClass('active')
22 | return
23 |
24 | @children().removeClass('active')
25 | $(@children().get(@items.indexOf(@current))).addClass('active')
26 |
27 | render: (items) ->
28 | @items = items if items
29 | @html @template(@items)
30 | @change @current
31 | if @selectFirst
32 | unless @children('.active').length
33 | @children(':first').click()
34 |
35 | children: (sel) ->
36 | @el.children(sel)
37 |
38 | click: (e) ->
39 | item = @items[$(e.currentTarget).index()]
40 | @trigger('change', item)
41 | true
42 |
43 | module?.exports = Spine.List
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/local.coffee:
--------------------------------------------------------------------------------
1 | Spine = @Spine or require('spine')
2 |
3 | Spine.Model.Local =
4 | extended: ->
5 | @change @saveLocal
6 | @fetch @loadLocal
7 |
8 | saveLocal: ->
9 | result = JSON.stringify(@)
10 | localStorage[@className] = result
11 |
12 | loadLocal: ->
13 | result = localStorage[@className]
14 | @refresh(result or [], clear: true)
15 |
16 | module?.exports = Spine.Model.Local
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/manager.coffee:
--------------------------------------------------------------------------------
1 | Spine = @Spine or require('spine')
2 | $ = Spine.$
3 |
4 | class Spine.Manager extends Spine.Module
5 | @include Spine.Events
6 |
7 | constructor: ->
8 | @controllers = []
9 | @bind 'change', @change
10 | @add(arguments...)
11 |
12 | add: (controllers...) ->
13 | @addOne(cont) for cont in controllers
14 |
15 | addOne: (controller) ->
16 | controller.bind 'active', (args...) =>
17 | @trigger('change', controller, args...)
18 | controller.bind 'release', =>
19 | @controllers.splice(@controllers.indexOf(controller), 1)
20 |
21 | @controllers.push(controller)
22 |
23 | deactivate: ->
24 | @trigger('change', false, arguments...)
25 |
26 | # Private
27 |
28 | change: (@current, args...) ->
29 | for cont in @controllers
30 | if cont is @current
31 | cont.activate(args...)
32 | else
33 | cont.deactivate(args...)
34 |
35 | Spine.Controller.include
36 | active: (args...) ->
37 | if typeof args[0] is 'function'
38 | @bind('active', args[0])
39 | else
40 | args.unshift('active')
41 | @trigger(args...)
42 | this
43 |
44 | isActive: ->
45 | @el.hasClass('active')
46 |
47 | activate: ->
48 | @el.addClass('active')
49 | this
50 |
51 | deactivate: ->
52 | @el.removeClass('active')
53 | this
54 |
55 | class Spine.Stack extends Spine.Controller
56 | controllers: {}
57 | routes: {}
58 |
59 | className: 'spine stack'
60 |
61 | constructor: ->
62 | super
63 |
64 | @manager = new Spine.Manager
65 |
66 | for key, value of @controllers
67 | @[key] = new value(stack: @)
68 | @add(@[key])
69 |
70 | for key, value of @routes
71 | do (key, value) =>
72 | callback = value if typeof value is 'function'
73 | callback or= => @[value].active(arguments...)
74 | @route(key, callback)
75 |
76 | @[@default].active() if @default
77 |
78 | add: (controller) ->
79 | @manager.add(controller)
80 | @append(controller)
81 |
82 | module?.exports = Spine.Manager
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/relation.coffee:
--------------------------------------------------------------------------------
1 | Spine = @Spine or require('spine')
2 | isArray = Spine.isArray
3 | require = @require or ((value) -> eval(value))
4 |
5 | class Collection extends Spine.Module
6 | constructor: (options = {}) ->
7 | for key, value of options
8 | @[key] = value
9 |
10 | all: ->
11 | @model.select (rec) => @associated(rec)
12 |
13 | first: ->
14 | @all()[0]
15 |
16 | last: ->
17 | values = @all()
18 | values[values.length - 1]
19 |
20 | find: (id) ->
21 | records = @select (rec) =>
22 | rec.id + '' is id + ''
23 | throw('Unknown record') unless records[0]
24 | records[0]
25 |
26 | findAllByAttribute: (name, value) ->
27 | @model.select (rec) =>
28 | @associated(rec) and rec[name] is value
29 |
30 | findByAttribute: (name, value) ->
31 | @findAllByAttribute(name, value)[0]
32 |
33 | select: (cb) ->
34 | @model.select (rec) =>
35 | @associated(rec) and cb(rec)
36 |
37 | refresh: (values) ->
38 | delete @model.records[record.id] for record in @all()
39 | records = @model.fromJSON(values)
40 |
41 | records = [records] unless isArray(records)
42 |
43 | for record in records
44 | record.newRecord = false
45 | record[@fkey] = @record.id
46 | @model.records[record.id] = record
47 |
48 | @model.trigger('refresh', @model.cloneArray(records))
49 |
50 | create: (record) ->
51 | record[@fkey] = @record.id
52 | @model.create(record)
53 |
54 | # Private
55 |
56 | associated: (record) ->
57 | record[@fkey] is @record.id
58 |
59 | class Instance extends Spine.Module
60 | constructor: (options = {}) ->
61 | for key, value of options
62 | @[key] = value
63 |
64 | exists: ->
65 | @record[@fkey] and @model.exists(@record[@fkey])
66 |
67 | update: (value) ->
68 | unless value instanceof @model
69 | value = new @model(value)
70 | value.save() if value.isNew()
71 | @record[@fkey] = value and value.id
72 |
73 | class Singleton extends Spine.Module
74 | constructor: (options = {}) ->
75 | for key, value of options
76 | @[key] = value
77 |
78 | find: ->
79 | @record.id and @model.findByAttribute(@fkey, @record.id)
80 |
81 | update: (value) ->
82 | unless value instanceof @model
83 | value = @model.fromJSON(value)
84 |
85 | value[@fkey] = @record.id
86 | value.save()
87 |
88 | singularize = (str) ->
89 | str.replace(/s$/, '')
90 |
91 | underscore = (str) ->
92 | str.replace(/::/g, '/')
93 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
94 | .replace(/([a-z\d])([A-Z])/g, '$1_$2')
95 | .replace(/-/g, '_')
96 | .toLowerCase()
97 |
98 | Spine.Model.extend
99 | hasMany: (name, model, fkey) ->
100 | fkey ?= "#{underscore(this.className)}_id"
101 |
102 | association = (record) ->
103 | model = require(model) if typeof model is 'string'
104 |
105 | new Collection(
106 | name: name, model: model,
107 | record: record, fkey: fkey
108 | )
109 |
110 | @::[name] = (value) ->
111 | association(@).refresh(value) if value?
112 | association(@)
113 |
114 | belongsTo: (name, model, fkey) ->
115 | fkey ?= "#{singularize(name)}_id"
116 |
117 | association = (record) ->
118 | model = require(model) if typeof model is 'string'
119 |
120 | new Instance(
121 | name: name, model: model,
122 | record: record, fkey: fkey
123 | )
124 |
125 | @::[name] = (value) ->
126 | association(@).update(value) if value?
127 | association(@).exists()
128 |
129 | @attributes.push(fkey)
130 |
131 | hasOne: (name, model, fkey) ->
132 | fkey ?= "#{underscore(@className)}_id"
133 |
134 | association = (record) ->
135 | model = require(model) if typeof model is 'string'
136 |
137 | new Singleton(
138 | name: name, model: model,
139 | record: record, fkey: fkey
140 | )
141 |
142 | @::[name] = (value) ->
143 | association(@).update(value) if value?
144 | association(@).find()
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/route.coffee:
--------------------------------------------------------------------------------
1 | Spine = @Spine or require('spine')
2 | $ = Spine.$
3 |
4 | hashStrip = /^#*/
5 | namedParam = /:([\w\d]+)/g
6 | splatParam = /\*([\w\d]+)/g
7 | escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g
8 |
9 | class Spine.Route extends Spine.Module
10 | @extend Spine.Events
11 |
12 | @historySupport: window.history?.pushState?
13 |
14 | @routes: []
15 |
16 | @options:
17 | trigger: true
18 | history: false
19 | shim: false
20 |
21 | @add: (path, callback) ->
22 | if (typeof path is 'object' and path not instanceof RegExp)
23 | @add(key, value) for key, value of path
24 | else
25 | @routes.push(new @(path, callback))
26 |
27 | @setup: (options = {}) ->
28 | @options = $.extend({}, @options, options)
29 |
30 | if (@options.history)
31 | @history = @historySupport && @options.history
32 |
33 | return if @options.shim
34 |
35 | if @history
36 | $(window).bind('popstate', @change)
37 | else
38 | $(window).bind('hashchange', @change)
39 | @change()
40 |
41 | @unbind: ->
42 | if @history
43 | $(window).unbind('popstate', @change)
44 | else
45 | $(window).unbind('hashchange', @change)
46 |
47 | @navigate: (args...) ->
48 | options = {}
49 |
50 | lastArg = args[args.length - 1]
51 | if typeof lastArg is 'object'
52 | options = args.pop()
53 | else if typeof lastArg is 'boolean'
54 | options.trigger = args.pop()
55 |
56 | options = $.extend({}, @options, options)
57 |
58 | path = args.join('/')
59 | return if @path is path
60 | @path = path
61 |
62 | @trigger('navigate', @path)
63 |
64 | @matchRoute(@path, options) if options.trigger
65 |
66 | return if options.shim
67 |
68 | if @history
69 | history.pushState(
70 | {},
71 | document.title,
72 | @path
73 | )
74 | else
75 | window.location.hash = @path
76 |
77 | # Private
78 |
79 | @getPath: ->
80 | path = window.location.pathname
81 | if path.substr(0,1) isnt '/'
82 | path = '/' + path
83 | path
84 |
85 | @getHash: -> window.location.hash
86 |
87 | @getFragment: -> @getHash().replace(hashStrip, '')
88 |
89 | @getHost: ->
90 | (document.location + '').replace(@getPath() + @getHash(), '')
91 |
92 | @change: ->
93 | path = if @getFragment() isnt '' then @getFragment() else @getPath()
94 | return if path is @path
95 | @path = path
96 | @matchRoute(@path)
97 |
98 | @matchRoute: (path, options) ->
99 | for route in @routes
100 | if route.match(path, options)
101 | @trigger('change', route, path)
102 | return route
103 |
104 | constructor: (@path, @callback) ->
105 | @names = []
106 |
107 | if typeof path is 'string'
108 | namedParam.lastIndex = 0
109 | while (match = namedParam.exec(path)) != null
110 | @names.push(match[1])
111 |
112 | path = path.replace(escapeRegExp, '\\$&')
113 | .replace(namedParam, '([^\/]*)')
114 | .replace(splatParam, '(.*?)')
115 |
116 | @route = new RegExp('^' + path + '$')
117 | else
118 | @route = path
119 |
120 | match: (path, options = {}) ->
121 | match = @route.exec(path)
122 | return false unless match
123 | options.match = match
124 | params = match.slice(1)
125 |
126 | if @names.length
127 | for param, i in params
128 | options[@names[i]] = param
129 |
130 | @callback.call(null, options) isnt false
131 |
132 | # Coffee-script bug
133 | Spine.Route.change = Spine.Route.proxy(Spine.Route.change)
134 |
135 | Spine.Controller.include
136 | route: (path, callback) ->
137 | Spine.Route.add(path, @proxy(callback))
138 |
139 | routes: (routes) ->
140 | @route(key, value) for key, value of routes
141 |
142 | navigate: ->
143 | Spine.Route.navigate.apply(Spine.Route, arguments)
144 |
145 | module?.exports = Spine.Route
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/tabs.coffee:
--------------------------------------------------------------------------------
1 | Spine ?= require('spine')
2 | $ = Spine.$
3 |
4 | class Spine.Tabs extends Spine.Controller
5 | events:
6 | 'click [data-name]': 'click'
7 |
8 | constructor: ->
9 | super
10 | @bind 'change', @change
11 |
12 | change: (name) =>
13 | return unless name
14 | @current = name
15 | @children().removeClass('active')
16 | @children("[data-name=#{@current}]").addClass('active')
17 |
18 | render: ->
19 | @change @current
20 | unless @children('.active').length or @current
21 | @children(':first').click()
22 |
23 | children: (sel) ->
24 | @el.children(sel)
25 |
26 | click: (e) ->
27 | name = $(e.currentTarget).attr('data-name')
28 | @trigger('change', name)
29 |
30 | connect: (tabName, controller) ->
31 | @bind 'change', (name) ->
32 | controller.active() if name is tabName
33 | controller.bind 'active', =>
34 | @change tabName
35 |
36 | module?.exports = Spine.Tabs
37 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/spine/tmpl.coffee:
--------------------------------------------------------------------------------
1 | # jQuery.tmpl.js utilities
2 |
3 | $ = jQuery ? require("jqueryify")
4 |
5 | $.fn.item = ->
6 | item = $(@)
7 | item = item.data("item") or item.tmplItem?().data
8 | item?.reload?()
9 | item
10 |
11 | $.fn.forItem = (item) ->
12 | @filter ->
13 | compare = $(@).item()
14 | return item.eql?(compare) or item is compare
15 |
16 |
--------------------------------------------------------------------------------