├── spec
├── dummy
│ ├── log
│ │ └── .gitkeep
│ ├── db
│ │ └── test.sqlite3
│ ├── app
│ │ ├── mailers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── assets
│ │ │ ├── javascripts
│ │ │ │ ├── templates
│ │ │ │ │ └── test.tmpl
│ │ │ │ └── application.js
│ │ │ └── stylesheets
│ │ │ │ └── application.css
│ │ ├── controllers
│ │ │ ├── main_controller.rb
│ │ │ └── application_controller.rb
│ │ └── views
│ │ │ ├── main
│ │ │ ├── prefix.html.erb
│ │ │ └── index.html.erb
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── lib
│ │ └── assets
│ │ │ └── .gitkeep
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 500.html
│ │ ├── 422.html
│ │ └── 404.html
│ ├── config
│ │ ├── routes.rb
│ │ ├── environment.rb
│ │ ├── locales
│ │ │ └── en.yml
│ │ ├── initializers
│ │ │ ├── mime_types.rb
│ │ │ ├── backtrace_silencers.rb
│ │ │ ├── session_store.rb
│ │ │ ├── secret_token.rb
│ │ │ ├── wrap_parameters.rb
│ │ │ └── inflections.rb
│ │ ├── boot.rb
│ │ ├── database.yml
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── test.rb
│ │ │ └── production.rb
│ │ └── application.rb
│ ├── config.ru
│ ├── Rakefile
│ ├── script
│ │ └── rails
│ └── README.rdoc
├── spec_helper.rb
├── integration_spec.rb
└── jquery-tmpl-rails
│ └── jquery_template_spec.rb
├── Gemfile
├── .gitignore
├── lib
├── jquery-tmpl-rails.rb
└── jquery-tmpl-rails
│ ├── version.rb
│ ├── engine.rb
│ └── jquery_template.rb
├── Rakefile
├── jquery-tmpl-rails.gemspec
├── LICENSE
├── README.md
└── vendor
└── assets
└── javascripts
└── jquery-tmpl.js
/spec/dummy/log/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/db/test.sqlite3:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/lib/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/spec/dummy/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gemspec
4 |
5 | gem 'sqlite3'
6 | gem 'jquery-rails'
7 |
--------------------------------------------------------------------------------
/spec/dummy/app/assets/javascripts/templates/test.tmpl:
--------------------------------------------------------------------------------
1 |
${test}<\\/div>\\n");}
13 | end
14 |
15 | context "when config.template_prefix is set" do
16 | it "removes the prefix from the template name" do
17 | Rails.configuration.jquery_templates.prefix = "templates"
18 | template = Rails.application.assets["templates/test"]
19 | template.to_s.should include('"test"')
20 | end
21 |
22 | it "normalizes template prefixes by removing extraneous slashes" do
23 | Rails.configuration.jquery_templates.prefix = "/templates/"
24 | template = Rails.application.assets["templates/test"]
25 | template.to_s.should include('"test"')
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger
20 | config.active_support.deprecation = :log
21 |
22 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Raise exception on mass assignment protection for Active Record models
26 | config.active_record.mass_assignment_sanitizer = :strict
27 |
28 | # Log the query plan for queries taking more than this (works
29 | # with SQLite, MySQL, and PostgreSQL)
30 | config.active_record.auto_explain_threshold_in_seconds = 0.5
31 |
32 | # Do not compress assets
33 | config.assets.compress = false
34 |
35 | # Expands the lines which load the assets
36 | config.assets.debug = true
37 | end
38 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.serve_static_assets = true
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Log error messages when you accidentally call methods on nil
15 | config.whiny_nils = true
16 |
17 | # Show full error reports and disable caching
18 | config.consider_all_requests_local = true
19 | config.action_controller.perform_caching = false
20 |
21 | # Raise exceptions instead of rendering exception templates
22 | config.action_dispatch.show_exceptions = false
23 |
24 | # Disable request forgery protection in test environment
25 | config.action_controller.allow_forgery_protection = false
26 |
27 | # Tell Action Mailer not to deliver emails to the real world.
28 | # The :test delivery method accumulates sent emails in the
29 | # ActionMailer::Base.deliveries array.
30 | config.action_mailer.delivery_method = :test
31 |
32 | # Raise exception on mass assignment protection for Active Record models
33 | config.active_record.mass_assignment_sanitizer = :strict
34 |
35 | # Print deprecation notices to the stderr
36 | config.active_support.deprecation = :stderr
37 | end
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://codeclimate.com/github/jimmycuadra/jquery-tmpl-rails) [](http://coderwall.com/jimmycuadra)
2 |
3 | # jquery-tmpl-rails
4 |
5 | This gem adds the jQuery Templates plugin and a corresponding Sprockets engine to the asset pipeline in Rails >= 3.1 applications.
6 |
7 | ## Installation
8 |
9 | Add it to your Gemfile and run `bundle`.
10 |
11 | ## Usage
12 |
13 | jQuery templates will be recognized by Sprockets with the `.tmpl` extension. Place them anywhere in the Sprockets load path.
14 |
15 | ```html
16 |
17 |
${name}
18 | ```
19 |
20 | In your application's JavaScript manifest file, require the jQuery Templates plugin followed by your templates. The templates are compiled and named with their Sprockets logical path:
21 |
22 | ```javascript
23 | //= require jquery-tmpl
24 | //= require_tree ./templates
25 |
26 | $.tmpl("templates/author", { name: "Jimmy" }).appendTo("#author");
27 | ```
28 |
29 | ## Configuration
30 |
31 | If the path to all of your templates have a common prefix that you prefer is not included in the template's name, you can set this option in `config/application.rb`:
32 |
33 | ```ruby
34 | config.jquery_templates.prefix = "templates"
35 | ```
36 |
37 | That would change the previous example to this:
38 |
39 | ```javascript
40 | $.tmpl("author", { name: "Jimmy" }).appendTo("#author");
41 | ```
42 |
43 | The prefix can also be a regular expression. For example, to use only the name of the file for the template name, regardless of directory structure:
44 |
45 | ```ruby
46 | config.jquery_templates.prefix = %r{([^/]*/)*}
47 | ```
48 |
49 | Happy templating!
50 |
51 | ## Acknowledgements
52 |
53 | The Sprockets engine was originally derived from the [sprockets-jquery-tmpl](https://github.com/rdy/sprockets-jquery-tmpl) gem. If you want a similar mechanism for use outside of Rails, take a look at this project.
54 |
--------------------------------------------------------------------------------
/spec/dummy/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | Bundler.require
6 | require "jquery-tmpl-rails"
7 |
8 | module Dummy
9 | class Application < Rails::Application
10 | # Settings in config/environments/* take precedence over those specified here.
11 | # Application configuration should go into files in config/initializers
12 | # -- all .rb files in that directory are automatically loaded.
13 |
14 | # Custom directories with classes and modules you want to be autoloadable.
15 | # config.autoload_paths += %W(#{config.root}/extras)
16 |
17 | # Only load the plugins named here, in the order given (default is alphabetical).
18 | # :all can be used as a placeholder for all plugins not explicitly named.
19 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
20 |
21 | # Activate observers that should always be running.
22 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
23 |
24 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
25 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
26 | # config.time_zone = 'Central Time (US & Canada)'
27 |
28 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
29 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
30 | # config.i18n.default_locale = :de
31 |
32 | # Configure the default encoding used in templates for Ruby 1.9.
33 | config.encoding = "utf-8"
34 |
35 | # Configure sensitive parameters which will be filtered from the log file.
36 | config.filter_parameters += [:password]
37 |
38 | # Use SQL instead of Active Record's schema dumper when creating the database.
39 | # This is necessary if your schema can't be completely dumped by the schema dumper,
40 | # like if you have constraints or database-specific column types
41 | # config.active_record.schema_format = :sql
42 |
43 | # Enforce whitelist mode for mass assignment.
44 | # This will create an empty whitelist of attributes available for mass-assignment for all models
45 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
46 | # parameters by using an attr_accessible or attr_protected declaration.
47 | # config.active_record.whitelist_attributes = true
48 |
49 | # Enable the asset pipeline
50 | config.assets.enabled = true
51 |
52 | # Version of your assets, change this if you want to expire all your assets
53 | config.assets.version = '1.0'
54 | end
55 | end
56 |
57 |
--------------------------------------------------------------------------------
/spec/dummy/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Dummy::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # Code is not reloaded between requests
5 | config.cache_classes = true
6 |
7 | # Full error reports are disabled and caching is turned on
8 | config.consider_all_requests_local = false
9 | config.action_controller.perform_caching = true
10 |
11 | # Disable Rails's static asset server (Apache or nginx will already do this)
12 | config.serve_static_assets = false
13 |
14 | # Compress JavaScripts and CSS
15 | config.assets.compress = true
16 |
17 | # Don't fallback to assets pipeline if a precompiled asset is missed
18 | config.assets.compile = false
19 |
20 | # Generate digests for assets URLs
21 | config.assets.digest = true
22 |
23 | # Defaults to Rails.root.join("public/assets")
24 | # config.assets.manifest = YOUR_PATH
25 |
26 | # Specifies the header that your server uses for sending files
27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | # config.force_ssl = true
32 |
33 | # See everything in the log (default is :info)
34 | # config.log_level = :debug
35 |
36 | # Prepend all log lines with the following tags
37 | # config.log_tags = [ :subdomain, :uuid ]
38 |
39 | # Use a different logger for distributed setups
40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41 |
42 | # Use a different cache store in production
43 | # config.cache_store = :mem_cache_store
44 |
45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
46 | # config.action_controller.asset_host = "http://assets.example.com"
47 |
48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49 | # config.assets.precompile += %w( search.js )
50 |
51 | # Disable delivery errors, bad email addresses will be ignored
52 | # config.action_mailer.raise_delivery_errors = false
53 |
54 | # Enable threaded mode
55 | # config.threadsafe!
56 |
57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 | # the I18n.default_locale when a translation can not be found)
59 | config.i18n.fallbacks = true
60 |
61 | # Send deprecation notices to registered listeners
62 | config.active_support.deprecation = :notify
63 |
64 | # Log the query plan for queries taking more than this (works
65 | # with SQLite, MySQL, and PostgreSQL)
66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5
67 | end
68 |
--------------------------------------------------------------------------------
/spec/dummy/README.rdoc:
--------------------------------------------------------------------------------
1 | == Welcome to Rails
2 |
3 | Rails is a web-application framework that includes everything needed to create
4 | database-backed web applications according to the Model-View-Control pattern.
5 |
6 | This pattern splits the view (also called the presentation) into "dumb"
7 | templates that are primarily responsible for inserting pre-built data in between
8 | HTML tags. The model contains the "smart" domain objects (such as Account,
9 | Product, Person, Post) that holds all the business logic and knows how to
10 | persist themselves to a database. The controller handles the incoming requests
11 | (such as Save New Account, Update Product, Show Post) by manipulating the model
12 | and directing data to the view.
13 |
14 | In Rails, the model is handled by what's called an object-relational mapping
15 | layer entitled Active Record. This layer allows you to present the data from
16 | database rows as objects and embellish these data objects with business logic
17 | methods. You can read more about Active Record in
18 | link:files/vendor/rails/activerecord/README.html.
19 |
20 | The controller and view are handled by the Action Pack, which handles both
21 | layers by its two parts: Action View and Action Controller. These two layers
22 | are bundled in a single package due to their heavy interdependence. This is
23 | unlike the relationship between the Active Record and Action Pack that is much
24 | more separate. Each of these packages can be used independently outside of
25 | Rails. You can read more about Action Pack in
26 | link:files/vendor/rails/actionpack/README.html.
27 |
28 |
29 | == Getting Started
30 |
31 | 1. At the command prompt, create a new Rails application:
32 |
rails new myapp (where
myapp is the application name)
33 |
34 | 2. Change directory to
myapp and start the web server:
35 |
cd myapp; rails server (run with --help for options)
36 |
37 | 3. Go to http://localhost:3000/ and you'll see:
38 | "Welcome aboard: You're riding Ruby on Rails!"
39 |
40 | 4. Follow the guidelines to start developing your application. You can find
41 | the following resources handy:
42 |
43 | * The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
44 | * Ruby on Rails Tutorial Book: http://www.railstutorial.org/
45 |
46 |
47 | == Debugging Rails
48 |
49 | Sometimes your application goes wrong. Fortunately there are a lot of tools that
50 | will help you debug it and get it back on the rails.
51 |
52 | First area to check is the application log files. Have "tail -f" commands
53 | running on the server.log and development.log. Rails will automatically display
54 | debugging and runtime information to these files. Debugging info will also be
55 | shown in the browser on requests from 127.0.0.1.
56 |
57 | You can also log your own messages directly into the log file from your code
58 | using the Ruby logger class from inside your controllers. Example:
59 |
60 | class WeblogController < ActionController::Base
61 | def destroy
62 | @weblog = Weblog.find(params[:id])
63 | @weblog.destroy
64 | logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
65 | end
66 | end
67 |
68 | The result will be a message in your log file along the lines of:
69 |
70 | Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
71 |
72 | More information on how to use the logger is at http://www.ruby-doc.org/core/
73 |
74 | Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
75 | several books available online as well:
76 |
77 | * Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
78 | * Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
79 |
80 | These two books will bring you up to speed on the Ruby language and also on
81 | programming in general.
82 |
83 |
84 | == Debugger
85 |
86 | Debugger support is available through the debugger command when you start your
87 | Mongrel or WEBrick server with --debugger. This means that you can break out of
88 | execution at any point in the code, investigate and change the model, and then,
89 | resume execution! You need to install ruby-debug to run the server in debugging
90 | mode. With gems, use
sudo gem install ruby-debug. Example:
91 |
92 | class WeblogController < ActionController::Base
93 | def index
94 | @posts = Post.all
95 | debugger
96 | end
97 | end
98 |
99 | So the controller will accept the action, run the first line, then present you
100 | with a IRB prompt in the server window. Here you can do things like:
101 |
102 | >> @posts.inspect
103 | => "[#
nil, "body"=>nil, "id"=>"1"}>,
105 | #"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
107 | >> @posts.first.title = "hello from a debugger"
108 | => "hello from a debugger"
109 |
110 | ...and even better, you can examine how your runtime objects actually work:
111 |
112 | >> f = @posts.first
113 | => #nil, "body"=>nil, "id"=>"1"}>
114 | >> f.
115 | Display all 152 possibilities? (y or n)
116 |
117 | Finally, when you're ready to resume execution, you can enter "cont".
118 |
119 |
120 | == Console
121 |
122 | The console is a Ruby shell, which allows you to interact with your
123 | application's domain model. Here you'll have all parts of the application
124 | configured, just like it is when the application is running. You can inspect
125 | domain models, change values, and save to the database. Starting the script
126 | without arguments will launch it in the development environment.
127 |
128 | To start the console, run rails console from the application
129 | directory.
130 |
131 | Options:
132 |
133 | * Passing the -s, --sandbox argument will rollback any modifications
134 | made to the database.
135 | * Passing an environment name as an argument will load the corresponding
136 | environment. Example: rails console production.
137 |
138 | To reload your controllers and models after launching the console run
139 | reload!
140 |
141 | More information about irb can be found at:
142 | link:http://www.rubycentral.org/pickaxe/irb.html
143 |
144 |
145 | == dbconsole
146 |
147 | You can go to the command line of your database directly through rails
148 | dbconsole. You would be connected to the database with the credentials
149 | defined in database.yml. Starting the script without arguments will connect you
150 | to the development database. Passing an argument will connect you to a different
151 | database, like rails dbconsole production. Currently works for MySQL,
152 | PostgreSQL and SQLite 3.
153 |
154 | == Description of Contents
155 |
156 | The default directory structure of a generated Ruby on Rails application:
157 |
158 | |-- app
159 | | |-- assets
160 | | |-- images
161 | | |-- javascripts
162 | | `-- stylesheets
163 | | |-- controllers
164 | | |-- helpers
165 | | |-- mailers
166 | | |-- models
167 | | `-- views
168 | | `-- layouts
169 | |-- config
170 | | |-- environments
171 | | |-- initializers
172 | | `-- locales
173 | |-- db
174 | |-- doc
175 | |-- lib
176 | | `-- tasks
177 | |-- log
178 | |-- public
179 | |-- script
180 | |-- test
181 | | |-- fixtures
182 | | |-- functional
183 | | |-- integration
184 | | |-- performance
185 | | `-- unit
186 | |-- tmp
187 | | |-- cache
188 | | |-- pids
189 | | |-- sessions
190 | | `-- sockets
191 | `-- vendor
192 | |-- assets
193 | `-- stylesheets
194 | `-- plugins
195 |
196 | app
197 | Holds all the code that's specific to this particular application.
198 |
199 | app/assets
200 | Contains subdirectories for images, stylesheets, and JavaScript files.
201 |
202 | app/controllers
203 | Holds controllers that should be named like weblogs_controller.rb for
204 | automated URL mapping. All controllers should descend from
205 | ApplicationController which itself descends from ActionController::Base.
206 |
207 | app/models
208 | Holds models that should be named like post.rb. Models descend from
209 | ActiveRecord::Base by default.
210 |
211 | app/views
212 | Holds the template files for the view that should be named like
213 | weblogs/index.html.erb for the WeblogsController#index action. All views use
214 | eRuby syntax by default.
215 |
216 | app/views/layouts
217 | Holds the template files for layouts to be used with views. This models the
218 | common header/footer method of wrapping views. In your views, define a layout
219 | using the layout :default and create a file named default.html.erb.
220 | Inside default.html.erb, call <% yield %> to render the view using this
221 | layout.
222 |
223 | app/helpers
224 | Holds view helpers that should be named like weblogs_helper.rb. These are
225 | generated for you automatically when using generators for controllers.
226 | Helpers can be used to wrap functionality for your views into methods.
227 |
228 | config
229 | Configuration files for the Rails environment, the routing map, the database,
230 | and other dependencies.
231 |
232 | db
233 | Contains the database schema in schema.rb. db/migrate contains all the
234 | sequence of Migrations for your schema.
235 |
236 | doc
237 | This directory is where your application documentation will be stored when
238 | generated using rake doc:app
239 |
240 | lib
241 | Application specific libraries. Basically, any kind of custom code that
242 | doesn't belong under controllers, models, or helpers. This directory is in
243 | the load path.
244 |
245 | public
246 | The directory available for the web server. Also contains the dispatchers and the
247 | default HTML files. This should be set as the DOCUMENT_ROOT of your web
248 | server.
249 |
250 | script
251 | Helper scripts for automation and generation.
252 |
253 | test
254 | Unit and functional tests along with fixtures. When using the rails generate
255 | command, template test files will be generated for you and placed in this
256 | directory.
257 |
258 | vendor
259 | External libraries that the application depends on. Also includes the plugins
260 | subdirectory. If the app has frozen rails, those gems also go here, under
261 | vendor/rails/. This directory is in the load path.
262 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/jquery-tmpl.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery Templating Plugin
3 | * Copyright 2010, John Resig
4 | * Dual licensed under the MIT or GPL Version 2 licenses.
5 | */
6 | ;(function( jQuery, undefined ){
7 | var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
8 | newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
9 |
10 | function newTmplItem( options, parentItem, fn, data ) {
11 | // Returns a template item data structure for a new rendered instance of a template (a 'template item').
12 | // The content field is a hierarchical array of strings and nested items (to be
13 | // removed and replaced by nodes field of dom elements, once inserted in DOM).
14 | var newItem = {
15 | data: data || (parentItem ? parentItem.data : {}),
16 | _wrap: parentItem ? parentItem._wrap : null,
17 | tmpl: null,
18 | parent: parentItem || null,
19 | nodes: [],
20 | calls: tiCalls,
21 | nest: tiNest,
22 | wrap: tiWrap,
23 | html: tiHtml,
24 | update: tiUpdate
25 | };
26 | if ( options ) {
27 | jQuery.extend( newItem, options, { nodes: [], parent: parentItem } );
28 | }
29 | if ( fn ) {
30 | // Build the hierarchical content to be used during insertion into DOM
31 | newItem.tmpl = fn;
32 | newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
33 | newItem.key = ++itemKey;
34 | // Keep track of new template item, until it is stored as jQuery Data on DOM element
35 | (stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
36 | }
37 | return newItem;
38 | }
39 |
40 | // Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
41 | jQuery.each({
42 | appendTo: "append",
43 | prependTo: "prepend",
44 | insertBefore: "before",
45 | insertAfter: "after",
46 | replaceAll: "replaceWith"
47 | }, function( name, original ) {
48 | jQuery.fn[ name ] = function( selector ) {
49 | var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
50 | parent = this.length === 1 && this[0].parentNode;
51 |
52 | appendToTmplItems = newTmplItems || {};
53 | if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
54 | insert[ original ]( this[0] );
55 | ret = this;
56 | } else {
57 | for ( i = 0, l = insert.length; i < l; i++ ) {
58 | cloneIndex = i;
59 | elems = (i > 0 ? this.clone(true) : this).get();
60 | jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
61 | ret = ret.concat( elems );
62 | }
63 | cloneIndex = 0;
64 | ret = this.pushStack( ret, name, insert.selector );
65 | }
66 | tmplItems = appendToTmplItems;
67 | appendToTmplItems = null;
68 | jQuery.tmpl.complete( tmplItems );
69 | return ret;
70 | };
71 | });
72 |
73 | jQuery.fn.extend({
74 | // Use first wrapped element as template markup.
75 | // Return wrapped set of template items, obtained by rendering template against data.
76 | tmpl: function( data, options, parentItem ) {
77 | return jQuery.tmpl( this[0], data, options, parentItem );
78 | },
79 |
80 | // Find which rendered template item the first wrapped DOM element belongs to
81 | tmplItem: function() {
82 | return jQuery.tmplItem( this[0] );
83 | },
84 |
85 | // Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
86 | template: function( name ) {
87 | return jQuery.template( name, this[0] );
88 | },
89 |
90 | domManip: function( args, table, callback, options ) {
91 | // This appears to be a bug in the appendTo, etc. implementation
92 | // it should be doing .call() instead of .apply(). See #6227
93 | if ( args[0] && args[0].nodeType ) {
94 | var dmArgs = jQuery.makeArray( arguments ), argsLength = args.length, i = 0, tmplItem;
95 | while ( i < argsLength && !(tmplItem = jQuery.data( args[i++], "tmplItem" ))) {}
96 | if ( argsLength > 1 ) {
97 | dmArgs[0] = [jQuery.makeArray( args )];
98 | }
99 | if ( tmplItem && cloneIndex ) {
100 | dmArgs[2] = function( fragClone ) {
101 | // Handler called by oldManip when rendered template has been inserted into DOM.
102 | jQuery.tmpl.afterManip( this, fragClone, callback );
103 | };
104 | }
105 | oldManip.apply( this, dmArgs );
106 | } else {
107 | oldManip.apply( this, arguments );
108 | }
109 | cloneIndex = 0;
110 | if ( !appendToTmplItems ) {
111 | jQuery.tmpl.complete( newTmplItems );
112 | }
113 | return this;
114 | }
115 | });
116 |
117 | jQuery.extend({
118 | // Return wrapped set of template items, obtained by rendering template against data.
119 | tmpl: function( tmpl, data, options, parentItem ) {
120 | var ret, topLevel = !parentItem;
121 | if ( topLevel ) {
122 | // This is a top-level tmpl call (not from a nested template using {{tmpl}})
123 | parentItem = topTmplItem;
124 | tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
125 | wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
126 | } else if ( !tmpl ) {
127 | // The template item is already associated with DOM - this is a refresh.
128 | // Re-evaluate rendered template for the parentItem
129 | tmpl = parentItem.tmpl;
130 | newTmplItems[parentItem.key] = parentItem;
131 | parentItem.nodes = [];
132 | if ( parentItem.wrapped ) {
133 | updateWrapped( parentItem, parentItem.wrapped );
134 | }
135 | // Rebuild, without creating a new template item
136 | return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
137 | }
138 | if ( !tmpl ) {
139 | return []; // Could throw...
140 | }
141 | if ( typeof data === "function" ) {
142 | data = data.call( parentItem || {} );
143 | }
144 | if ( options && options.wrapped ) {
145 | updateWrapped( options, options.wrapped );
146 | }
147 | ret = jQuery.isArray( data ) ?
148 | jQuery.map( data, function( dataItem ) {
149 | return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
150 | }) :
151 | [ newTmplItem( options, parentItem, tmpl, data ) ];
152 | return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
153 | },
154 |
155 | // Return rendered template item for an element.
156 | tmplItem: function( elem ) {
157 | var tmplItem;
158 | if ( elem instanceof jQuery ) {
159 | elem = elem[0];
160 | }
161 | while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
162 | return tmplItem || topTmplItem;
163 | },
164 |
165 | // Set:
166 | // Use $.template( name, tmpl ) to cache a named template,
167 | // where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
168 | // Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
169 |
170 | // Get:
171 | // Use $.template( name ) to access a cached template.
172 | // Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
173 | // will return the compiled template, without adding a name reference.
174 | // If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
175 | // to $.template( null, templateString )
176 | template: function( name, tmpl ) {
177 | if (tmpl) {
178 | // Compile template and associate with name
179 | if ( typeof tmpl === "string" ) {
180 | // This is an HTML string being passed directly in.
181 | tmpl = buildTmplFn( tmpl )
182 | } else if ( tmpl instanceof jQuery ) {
183 | tmpl = tmpl[0] || {};
184 | }
185 | if ( tmpl.nodeType ) {
186 | // If this is a template block, use cached copy, or generate tmpl function and cache.
187 | tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
188 | }
189 | return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
190 | }
191 | // Return named compiled template
192 | return name ? (typeof name !== "string" ? jQuery.template( null, name ):
193 | (jQuery.template[name] ||
194 | // If not in map, treat as a selector. (If integrated with core, use quickExpr.exec)
195 | jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
196 | },
197 |
198 | encode: function( text ) {
199 | // Do HTML encoding replacing < > & and ' and " by corresponding entities.
200 | return ("" + text).split("<").join("<").split(">").join(">").split('"').join(""").split("'").join("'");
201 | }
202 | });
203 |
204 | jQuery.extend( jQuery.tmpl, {
205 | tag: {
206 | "tmpl": {
207 | _default: { $2: "null" },
208 | open: "if($notnull_1){_=_.concat($item.nest($1,$2));}"
209 | // tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
210 | // This means that {{tmpl foo}} treats foo as a template (which IS a function).
211 | // Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
212 | },
213 | "wrap": {
214 | _default: { $2: "null" },
215 | open: "$item.calls(_,$1,$2);_=[];",
216 | close: "call=$item.calls();_=call._.concat($item.wrap(call,_));"
217 | },
218 | "each": {
219 | _default: { $2: "$index, $value" },
220 | open: "if($notnull_1){$.each($1a,function($2){with(this){",
221 | close: "}});}"
222 | },
223 | "if": {
224 | open: "if(($notnull_1) && $1a){",
225 | close: "}"
226 | },
227 | "else": {
228 | _default: { $1: "true" },
229 | open: "}else if(($notnull_1) && $1a){"
230 | },
231 | "html": {
232 | // Unecoded expression evaluation.
233 | open: "if($notnull_1){_.push($1a);}"
234 | },
235 | "=": {
236 | // Encoded expression evaluation. Abbreviated form is ${}.
237 | _default: { $1: "$data" },
238 | open: "if($notnull_1){_.push($.encode($1a));}"
239 | },
240 | "!": {
241 | // Comment tag. Skipped by parser
242 | open: ""
243 | }
244 | },
245 |
246 | // This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
247 | complete: function( items ) {
248 | newTmplItems = {};
249 | },
250 |
251 | // Call this from code which overrides domManip, or equivalent
252 | // Manage cloning/storing template items etc.
253 | afterManip: function afterManip( elem, fragClone, callback ) {
254 | // Provides cloned fragment ready for fixup prior to and after insertion into DOM
255 | var content = fragClone.nodeType === 11 ?
256 | jQuery.makeArray(fragClone.childNodes) :
257 | fragClone.nodeType === 1 ? [fragClone] : [];
258 |
259 | // Return fragment to original caller (e.g. append) for DOM insertion
260 | callback.call( elem, fragClone );
261 |
262 | // Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
263 | storeTmplItems( content );
264 | cloneIndex++;
265 | }
266 | });
267 |
268 | //========================== Private helper functions, used by code above ==========================
269 |
270 | function build( tmplItem, nested, content ) {
271 | // Convert hierarchical content into flat string array
272 | // and finally return array of fragments ready for DOM insertion
273 | var frag, ret = content ? jQuery.map( content, function( item ) {
274 | return (typeof item === "string") ?
275 | // Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
276 | (tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
277 | // This is a child template item. Build nested template.
278 | build( item, tmplItem, item._ctnt );
279 | }) :
280 | // If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
281 | tmplItem;
282 | if ( nested ) {
283 | return ret;
284 | }
285 |
286 | // top-level template
287 | ret = ret.join("");
288 |
289 | // Support templates which have initial or final text nodes, or consist only of text
290 | // Also support HTML entities within the HTML markup.
291 | ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
292 | frag = jQuery( middle ).get();
293 |
294 | storeTmplItems( frag );
295 | if ( before ) {
296 | frag = unencode( before ).concat(frag);
297 | }
298 | if ( after ) {
299 | frag = frag.concat(unencode( after ));
300 | }
301 | });
302 | return frag ? frag : unencode( ret );
303 | }
304 |
305 | function unencode( text ) {
306 | // Use createElement, since createTextNode will not render HTML entities correctly
307 | var el = document.createElement( "div" );
308 | el.innerHTML = text;
309 | return jQuery.makeArray(el.childNodes);
310 | }
311 |
312 | // Generate a reusable function that will serve to render a template against data
313 | function buildTmplFn( markup ) {
314 | return new Function("jQuery","$item",
315 | "var $=jQuery,call,_=[],$data=$item.data;" +
316 |
317 | // Introduce the data as local variables using with(){}
318 | "with($data){_.push('" +
319 |
320 | // Convert the template into pure JavaScript
321 | jQuery.trim(markup)
322 | .replace( /([\\'])/g, "\\$1" )
323 | .replace( /[\r\t\n]/g, " " )
324 | .replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
325 | .replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
326 | function( all, slash, type, fnargs, target, parens, args ) {
327 | var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
328 | if ( !tag ) {
329 | throw "Template command not found: " + type;
330 | }
331 | def = tag._default || [];
332 | if ( parens && !/\w$/.test(target)) {
333 | target += parens;
334 | parens = "";
335 | }
336 | if ( target ) {
337 | target = unescape( target );
338 | args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
339 | // Support for target being things like a.toLowerCase();
340 | // In that case don't call with template item as 'this' pointer. Just evaluate...
341 | expr = parens ? (target.indexOf(".") > -1 ? target + parens : ("(" + target + ").call($item" + args)) : target;
342 | exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
343 | } else {
344 | exprAutoFnDetect = expr = def.$1 || "null";
345 | }
346 | fnargs = unescape( fnargs );
347 | return "');" +
348 | tag[ slash ? "close" : "open" ]
349 | .split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
350 | .split( "$1a" ).join( exprAutoFnDetect )
351 | .split( "$1" ).join( expr )
352 | .split( "$2" ).join( fnargs ?
353 | fnargs.replace( /\s*([^\(]+)\s*(\((.*?)\))?/g, function( all, name, parens, params ) {
354 | params = params ? ("," + params + ")") : (parens ? ")" : "");
355 | return params ? ("(" + name + ").call($item" + params) : all;
356 | })
357 | : (def.$2||"")
358 | ) +
359 | "_.push('";
360 | }) +
361 | "');}return _;"
362 | );
363 | }
364 | function updateWrapped( options, wrapped ) {
365 | // Build the wrapped content.
366 | options._wrap = build( options, true,
367 | // Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
368 | jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
369 | ).join("");
370 | }
371 |
372 | function unescape( args ) {
373 | return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
374 | }
375 | function outerHtml( elem ) {
376 | var div = document.createElement("div");
377 | div.appendChild( elem.cloneNode(true) );
378 | return div.innerHTML;
379 | }
380 |
381 | // Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
382 | function storeTmplItems( content ) {
383 | var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
384 | for ( i = 0, l = content.length; i < l; i++ ) {
385 | if ( (elem = content[i]).nodeType !== 1 ) {
386 | continue;
387 | }
388 | elems = elem.getElementsByTagName("*");
389 | for ( m = elems.length - 1; m >= 0; m-- ) {
390 | processItemKey( elems[m] );
391 | }
392 | processItemKey( elem );
393 | }
394 | function processItemKey( el ) {
395 | var pntKey, pntNode = el, pntItem, tmplItem, key;
396 | // Ensure that each rendered template inserted into the DOM has its own template item,
397 | if ( (key = el.getAttribute( tmplItmAtt ))) {
398 | while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
399 | if ( pntKey !== key ) {
400 | // The next ancestor with a _tmplitem expando is on a different key than this one.
401 | // So this is a top-level element within this template item
402 | // Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
403 | pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
404 | if ( !(tmplItem = newTmplItems[key]) ) {
405 | // The item is for wrapped content, and was copied from the temporary parent wrappedItem.
406 | tmplItem = wrappedItems[key];
407 | tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode], null, true );
408 | tmplItem.key = ++itemKey;
409 | newTmplItems[itemKey] = tmplItem;
410 | }
411 | if ( cloneIndex ) {
412 | cloneTmplItem( key );
413 | }
414 | }
415 | el.removeAttribute( tmplItmAtt );
416 | } else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
417 | // This was a rendered element, cloned during append or appendTo etc.
418 | // TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
419 | cloneTmplItem( tmplItem.key );
420 | newTmplItems[tmplItem.key] = tmplItem;
421 | pntNode = jQuery.data( el.parentNode, "tmplItem" );
422 | pntNode = pntNode ? pntNode.key : 0;
423 | }
424 | if ( tmplItem ) {
425 | pntItem = tmplItem;
426 | // Find the template item of the parent element.
427 | // (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
428 | while ( pntItem && pntItem.key != pntNode ) {
429 | // Add this element as a top-level node for this rendered template item, as well as for any
430 | // ancestor items between this item and the item of its parent element
431 | pntItem.nodes.push( el );
432 | pntItem = pntItem.parent;
433 | }
434 | // Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
435 | delete tmplItem._ctnt;
436 | delete tmplItem._wrap;
437 | // Store template item as jQuery data on the element
438 | jQuery.data( el, "tmplItem", tmplItem );
439 | }
440 | function cloneTmplItem( key ) {
441 | key = key + keySuffix;
442 | tmplItem = newClonedItems[key] =
443 | (newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent, null, true ));
444 | }
445 | }
446 | }
447 |
448 | //---- Helper functions for template item ----
449 |
450 | function tiCalls( content, tmpl, data, options ) {
451 | if ( !content ) {
452 | return stack.pop();
453 | }
454 | stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
455 | }
456 |
457 | function tiNest( tmpl, data, options ) {
458 | // nested template, using {{tmpl}} tag
459 | return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
460 | }
461 |
462 | function tiWrap( call, wrapped ) {
463 | // nested template, using {{wrap}} tag
464 | var options = call.options || {};
465 | options.wrapped = wrapped;
466 | // Apply the template, which may incorporate wrapped content,
467 | return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
468 | }
469 |
470 | function tiHtml( filter, textOnly ) {
471 | var wrapped = this._wrap;
472 | return jQuery.map(
473 | jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
474 | function(e) {
475 | return textOnly ?
476 | e.innerText || e.textContent :
477 | e.outerHTML || outerHtml(e);
478 | });
479 | }
480 |
481 | function tiUpdate() {
482 | var coll = this.nodes;
483 | jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
484 | jQuery( coll ).remove();
485 | }
486 | })( jQuery );
487 |
--------------------------------------------------------------------------------