qedproject 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/bin/qedproject +49 -0
  2. data/lib/qedproject/helpers.rb +17 -0
  3. data/lib/qedproject/libraries/backbone.rb +9 -0
  4. data/lib/qedproject/libraries/base.rb +105 -0
  5. data/lib/qedproject/libraries/jquery.rb +9 -0
  6. data/lib/qedproject/libraries/jquerytmpl.rb +9 -0
  7. data/lib/qedproject/libraries/knockout.rb +9 -0
  8. data/lib/qedproject/libraries/skeleton.rb +21 -0
  9. data/lib/qedproject/project.rb +185 -0
  10. data/lib/qedproject/version.rb +3 -0
  11. data/lib/qedproject.rb +14 -0
  12. data/templates/Guardfile +29 -0
  13. data/templates/Rakefile +25 -0
  14. data/templates/assets.yml +10 -0
  15. data/templates/index.html +35 -0
  16. data/templates/sampleSpec.coffee +0 -0
  17. data/templates/sampleSpec.js +7 -0
  18. data/templates/suite.html +23 -0
  19. data/vendor/backbone/VERSION +1 -0
  20. data/vendor/backbone/backbone-min.js +33 -0
  21. data/vendor/backbone/json2.js +480 -0
  22. data/vendor/backbone/underscore-min.js +27 -0
  23. data/vendor/jasmine/lib/jasmine-1.0.2/jasmine-html.js +188 -0
  24. data/vendor/jasmine/lib/jasmine-1.0.2/jasmine.css +166 -0
  25. data/vendor/jasmine/lib/jasmine-1.0.2/jasmine.js +2421 -0
  26. data/vendor/jquery/VERSION +1 -0
  27. data/vendor/jquery/jquery-1.6.2.min.js +18 -0
  28. data/vendor/jquerytmpl/VERSION +1 -0
  29. data/vendor/jquerytmpl/jquery.tmpl.min.js +10 -0
  30. data/vendor/knockout/VERSION +1 -0
  31. data/vendor/knockout/knockout-1.2.1.js +76 -0
  32. data/vendor/skeleton/VERSION +1 -0
  33. data/vendor/skeleton/apple-touch-icon-114x114.png +0 -0
  34. data/vendor/skeleton/apple-touch-icon-72x72.png +0 -0
  35. data/vendor/skeleton/apple-touch-icon.png +0 -0
  36. data/vendor/skeleton/base.css +335 -0
  37. data/vendor/skeleton/favicon.ico +0 -0
  38. data/vendor/skeleton/layout.css +58 -0
  39. data/vendor/skeleton/skeleton.css +236 -0
  40. data/vendor/skeleton/tabs.js +42 -0
  41. data/vendor/skeleton/templates/index.html +94 -0
  42. metadata +219 -0
data/bin/qedproject ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ require 'qedproject'
3
+
4
+ require 'optparse'
5
+
6
+ banner = "QEDProject #{QEDProject::VERSION} (C) 2011 Brian P. Hogan\n"
7
+ banner << "-" * banner.length
8
+ puts banner
9
+
10
+ options = {}
11
+ LIBRARIES = QEDProject::Libraries::Base.libs.keys
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: qedproject <projectname> [options]"
14
+
15
+
16
+ opts.on('-s', '--sass', "Use SASS") do
17
+ options[:sass] = true
18
+ end
19
+
20
+ opts.on('-c', '--coffee', "Use Coffeescript") do
21
+ options[:coffeescript] = true
22
+ end
23
+
24
+ opts.on('-a', '--assets', "Use Jammit to manage assets") do
25
+ options[:jammit] = true
26
+ end
27
+
28
+ opts.on('-t', '--testing', "Set up testing with Jasmine") do
29
+ options[:testing] = true
30
+ end
31
+
32
+ opts.on("-l", "--libs [LIBRARIES]", "comma-separated list of libraries to include.\nLibraries are: #{LIBRARIES.join(",")}") do |libs|
33
+ options[:libs] = libs.split(",").collect{ |f| f.to_sym }
34
+ end
35
+ end.parse!
36
+ options[:verbose] = true
37
+ project = ARGV[0]
38
+
39
+ if project.nil?
40
+ puts "Specify a project. Use qedproject -h for options."
41
+ else
42
+ begin
43
+ QEDProject::Project.create(project, options)
44
+ rescue QEDProject::BadLibraryError => e
45
+ puts e.message
46
+ puts "QEDProject supports #{LIBRARIES.join(",")}"
47
+ end
48
+ end
49
+
@@ -0,0 +1,17 @@
1
+ module QEDProject
2
+ module Helpers
3
+ # Reads a template from the file system,
4
+ # evaluates it with ERB
5
+ # places it in the output folder specified.
6
+ # Takes the binding context as the last parameter
7
+ # so that ERb has access to instance variables, etc.
8
+ # This works similar to how Rails and Sinatra templates
9
+ # work.
10
+ def render_template_to_file(template, file, context)
11
+ t = File.read(File.join(self.template_root, template))
12
+ File.open(file, "w") do |f|
13
+ f << ERB.new(t, nil, "-").result(context)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module QEDProject
2
+ module Libraries
3
+ class Backbone < QEDProject::Libraries::Base
4
+ library :backbone
5
+ set_js_files ["json2.js", "underscore-min.js", "backbone-min.js"]
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,105 @@
1
+ module QEDProject
2
+ module Libraries
3
+
4
+ class Base
5
+ include QEDProject::Helpers
6
+
7
+ attr_accessor :template_root, :lib_root, :project
8
+ class << self
9
+
10
+ attr_accessor :libs
11
+
12
+ # Support for DSL, for setting the library name
13
+ # when creating the adapter.
14
+ # Adds the library and class to QEDProject::Libraries::Base.libs hash
15
+ # and also creates a getter method on the adapter instance
16
+ def library(name)
17
+ QEDProject::Libraries::Base.libs ||= {}
18
+ QEDProject::Libraries::Base.libs[name] = self
19
+ class_eval do
20
+ define_method :library do
21
+ name
22
+ end
23
+ end
24
+ end
25
+
26
+ def set_js_files(files)
27
+ # define class method
28
+ m = class << self; self; end
29
+ m.send :define_method, :js_files do
30
+ files
31
+ end
32
+
33
+ # define instance method
34
+ class_eval do
35
+ define_method :js_files do
36
+ files
37
+ end
38
+ end
39
+ end
40
+
41
+ # convenience method for the DSL for setting CSS files
42
+ def set_css_files(files)
43
+ # define class method to get the data out
44
+ m = class << self; self; end
45
+ m.send :define_method, :css_files do
46
+ files
47
+ end
48
+
49
+ class_eval do
50
+ define_method :css_files do
51
+ files
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ # convenience method for the DSL for setting image files.
58
+ def set_image_files(files)
59
+
60
+ m = class << self; self; end
61
+ m.send :define_method, :image_files do
62
+ files
63
+ end
64
+
65
+ class_eval do
66
+ define_method :image_files do
67
+ files
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def initialize(project)
74
+ @project = project
75
+ @lib_root = File.join(@project.vendor_root, self.library.to_s)
76
+ @template_root = File.join(@lib_root, "templates")
77
+ end
78
+
79
+ def generate!
80
+ self.copy_js if self.respond_to?(:js_files) && self.js_files.any?
81
+ self.copy_css if self.respond_to?(:css_files) && self.css_files.any?
82
+ self.copy_images if self.respond_to?(:image_files) && self.image_files.any?
83
+ end
84
+
85
+ def copy_images
86
+ self.image_files.each do |lib|
87
+ ::FileUtils.cp_r File.join(self.lib_root, lib), File.join(self.project.path, self.project.images_path), :verbose => self.project.verbose
88
+ end
89
+ end
90
+
91
+ def copy_css
92
+ self.css_files.each do |lib|
93
+ ::FileUtils.cp_r File.join(self.lib_root, lib), File.join(self.project.path, self.project.css_path), :verbose => self.project.verbose
94
+ end
95
+ end
96
+
97
+ def copy_js
98
+ self.js_files.each do |lib|
99
+ ::FileUtils.cp_r File.join(self.lib_root, lib), File.join(self.project.path, self.project.js_path), :verbose => self.project.verbose
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,9 @@
1
+ module QEDProject
2
+ module Libraries
3
+ class JQuery < QEDProject::Libraries::Base
4
+ library :jquery
5
+ set_js_files ["jquery-1.6.2.min.js"]
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,9 @@
1
+ module QEDProject
2
+ module Libraries
3
+ class JQuerytmpl < QEDProject::Libraries::Base
4
+ library :jquerytmpl
5
+ set_js_files ["jquery.tmpl.min.js"]
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,9 @@
1
+ module QEDProject
2
+ module Libraries
3
+ class Knockout < QEDProject::Libraries::Base
4
+ library :knockout
5
+ set_js_files ["knockout-1.2.1.js"]
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,21 @@
1
+ module QEDProject
2
+ module Libraries
3
+ class Skeleton < QEDProject::Libraries::Base
4
+ library :skeleton
5
+ set_js_files ["tabs.js"]
6
+ set_css_files ["base.css", "skeleton.css", "layout.css" ]
7
+ set_image_files [
8
+ "apple-touch-icon-114x114.png",
9
+ "apple-touch-icon-72x72.png",
10
+ "apple-touch-icon.png",
11
+ "favicon.ico"
12
+ ]
13
+
14
+ def generate!
15
+ super
16
+ render_template_to_file "index.html", File.join(self.project.path, "public", "index.html"), binding
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,185 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ module QEDProject
4
+
5
+ class Project
6
+
7
+ include QEDProject::Helpers
8
+
9
+ attr_accessor :path, :libs, :coffeescript, :sass, :jammit,
10
+ :js_path, :css_path, :images_path, :verbose, :testing
11
+ # convenience method to create a new project.
12
+ # Simply call create with the project path and the options.
13
+ def self.create(project_path, options= {})
14
+ self.new(project_path, options).generate
15
+ end
16
+
17
+ def template_root
18
+ File.expand_path("../../../templates", __FILE__)
19
+ end
20
+
21
+ def vendor_root
22
+ File.expand_path("../../../vendor", __FILE__)
23
+ end
24
+
25
+ # Creates a new Project instance.
26
+ # Options:
27
+ # :libs : A list of js libraries to include, Can be :backbone, :jquery, :knockout
28
+ # :jammit: boolean, should Jammit be included?
29
+ # :coffeescript : Should CoffeeScript support be included?
30
+ # :sass Should sass support be included?
31
+ def initialize(project_path, options = {})
32
+ self.path = project_path
33
+ self.process_options(options)
34
+ self.set_paths
35
+ end
36
+
37
+
38
+ # Convenience method for showing all of the JavaScript files that
39
+ # the project should contain. Good for creating asset lists
40
+ # and loading files in rendered templates
41
+ def js_assets
42
+ libs.inject([]) do |original, lib|
43
+ if QEDProject::Libraries::Base.libs[lib].respond_to?(:js_files)
44
+ original += QEDProject::Libraries::Base.libs[lib].js_files
45
+ else
46
+ original
47
+ end
48
+ end
49
+ end
50
+
51
+ # Convenience method for showing all of the css files that
52
+ # the project should contain. Good for creating asset lists
53
+ # and loading files in rendered templates
54
+ def css_assets
55
+ libs.inject([]) do |original, lib|
56
+ if QEDProject::Libraries::Base.libs[lib].respond_to?(:css_files)
57
+ original += QEDProject::Libraries::Base.libs[lib].css_files
58
+ else
59
+ original
60
+ end
61
+ end
62
+ end
63
+
64
+ # Sets instance methods with values from the options hash.
65
+ def process_options(options)
66
+ self.libs = options[:libs] || []
67
+
68
+ libs.each do |lib|
69
+ raise QEDProject::BadLibraryError, "#{lib} is not a valid library" unless QEDProject::Libraries::Base.libs.include? lib
70
+ end
71
+
72
+ self.jammit = options[:jammit]
73
+ self.sass = options[:sass]
74
+ self.coffeescript = options[:coffeescript]
75
+ self.verbose = options[:verbose]
76
+ self.testing = options[:testing]
77
+ end
78
+
79
+ # Set up the basic paths we'll use throughout
80
+ def set_paths
81
+ self.images_path = File.join("public", "images")
82
+ self.js_path = self.jammit ? "javascripts" : File.join("public", "javascripts")
83
+ self.css_path = self.jammit ? "stylesheets" : File.join("public", "stylesheets")
84
+ end
85
+
86
+ # We need a guardfile if the user is using jammit, sass, or coffeescript.
87
+ def needs_guardfile?
88
+ self.jammit || self.sass || self.coffeescript
89
+ end
90
+
91
+ # Only jammit needs a config folder for now.
92
+ def needs_config_folder?
93
+ self.jammit
94
+ end
95
+
96
+ # Start the project generation.
97
+ # Create the folder
98
+ # Get all of the libraries
99
+ # create an assets file if needed
100
+ # add the testing folder if needed
101
+ # create a guardfile if needed
102
+ def generate
103
+ self.create_project_skeleton
104
+ self.create_index
105
+ self.get_libraries if self.libs
106
+ self.create_asset_file if self.jammit
107
+ self.add_testing if self.testing
108
+ self.create_guardfile if self.needs_guardfile?
109
+ self.create_rakefile
110
+ end
111
+
112
+ # includes the Jasmine BDD framework for javascript testing
113
+ def add_testing
114
+ FileUtils.mkdir_p File.join(self.path, "spec"), :verbose => self.verbose
115
+ FileUtils.cp_r File.join(self.vendor_root, "jasmine", "lib"),
116
+ File.join(self.path, "spec", "lib"), :verbose => self.verbose
117
+
118
+
119
+ render_template_to_file "suite.html", File.join(self.path, "spec", "suite.html"), binding
120
+ if self.coffeescript
121
+ render_template_to_file "sampleSpec.coffee", File.join(self.path, "spec", "sampleSpec.coffee"), binding
122
+ else
123
+ render_template_to_file "sampleSpec.js", File.join(self.path, "spec", "sampleSpec.js"), binding
124
+ end
125
+
126
+ end
127
+
128
+ # Set up the main project skeleton
129
+ # project
130
+ # public/
131
+ # assets/ * only with assets
132
+ # images/
133
+ # javascripts/ * only without assets
134
+ # stylesheets/ * only without assets
135
+ # config/
136
+ # assets.yml * optional
137
+ # coffeescripts/ * optional
138
+ # sass/ * optional
139
+ # spec/ * optional
140
+ # Guardfile * optional
141
+ def create_project_skeleton
142
+ ::FileUtils.mkdir_p( self.path, :verbose => self.verbose)
143
+ ::FileUtils.mkdir_p( File.join(self.path, "public"), :verbose => self.verbose)
144
+ ::FileUtils.mkdir_p( File.join(self.path, self.images_path), :verbose => self.verbose)
145
+ ::FileUtils.mkdir_p( File.join(self.path, "tmp"), :verbose => self.verbose)
146
+ ::FileUtils.mkdir_p( File.join(self.path, self.js_path), :verbose => self.verbose)
147
+ ::FileUtils.mkdir_p( File.join(self.path, self.css_path), :verbose => self.verbose)
148
+ ::FileUtils.mkdir_p( File.join(self.path, "config"), :verbose => self.verbose) if self.needs_config_folder?
149
+ ::FileUtils.mkdir_p( File.join(self.path, "coffeescripts"), :verbose => self.verbose) if self.coffeescript
150
+ ::FileUtils.mkdir_p( File.join(self.path, "sass"), :verbose => self.verbose) if self.sass
151
+ end
152
+
153
+ # Loop through the libraries the user added
154
+ # and run their generate methods
155
+ # which pulls in the additional optional files.
156
+ def get_libraries
157
+ libs.each do |lib|
158
+ library = QEDProject::Libraries::Base.libs[lib]
159
+ l = library.new(self)
160
+ l.generate!
161
+ end
162
+ end
163
+
164
+ def create_asset_file
165
+ render_template_to_file("assets.yml", File.join(self.path, "config", "assets.yml"), binding)
166
+ puts "Created #{File.join(self.path, 'config', 'assets.yml')}" if self.verbose
167
+ end
168
+
169
+ def create_guardfile
170
+ render_template_to_file "Guardfile", File.join(self.path, "Guardfile"), binding
171
+ puts "Created #{ File.join(self.path, "Guardfile")}" if self.verbose
172
+ end
173
+
174
+ def create_index
175
+ @project = self
176
+ render_template_to_file "index.html", File.join(self.path, "public", "index.html"), binding
177
+ puts "Created #{ File.join(self.path, "public", "index.html")}" if self.verbose
178
+ end
179
+
180
+ def create_rakefile
181
+ render_template_to_file "Rakefile", File.join(self.path, "Rakefile"), binding
182
+ puts "Created #{ File.join(self.path, "Rakefile")}" if self.verbose
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,3 @@
1
+ module QEDProject
2
+ VERSION = "0.0.4"
3
+ end
data/lib/qedproject.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "qedproject/version"
2
+ require "qedproject/helpers"
3
+ require "qedproject/libraries/base"
4
+ require "qedproject/libraries/jquery"
5
+ require "qedproject/libraries/backbone"
6
+ require "qedproject/libraries/knockout"
7
+ require "qedproject/libraries/jquerytmpl"
8
+ require "qedproject/libraries/skeleton"
9
+ require "qedproject/project"
10
+
11
+ module QEDProject
12
+ class BadLibraryError < RuntimeError
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ <% if self.sass -%>
2
+ <% if self.jammit -%>
3
+ guard "sass", :input => "sass", :output => "tmp"
4
+ <% else -%>
5
+ guard "sass", :input => "sass", :output => "public/stylesheets"
6
+ <% end -%>
7
+ <% end -%>
8
+ <% if self.coffeescript -%>
9
+ <% if self.jammit -%>
10
+ guard "coffeescript", :input => "coffeescripts", :output => "tmp"
11
+ <% else -%>
12
+ guard "coffeescript", :input => "coffeescripts", :output => "public/javascripts"
13
+ <% end -%>
14
+ <% if self.testing -%>
15
+ guard "coffeescript", :input => "spec", :output => "spec"
16
+ <% end -%>
17
+ <% end -%>
18
+ <% if self.jammit -%>
19
+ guard "jammit" do
20
+ watch(/^javascripts\\/(.*)\.js/)
21
+ watch(/^stylesheets\\/(.*)\.css/)
22
+ <% if self.coffeescript -%>
23
+ watch(/^tmp\\/(.*)\\.js/)
24
+ <% end -%>
25
+ <% if self.sass -%>
26
+ watch(/^tmp\\/(.*)\\.css/)
27
+ <% end -%>
28
+ end
29
+ <% end -%>
@@ -0,0 +1,25 @@
1
+ SERVER = "yourhost.com"
2
+ USERNAME = "yourusername"
3
+ REMOTE_FOLDER = "/home/#{USERNAME}/yourhost.com"
4
+
5
+ require 'net/scp'
6
+
7
+ <% if self.needs_guardfile? -%>
8
+ desc "compile CSS, js files"
9
+ task :compile do
10
+ require 'guard'
11
+ Guard.setup
12
+ Guard::Dsl.evaluate_guardfile
13
+ Guard::guards.each{|guard| guard.run_all}
14
+ end
15
+ <% end -%>
16
+ desc "Deploy web site"
17
+ <% if self.needs_guardfile? -%>
18
+ task :deploy => :compile do
19
+ <% else -%>
20
+ task :deploy do
21
+ <% end -%>
22
+ Net::SCP.start(SERVER, USERNAME, {:port => PORT} ) do |scp|
23
+ scp.upload! "public", REMOTE_FOLDER, :recursive => true
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ stylesheets:
2
+ style.css:
3
+ <% self.css_assets.each do |lib| -%>
4
+ - <%=File.join(self.css_path, lib) %>
5
+ <% end -%>
6
+ javascripts:
7
+ app.js:
8
+ <% self.js_assets.each do |lib| -%>
9
+ - <%=File.join(self.js_path, lib) %>
10
+ <% end -%>
@@ -0,0 +1,35 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Project</title>
5
+ <% if @project.jammit %>
6
+ <link rel="stylesheet" href="assets/app.css">
7
+ <% else -%>
8
+ <link rel="stylesheet" href="stylesheets/style.css">
9
+ <% end -%>
10
+
11
+ </head>
12
+ <body>
13
+
14
+ <h1>My Project</h1>
15
+
16
+ <p>
17
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
18
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
19
+ minim veniam, quis nostrud exercitation ullamco laboris nisi ut
20
+ aliquip ex ea commodo consequat. Duis aute irure dolor in
21
+ reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
22
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
23
+ culpa qui officia deserunt mollit anim id est laborum.
24
+ </p>
25
+
26
+
27
+ <% if @project.jammit %>
28
+ <script src="assets/app.js"></script>
29
+ <% else -%>
30
+ <% @project.js_assets.each do |js| -%>
31
+ <script src="javascripts/<%=js %>"></script>
32
+ <% end -%>
33
+ <% end -%>
34
+ </body>
35
+ </html>
File without changes
@@ -0,0 +1,7 @@
1
+ (function() {
2
+ describe("A sample spec", function() {
3
+ return it("performs addition", function() {
4
+ return (expect(1 + 1)).toEqual(3);
5
+ });
6
+ });
7
+ }).call(this);
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Jasmine Test Runner</title>
5
+ <link rel="stylesheet" type="text/css" href="lib/jasmine-1.0.2/jasmine.css">
6
+ <script type="text/javascript" src="lib/jasmine-1.0.2/jasmine.js"></script>
7
+ <script type="text/javascript" src="lib/jasmine-1.0.2/jasmine-html.js"></script>
8
+
9
+ <!-- include source files here... -->
10
+
11
+ <!-- include spec files here... -->
12
+ <script type="text/javascript" src="sampleSpec.js"></script>
13
+
14
+ </head>
15
+ <body>
16
+
17
+ <script type="text/javascript">
18
+ jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
19
+ jasmine.getEnv().execute();
20
+ </script>
21
+
22
+ </body>
23
+ </html>
@@ -0,0 +1 @@
1
+ 0.5.3
@@ -0,0 +1,33 @@
1
+ // Backbone.js 0.5.3
2
+ // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3
+ // Backbone may be freely distributed under the MIT license.
4
+ // For all details and documentation:
5
+ // http://documentcloud.github.com/backbone
6
+ (function(){var h=this,p=h.Backbone,e;e=typeof exports!=="undefined"?exports:h.Backbone={};e.VERSION="0.5.3";var f=h._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var g=h.jQuery||h.Zepto;e.noConflict=function(){h.Backbone=p;return this};e.emulateHTTP=!1;e.emulateJSON=!1;e.Events={bind:function(a,b,c){var d=this._callbacks||(this._callbacks={});(d[a]||(d[a]=[])).push([b,c]);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=
7
+ 0,e=c.length;d<e;d++)if(c[d]&&b===c[d][0]){c[d]=null;break}}else c[a]=[]}else this._callbacks={};return this},trigger:function(a){var b,c,d,e,f=2;if(!(c=this._callbacks))return this;for(;f--;)if(b=f?a:"all",b=c[b])for(var g=0,h=b.length;g<h;g++)(d=b[g])?(e=f?Array.prototype.slice.call(arguments,1):arguments,d[0].apply(d[1]||this,e)):(b.splice(g,1),g--,h--);return this}};e.Model=function(a,b){var c;a||(a={});if(c=this.defaults)f.isFunction(c)&&(c=c.call(this)),a=f.extend({},c,a);this.attributes={};
8
+ this._escapedAttributes={};this.cid=f.uniqueId("c");this.set(a,{silent:!0});this._changed=!1;this._previousAttributes=f.clone(this.attributes);if(b&&b.collection)this.collection=b.collection;this.initialize(a,b)};f.extend(e.Model.prototype,e.Events,{_previousAttributes:null,_changed:!1,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.attributes[a];
9
+ return this._escapedAttributes[a]=(b==null?"":""+b).replace(/&(?!\w+;|#\d+;|#x[\da-f]+;)/gi,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")},has:function(a){return this.attributes[a]!=null},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return!1;if(this.idAttribute in a)this.id=a[this.idAttribute];
10
+ var e=this._changing;this._changing=!0;for(var g in a){var h=a[g];if(!f.isEqual(c[g],h))c[g]=h,delete d[g],this._changed=!0,b.silent||this.trigger("change:"+g,this,h,b)}!e&&!b.silent&&this._changed&&this.change(b);this._changing=!1;return this},unset:function(a,b){if(!(a in this.attributes))return this;b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&&!this._performValidation(c,b))return!1;delete this.attributes[a];delete this._escapedAttributes[a];a==this.idAttribute&&delete this.id;this._changed=
11
+ !0;b.silent||(this.trigger("change:"+a,this,void 0,b),this.change(b));return this},clear:function(a){a||(a={});var b,c=this.attributes,d={};for(b in c)d[b]=void 0;if(!a.silent&&this.validate&&!this._performValidation(d,a))return!1;this.attributes={};this._escapedAttributes={};this._changed=!0;if(!a.silent){for(b in c)this.trigger("change:"+b,this,void 0,a);this.change(a)}return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&
12
+ c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return!1;var c=this,d=b.success;b.success=function(a,e,f){if(!c.set(c.parse(a,f),b))return!1;d&&d(c,a,f)};b.error=i(b.error,c,b);var f=this.isNew()?"create":"update";return(this.sync||e.sync).call(this,f,this,b)},destroy:function(a){a||(a={});if(this.isNew())return this.trigger("destroy",this,this.collection,a);var b=this,c=a.success;a.success=function(d){b.trigger("destroy",
13
+ b,b.collection,a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"delete",this,a)},url:function(){var a=k(this.collection)||this.urlRoot||l();if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return this.id==null},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=!1},hasChanged:function(a){if(a)return this._previousAttributes[a]!=
14
+ this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=!1,d;for(d in a)f.isEqual(b[d],a[d])||(c=c||{},c[d]=a[d]);return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c)return b.error?b.error(this,c,b):this.trigger("error",this,c,b),!1;return!0}});
15
+ e.Collection=function(a,b){b||(b={});if(b.comparator)this.comparator=b.comparator;f.bindAll(this,"_onModelEvent","_removeReference");this._reset();a&&this.reset(a,{silent:!0});this.initialize.apply(this,arguments)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c<d;c++)this._add(a[c],b);else this._add(a,b);return this},remove:function(a,b){if(f.isArray(a))for(var c=
16
+ 0,d=a.length;c<d;c++)this._remove(a[c],b);else this._remove(a,b);return this},get:function(a){if(a==null)return null;return this._byId[a.id!=null?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");this.models=this.sortBy(this.comparator);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},
17
+ reset:function(a,b){a||(a=[]);b||(b={});this.each(this._removeReference);this._reset();this.add(a,{silent:!0});b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a||(a={});var b=this,c=a.success;a.success=function(d,f,e){b[a.add?"add":"reset"](b.parse(d,e),a);c&&c(b,d)};a.error=i(a.error,b,a);return(this.sync||e.sync).call(this,"read",this,a)},create:function(a,b){var c=this;b||(b={});a=this._prepareModel(a,b);if(!a)return!1;var d=b.success;b.success=function(a,e,f){c.add(a,b);
18
+ d&&d(a,e,f)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId={};this._byCid={}},_prepareModel:function(a,b){if(a instanceof e.Model){if(!a.collection)a.collection=this}else{var c=a;a=new this.model(c,{collection:this});a.validate&&!a._performValidation(c,b)&&(a=!1)}return a},_add:function(a,b){b||(b={});a=this._prepareModel(a,b);if(!a)return!1;var c=this.getByCid(a);if(c)throw Error(["Can't add the same model to a set twice",
19
+ c.id]);this._byId[a.id]=a;this._byCid[a.cid]=a;this.models.splice(b.at!=null?b.at:this.comparator?this.sortedIndex(a,this.comparator):this.length,0,a);a.bind("all",this._onModelEvent);this.length++;b.silent||a.trigger("add",a,this,b);return a},_remove:function(a,b){b||(b={});a=this.getByCid(a)||this.get(a);if(!a)return null;delete this._byId[a.id];delete this._byCid[a.cid];this.models.splice(this.indexOf(a),1);this.length--;b.silent||a.trigger("remove",a,this,b);this._removeReference(a);return a},
20
+ _removeReference:function(a){this==a.collection&&delete a.collection;a.unbind("all",this._onModelEvent)},_onModelEvent:function(a,b,c,d){(a=="add"||a=="remove")&&c!=this||(a=="destroy"&&this._remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,arguments))}});f.each(["forEach","each","map","reduce","reduceRight","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max",
21
+ "min","sortBy","sortedIndex","toArray","size","first","rest","last","without","indexOf","lastIndexOf","isEmpty","groupBy"],function(a){e.Collection.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});e.Router=function(a){a||(a={});if(a.routes)this.routes=a.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var q=/:([\w\d]+)/g,r=/\*([\w\d]+)/g,s=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(e.Router.prototype,e.Events,{initialize:function(){},route:function(a,
22
+ b,c){e.history||(e.history=new e.History);f.isRegExp(a)||(a=this._routeToRegExp(a));e.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d))},this))},navigate:function(a,b){e.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(s,"\\$&").replace(q,
23
+ "([^/]*)").replace(r,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});e.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")};var j=/^#*/,t=/msie [\w.]+/,m=!1;f.extend(e.History.prototype,{interval:50,getFragment:function(a,b){if(a==null)if(this._hasPushState||b){a=window.location.pathname;var c=window.location.search;c&&(a+=c);a.indexOf(this.options.root)==0&&(a=a.substr(this.options.root.length))}else a=window.location.hash;return decodeURIComponent(a.replace(j,
24
+ ""))},start:function(a){if(m)throw Error("Backbone.history has already been started");this.options=f.extend({},{root:"/"},this.options,a);this._wantsPushState=!!this.options.pushState;this._hasPushState=!(!this.options.pushState||!window.history||!window.history.pushState);a=this.getFragment();var b=document.documentMode;if(b=t.exec(navigator.userAgent.toLowerCase())&&(!b||b<=7))this.iframe=g('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);
25
+ this._hasPushState?g(window).bind("popstate",this.checkUrl):"onhashchange"in window&&!b?g(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);this.fragment=a;m=!0;a=window.location;b=a.pathname==this.options.root;if(this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;else if(this._wantsPushState&&this._hasPushState&&b&&a.hash)this.fragment=a.hash.replace(j,""),window.history.replaceState({},
26
+ document.title,a.protocol+"//"+a.host+this.options.root+this.fragment);if(!this.options.silent)return this.loadUrl()},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.iframe.location.hash));if(a==this.fragment||a==decodeURIComponent(this.fragment))return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(window.location.hash)},loadUrl:function(a){var b=this.fragment=this.getFragment(a);
27
+ return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){var c=(a||"").replace(j,"");if(!(this.fragment==c||this.fragment==decodeURIComponent(c))){if(this._hasPushState){var d=window.location;c.indexOf(this.options.root)!=0&&(c=this.options.root+c);this.fragment=c;window.history.pushState({},document.title,d.protocol+"//"+d.host+c)}else if(window.location.hash=this.fragment=c,this.iframe&&c!=this.getFragment(this.iframe.location.hash))this.iframe.document.open().close(),
28
+ this.iframe.location.hash=c;b&&this.loadUrl(a)}}});e.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize.apply(this,arguments)};var u=/^(\S+)\s*(.*)$/,n=["model","collection","el","id","attributes","className","tagName"];f.extend(e.View.prototype,e.Events,{tagName:"div",$:function(a){return g(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){g(this.el).remove();return this},make:function(a,
29
+ b,c){a=document.createElement(a);b&&g(a).attr(b);c&&g(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events))for(var b in f.isFunction(a)&&(a=a.call(this)),g(this.el).unbind(".delegateEvents"+this.cid),a){var c=this[a[b]];if(!c)throw Error('Event "'+a[b]+'" does not exist');var d=b.match(u),e=d[1];d=d[2];c=f.bind(c,this);e+=".delegateEvents"+this.cid;d===""?g(this.el).bind(e,c):g(this.el).delegate(d,e,c)}},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=
30
+ 0,c=n.length;b<c;b++){var d=n[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el){if(f.isString(this.el))this.el=g(this.el).get(0)}else{var a=this.attributes||{};if(this.id)a.id=this.id;if(this.className)a["class"]=this.className;this.el=this.make(this.tagName,a)}}});e.Model.extend=e.Collection.extend=e.Router.extend=e.View.extend=function(a,b){var c=v(this,a,b);c.extend=this.extend;return c};var w={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};e.sync=function(a,
31
+ b,c){var d=w[a];c=f.extend({type:d,dataType:"json"},c);if(!c.url)c.url=k(b)||l();if(!c.data&&b&&(a=="create"||a=="update"))c.contentType="application/json",c.data=JSON.stringify(b.toJSON());if(e.emulateJSON)c.contentType="application/x-www-form-urlencoded",c.data=c.data?{model:c.data}:{};if(e.emulateHTTP&&(d==="PUT"||d==="DELETE")){if(e.emulateJSON)c.data._method=d;c.type="POST";c.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)}}if(c.type!=="GET"&&!e.emulateJSON)c.processData=
32
+ !1;return g.ajax(c)};var o=function(){},v=function(a,b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){return a.apply(this,arguments)};f.extend(d,a);o.prototype=a.prototype;d.prototype=new o;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},k=function(a){if(!a||!a.url)return null;return f.isFunction(a.url)?a.url():a.url},l=function(){throw Error('A "url" property or function must be specified');},i=function(a,b,c){return function(d){a?
33
+ a(b,d,c):b.trigger("error",b,d,c)}}}).call(this);