├── .gitattributes
├── .gitignore
├── .travis.yml
├── Examples
├── ExampleSearchResults
│ ├── Services
│ │ └── searchResults.txt
│ ├── Templates
│ │ └── SearchResult.html
│ └── search.html
├── OptionalBinding
│ └── example.html
├── SimpleExample
│ ├── Templates
│ │ └── simple.html
│ ├── example.html
│ └── img
│ │ └── joeBloggs.gif
├── css
│ └── default.css
└── index.html
├── MIT-LICENSE.txt
├── bower.json
├── dist
├── jquery.loadTemplate.js
└── jquery.loadTemplate.min.js
├── loadTemplate.jquery.json
├── package.json
├── readme.md
└── tests
├── files
├── bindingOptions.js
├── callback.js
├── error.js
├── formatters.js
└── general.js
├── index.html
├── phantom.js
├── testRunner.js
└── wru.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | node_modules/
3 |
4 | #################
5 | ## Eclipse
6 | #################
7 |
8 | *.pydevproject
9 | .project
10 | .metadata
11 | bin/
12 | tmp/
13 | *.tmp
14 | *.bak
15 | *.swp
16 | *~.nib
17 | local.properties
18 | .classpath
19 | .settings/
20 | .loadpath
21 |
22 | # External tool builders
23 | .externalToolBuilders/
24 |
25 | # Locally stored "Eclipse launch configurations"
26 | *.launch
27 |
28 | # CDT-specific
29 | .cproject
30 |
31 | # PDT-specific
32 | .buildpath
33 |
34 |
35 | #################
36 | ## Visual Studio
37 | #################
38 |
39 | ## Ignore Visual Studio temporary files, build results, and
40 | ## files generated by popular Visual Studio add-ons.
41 |
42 | # User-specific files
43 | *.suo
44 | *.user
45 | *.sln.docstates
46 |
47 | # Build results
48 |
49 | [Dd]ebug/
50 | [Rr]elease/
51 | x64/
52 | build/
53 | [Bb]in/
54 | [Oo]bj/
55 |
56 | # MSTest test Results
57 | [Tt]est[Rr]esult*/
58 | [Bb]uild[Ll]og.*
59 |
60 | *_i.c
61 | *_p.c
62 | *.ilk
63 | *.meta
64 | *.obj
65 | *.pch
66 | *.pdb
67 | *.pgc
68 | *.pgd
69 | *.rsp
70 | *.sbr
71 | *.tlb
72 | *.tli
73 | *.tlh
74 | *.tmp
75 | *.tmp_proj
76 | *.log
77 | *.vspscc
78 | *.vssscc
79 | .builds
80 | *.pidb
81 | *.log
82 | *.scc
83 |
84 | # Visual C++ cache files
85 | ipch/
86 | *.aps
87 | *.ncb
88 | *.opensdf
89 | *.sdf
90 | *.cachefile
91 |
92 | # Visual Studio profiler
93 | *.psess
94 | *.vsp
95 | *.vspx
96 |
97 | # Guidance Automation Toolkit
98 | *.gpState
99 |
100 | # ReSharper is a .NET coding add-in
101 | _ReSharper*/
102 | *.[Rr]e[Ss]harper
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | *.ncrunch*
112 | .*crunch*.local.xml
113 |
114 | # Installshield output folder
115 | [Ee]xpress/
116 |
117 | # DocProject is a documentation generator add-in
118 | DocProject/buildhelp/
119 | DocProject/Help/*.HxT
120 | DocProject/Help/*.HxC
121 | DocProject/Help/*.hhc
122 | DocProject/Help/*.hhk
123 | DocProject/Help/*.hhp
124 | DocProject/Help/Html2
125 | DocProject/Help/html
126 |
127 | # Click-Once directory
128 | publish/
129 |
130 | # Publish Web Output
131 | *.Publish.xml
132 | *.pubxml
133 |
134 | # NuGet Packages Directory
135 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
136 | #packages/
137 |
138 | # Windows Azure Build Output
139 | csx
140 | *.build.csdef
141 |
142 | # Windows Store app package directory
143 | AppPackages/
144 |
145 | # Others
146 | sql/
147 | *.Cache
148 | ClientBin/
149 | [Ss]tyle[Cc]op.*
150 | ~$*
151 | *~
152 | *.dbmdl
153 | *.[Pp]ublish.xml
154 | *.pfx
155 | *.publishsettings
156 |
157 | # RIA/Silverlight projects
158 | Generated_Code/
159 |
160 | # Backup & report files from converting an old project file to a newer
161 | # Visual Studio version. Backup files are not needed, because we have git ;-)
162 | _UpgradeReport_Files/
163 | Backup*/
164 | UpgradeLog*.XML
165 | UpgradeLog*.htm
166 |
167 | # SQL Server files
168 | App_Data/*.mdf
169 | App_Data/*.ldf
170 |
171 | #############
172 | ## Windows detritus
173 | #############
174 |
175 | # Windows image file caches
176 | Thumbs.db
177 | ehthumbs.db
178 |
179 | # Folder config file
180 | Desktop.ini
181 |
182 | # Recycle Bin used on file shares
183 | $RECYCLE.BIN/
184 |
185 | # Mac crap
186 | .DS_Store
187 |
188 |
189 | #############
190 | ## Python
191 | #############
192 |
193 | *.py[co]
194 |
195 | # Packages
196 | *.egg
197 | *.egg-info
198 | build/
199 | eggs/
200 | parts/
201 | var/
202 | sdist/
203 | develop-eggs/
204 | .installed.cfg
205 |
206 | # Installer logs
207 | pip-log.txt
208 |
209 | # Unit test / coverage reports
210 | .coverage
211 | .tox
212 |
213 | #Translations
214 | *.mo
215 |
216 | #Mr Developer
217 | .mr.developer.cfg
218 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | before_script : cd tests
2 | script: phantomjs phantom.js
3 |
--------------------------------------------------------------------------------
/Examples/ExampleSearchResults/Services/searchResults.txt:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 2,
4 | "name": "An ice sculpture",
5 | "price": 0.00,
6 | "tags": ["cold", "ice", "sculpture"],
7 | "dimensions": {
8 | "length": 7.0,
9 | "width": 12.0,
10 | "height": 9.5
11 | },
12 | "warehouseLocation": {
13 | "latitude": -78.75,
14 | "longitude": 20.4
15 | }
16 | },
17 | {
18 | "id": 3,
19 | "name": "A blue mouse",
20 | "price": 25.50,
21 | "tags": ["blue", "mouse"],
22 | "dimensions": {
23 | "length": 3.1,
24 | "width": 1.0,
25 | "height": 1.0
26 | },
27 | "warehouseLocation": {
28 | "latitude": 54.4,
29 | "longitude": -32.7
30 | }
31 | },
32 | {
33 | "id": 4,
34 | "name": "A house",
35 | "price": 250000.50,
36 | "tags": ["house", "accommodation"],
37 | "dimensions": {
38 | "length": 30.1,
39 | "width": 21.0,
40 | "height": 7.0
41 | },
42 | "warehouseLocation": {
43 | "latitude": -78.75,
44 | "longitude": 20.4
45 | }
46 | },
47 | {
48 | "id": 5,
49 | "name": "A purple lawnmower",
50 | "price": 45.50,
51 | "tags": ["purple", "lawnmower"],
52 | "dimensions": {
53 | "length": 4.1,
54 | "width": 2.0,
55 | "height": 2.0
56 | },
57 | "warehouseLocation": {
58 | "latitude": -78.75,
59 | "longitude": 20.4
60 | }
61 | },
62 | {
63 | "id": 6,
64 | "name": "A green mouse",
65 | "price": 25.50,
66 | "tags": ["green", "mouse"],
67 | "dimensions": {
68 | "length": 3.1,
69 | "width": 1.0,
70 | "height": 1.0
71 | },
72 | "warehouseLocation": {
73 | "latitude": 54.4,
74 | "longitude": -32.7
75 | }
76 | },
77 | {
78 | "id": 7,
79 | "name": "A yellow mouse",
80 | "price": 25.50,
81 | "tags": ["yellow", "mouse"],
82 | "dimensions": {
83 | "length": 3.1,
84 | "width": 1.0,
85 | "height": 1.0
86 | },
87 | "warehouseLocation": {
88 | "latitude": 54.4,
89 | "longitude": -32.7
90 | }
91 | },
92 | {
93 | "id": 8,
94 | "name": "A white mouse",
95 | "price": 25.50,
96 | "tags": ["white", "mouse"],
97 | "dimensions": {
98 | "length": 3.1,
99 | "width": 1.0,
100 | "height": 1.0
101 | },
102 | "warehouseLocation": {
103 | "latitude": 54.4,
104 | "longitude": -32.7
105 | }
106 | },
107 | {
108 | "id": 9,
109 | "name": "A black mouse",
110 | "price": 30.50,
111 | "tags": ["black", "mouse"],
112 | "dimensions": {
113 | "length": 3.1,
114 | "width": 1.0,
115 | "height": 1.0
116 | },
117 | "warehouseLocation": {
118 | "latitude": 54.4,
119 | "longitude": -32.7
120 | }
121 | },
122 | {
123 | "id": 10,
124 | "name": "A red mouse",
125 | "price": 30.50,
126 | "tags": ["red", "mouse"],
127 | "dimensions": {
128 | "length": 3.1,
129 | "width": 1.0,
130 | "height": 1.0
131 | },
132 | "warehouseLocation": {
133 | "latitude": 54.4,
134 | "longitude": -32.7
135 | }
136 | },
137 | {
138 | "id": 11,
139 | "name": "An orange mouse",
140 | "price": 25.50,
141 | "tags": ["orange", "mouse"],
142 | "dimensions": {
143 | "length": 3.1,
144 | "width": 1.0,
145 | "height": 1.0
146 | },
147 | "warehouseLocation": {
148 | "latitude": 54.4,
149 | "longitude": -32.7
150 | }
151 | },
152 | {
153 | "id": 12,
154 | "name": "A stone sculpture",
155 | "price": 12.50,
156 | "tags": ["stone", "sculpture"],
157 | "dimensions": {
158 | "length": 7.0,
159 | "width": 12.0,
160 | "height": 9.5
161 | },
162 | "warehouseLocation": {
163 | "latitude": -78.75,
164 | "longitude": 20.4
165 | }
166 | },
167 | {
168 | "id": 13,
169 | "name": "A wood sculpture",
170 | "price": 12.50,
171 | "tags": ["wood", "sculpture", "flammable"],
172 | "dimensions": {
173 | "length": 7.0,
174 | "width": 12.0,
175 | "height": 9.5
176 | },
177 | "warehouseLocation": {
178 | "latitude": -78.75,
179 | "longitude": 20.4
180 | }
181 | },
182 | {
183 | "id": 14,
184 | "name": "An iron sculpture",
185 | "price": 12.50,
186 | "tags": ["iron", "sculpture"],
187 | "dimensions": {
188 | "length": 7.0,
189 | "width": 12.0,
190 | "height": 9.5
191 | },
192 | "warehouseLocation": {
193 | "latitude": -78.75,
194 | "longitude": 20.4
195 | }
196 | },
197 | {
198 | "id": 15,
199 | "name": "A steel sculpture",
200 | "price": 12.50,
201 | "tags": ["steel", "sculpture"],
202 | "dimensions": {
203 | "length": 7.0,
204 | "width": 12.0,
205 | "height": 9.5
206 | },
207 | "warehouseLocation": {
208 | "latitude": -78.75,
209 | "longitude": 20.4
210 | }
211 | },
212 | {
213 | "id": 16,
214 | "name": "A paper sculpture",
215 | "price": 12.50,
216 | "tags": ["paper", "sculpture"],
217 | "dimensions": {
218 | "length": 7.0,
219 | "width": 12.0,
220 | "height": 9.5
221 | },
222 | "warehouseLocation": {
223 | "latitude": -78.75,
224 | "longitude": 20.4
225 | }
226 | },
227 | {
228 | "id": 17,
229 | "name": "A flat",
230 | "price": 120000.50,
231 | "tags": ["flat", "accommodation"],
232 | "dimensions": {
233 | "length": 30.1,
234 | "width": 21.0,
235 | "height": 3.0
236 | },
237 | "warehouseLocation": {
238 | "latitude": -78.75,
239 | "longitude": 20.4
240 | }
241 | },
242 | {
243 | "id": 18,
244 | "name": "A bungalow",
245 | "price": 160000.50,
246 | "tags": ["bungalow", "accommodation"],
247 | "dimensions": {
248 | "length": 30.1,
249 | "width": 21.0,
250 | "height": 3.0
251 | },
252 | "warehouseLocation": {
253 | "latitude": -78.75,
254 | "longitude": 20.4
255 | }
256 | },
257 | {
258 | "id": 19,
259 | "name": "A yellow lawnmower",
260 | "price": 45.50,
261 | "tags": ["yellow", "lawnmower"],
262 | "dimensions": {
263 | "length": 4.1,
264 | "width": 2.0,
265 | "height": 2.0
266 | },
267 | "warehouseLocation": {
268 | "latitude": -78.75,
269 | "longitude": 20.4
270 | }
271 | },
272 | {
273 | "id": 20,
274 | "name": "A red lawnmower",
275 | "price": 45.50,
276 | "tags": ["red", "lawnmower"],
277 | "dimensions": {
278 | "length": 4.1,
279 | "width": 2.0,
280 | "height": 2.0
281 | },
282 | "warehouseLocation": {
283 | "latitude": -78.75,
284 | "longitude": 20.4
285 | }
286 | }
287 | ]
--------------------------------------------------------------------------------
/Examples/ExampleSearchResults/Templates/SearchResult.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/ExampleSearchResults/search.html:
--------------------------------------------------------------------------------
1 | Example of using jQuery.loadTemplate for improved search.
2 | jQuery.loadTemplate can be used for easily displaying data sets.
3 | The example below is an example of utilising this. When the search button is pressed,
4 | data is fetched from the server in the form of a JSON object. Processing this data is done
5 | client side, and allows for great client experience, sending the minimal amount of data
6 | from the server (just a single json object, and a template). Another benefit this brings
7 | is we have all our data in an easy to manipulate javascript object on the client.
8 | The example below makes use of paging a javascript array with the jQuery.loadTemplate
9 | function. Simply by passing in options to set paged to true, which page to display, and the
10 | number of elements per page, the function will only output the elements on that page. The
11 | navigation of pages is entirely separate from the function (the next and previous buttons
12 | are not provided).
13 | Search
14 |
15 |
16 |
17 | id
18 | name
19 | price
20 | tags
21 | coordinates
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Previous
30 | Next
31 |
32 |
33 |
91 |
--------------------------------------------------------------------------------
/Examples/OptionalBinding/example.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
10 |
11 |
15 |
16 |
20 |
21 |
22 |
46 |
47 |
48 | Loaded with function parameters
49 |
50 |
51 |
52 |
53 |
54 |
55 | Loaded with template parameters
56 |
57 |
58 |
59 |
60 |
61 |
62 | Loaded with template as data-bind-template attribute
63 |
64 |
65 |
66 |
67 | Loaded with nested template with function parameters
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/Examples/SimpleExample/Templates/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Examples/SimpleExample/example.html:
--------------------------------------------------------------------------------
1 | Example Usage of jQuery.loadTemplate
2 | Example of loading an html template from the application
3 | Loading Multiple posts into a single container. Templates loaded from an external source
4 |
5 | Loading single post into single container. Templates loaded from an external source
6 |
7 | Example of loading a template from a script tag within the same document.
8 | Loading a single post into multiple containers. Templates loaded from within the page.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
27 |
28 |
32 |
33 |
38 |
41 |
42 |
46 |
47 |
50 |
51 |
122 |
--------------------------------------------------------------------------------
/Examples/SimpleExample/img/joeBloggs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codepb/jquery-template/01642159be50b046e62f83c41da91f01bc9fadb3/Examples/SimpleExample/img/joeBloggs.gif
--------------------------------------------------------------------------------
/Examples/css/default.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: #555;
3 | font-family: 'Roboto', sans-serif;
4 | width: 100%;
5 | }
6 | h1, h2, h3, h4, h5, h6 {
7 | text-align: center;
8 | font-weight: normal;
9 | margin: 0;
10 | }
11 | .contentContainer p {
12 | text-align: justify;
13 | }
14 | .contentContainer, .codeContainer {
15 | height: 100%;
16 | }
17 | .contentContainer > div, .codeContainer > div {
18 | display: none;
19 | margin: 0 3%;
20 | height: 100%;
21 | vertical-align: top;
22 | }
23 |
24 | .codeContainer pre {
25 | white-space: pre;
26 | word-wrap: normal;
27 | margin: 0;
28 | height: calc(100% - 30px);
29 | }
30 |
31 | .codeContainer code {
32 | overflow: auto;
33 | height: calc(100% - 1em);
34 | }
35 | .nav {
36 | width: 200px;
37 | height: 100%;
38 | display: inline-block;
39 | vertical-align: top;
40 | margin-right: 20px;
41 | }
42 | .contentHead {
43 | height: 30px;
44 | }
45 | .contentContainer div.content {
46 | overflow: auto;
47 | height: calc(100% - 30px);
48 | }
49 | .codeContainer > pre {
50 | overflow: auto;
51 | height: calc(100% - 30px);
52 | }
53 | .head {
54 | height: 100px;
55 | width: 100%;
56 | }
57 | .row {
58 | position: absolute;
59 | }
60 | .body {
61 | top: 100px;
62 | bottom: 60px;
63 | width: 100%
64 | }
65 |
66 |
67 | /* Search Example */
68 | #ResultsDisplay {
69 | width: 100%;
70 | display: none;
71 | }
72 |
73 | #ResultsPaging {
74 | display: none;
75 | }
76 |
77 | #PerformSearch {
78 | margin-bottom: 3px;
79 | }
80 |
81 | .nav-button {
82 | margin-top: 3px;
83 | }
84 |
85 | /* Simple Example */
86 | .simple-template-container, .simple-template-container-single {
87 | width: 400px;
88 | border: 1px solid #ccc;
89 | padding: 10px;
90 | margin: 10px;
91 | }
92 |
93 | .simple-template-container img {
94 | display: inline-block;
95 | }
96 |
97 | .simple-template-container .post-container {
98 | display: inline-block;
99 | width: 240px;
100 | margin-left: 10px;
101 | vertical-align: top;
102 | }
103 |
104 | .script-template-container {
105 | width: 140px;
106 | border: 1px dashed #ccc;
107 | padding: 10px;
108 | margin: 10px;
109 | display: inline-block;
110 | }
--------------------------------------------------------------------------------
/Examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Examples
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Examples of jQuery.loadTemplate
17 |
18 |
19 |
20 |
21 |
29 |
30 |
31 |
32 |
33 |
Display
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
Code
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/MIT-LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Paul Burgess and other contributors,
2 | http://codepb.com
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-load-template",
3 | "homepage": "http://codepb.github.io/jquery-template/",
4 | "authors": [
5 | "Paul Burgess "
6 | ],
7 | "description": "jQuery plugin for loading and using templates. The plugin supports loading templates from within the page, or using AJAX to load html files.",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/codepb/jquery-template.git"
11 | },
12 | "main": "dist/jquery.loadTemplate.js",
13 | "keywords": [
14 | "templates",
15 | "templating",
16 | "jquery-plugin"
17 | ],
18 | "ignore": [
19 | "Examples",
20 | "tests",
21 | ".gitattributes",
22 | ".gitignore",
23 | ".travis.yml",
24 | "loadTemplate.jquery.json",
25 | "MIT-LICENSE.txt",
26 | "package.json",
27 | "readme.md"
28 | ],
29 | "dependencies": {
30 | "jquery": ">=1.8"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/dist/jquery.loadTemplate.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 | "use strict";
3 | var templates = {},
4 | queue = {},
5 | formatters = {},
6 | isArray;
7 |
8 | function loadTemplate(template, data, options) {
9 | var $that = this,
10 | $template,
11 | isFile,
12 | settings;
13 |
14 | data = data || {};
15 |
16 | settings = $.extend(true, {
17 | // These are the defaults.
18 | async: true,
19 | overwriteCache: false,
20 | complete: null,
21 | success: null,
22 | error: function () {
23 | $(this).each(function () {
24 | $(this).html(settings.errorMessage);
25 | });
26 | },
27 | errorMessage: "There was an error loading the template.",
28 | paged: false,
29 | pageNo: 1,
30 | elemPerPage: 10,
31 | append: false,
32 | prepend: false,
33 | beforeInsert: null,
34 | afterInsert: null,
35 | bindingOptions: {
36 | ignoreUndefined: false,
37 | ignoreNull: false,
38 | ignoreEmptyString: false
39 | }
40 | }, options);
41 |
42 | if ($.type(data) === "array") {
43 | isArray = true;
44 | return processArray.call(this, template, data, settings);
45 | }
46 |
47 | if (!containsSlashes(template)) {
48 | $template = $(template);
49 | if (typeof template === 'string' && template.indexOf('#') === 0) {
50 | settings.isFile = false;
51 | }
52 | }
53 |
54 | isFile = settings.isFile || (typeof settings.isFile === "undefined" && (typeof $template === "undefined" || $template.length === 0));
55 |
56 | if (isFile && !settings.overwriteCache && templates[template]) {
57 | prepareTemplateFromCache(template, $that, data, settings);
58 | } else if (isFile && !settings.overwriteCache && templates.hasOwnProperty(template)) {
59 | addToQueue(template, $that, data, settings);
60 | } else if (isFile) {
61 | loadAndPrepareTemplate(template, $that, data, settings);
62 | } else {
63 | loadTemplateFromDocument($template, $that, data, settings);
64 | }
65 | return this;
66 | }
67 |
68 | function addTemplateFormatter(key, formatter) {
69 | if (formatter) {
70 | formatters[key] = formatter;
71 | } else {
72 | formatters = $.extend(formatters, key);
73 | }
74 | }
75 |
76 | function containsSlashes(str) {
77 | return typeof str === "string" && str.indexOf("/") > -1;
78 | }
79 |
80 | function processArray(template, data, settings) {
81 | settings = settings || {};
82 | var $that = this,
83 | todo = data.length,
84 | doPrepend = settings.prepend && !settings.append,
85 | done = 0,
86 | success = 0,
87 | errored = false,
88 | errorObjects = [],
89 | newOptions;
90 |
91 | if (settings.paged) {
92 | var startNo = (settings.pageNo - 1) * settings.elemPerPage;
93 | data = data.slice(startNo, startNo + settings.elemPerPage);
94 | todo = data.length;
95 | }
96 |
97 | if (!settings.append && !settings.prepend) {
98 | $that.html("");
99 | }
100 |
101 | newOptions = $.extend(
102 | {},
103 | settings,
104 | {
105 | append: !settings.prepend && true,
106 | complete: function (data) {
107 | done++;
108 | if (done === todo || errored) {
109 | if (errored && settings && typeof settings.error === "function") {
110 | settings.error.call($that, errorObjects);
111 | }
112 | if (settings && typeof settings.complete === "function") {
113 | settings.complete();
114 | }
115 | }
116 | },
117 | success: function () {
118 | success++;
119 | if (success === todo) {
120 | if (settings && typeof settings.success === "function") {
121 | settings.success();
122 | }
123 | }
124 | },
125 | error: function (e) {
126 | errored = true;
127 | errorObjects.push(e);
128 | }
129 | }
130 | );
131 |
132 |
133 |
134 | if (doPrepend) data.reverse();
135 | $(data).each(function () {
136 |
137 | loadTemplate.call($that, template, this, newOptions);
138 | if (errored) {
139 | return false;
140 | }
141 | });
142 |
143 | return this;
144 | }
145 |
146 | function addToQueue(template, selection, data, settings) {
147 | if (queue[template]) {
148 | queue[template].push({ data: data, selection: selection, settings: settings });
149 | } else {
150 | queue[template] = [{ data: data, selection: selection, settings: settings}];
151 | }
152 | }
153 |
154 | function prepareTemplateFromCache(template, selection, data, settings) {
155 | var $templateContainer = templates[template].clone();
156 |
157 | prepareTemplate.call(selection, $templateContainer, data, settings);
158 | if (typeof settings.success === "function") {
159 | settings.success();
160 | }
161 | }
162 |
163 | function uniqueId() {
164 | return new Date().getTime();
165 | }
166 |
167 | function urlAvoidCache(url) {
168 | if (url.indexOf('?') !== -1) {
169 | return url + "&_=" + uniqueId();
170 | }
171 | else {
172 | return url + "?_=" + uniqueId();
173 | }
174 | }
175 |
176 | function loadAndPrepareTemplate(template, selection, data, settings) {
177 |
178 | templates[template] = null;
179 | var templateUrl = template;
180 | if (settings.overwriteCache) {
181 | templateUrl = urlAvoidCache(templateUrl);
182 | }
183 | $.ajax({
184 | url: templateUrl,
185 | async: settings.async,
186 | success: function (templateContent) {
187 | handleTemplateLoadingSuccess($(templateContent), template, selection, data, settings);
188 | },
189 | error: function (e) {
190 | handleTemplateLoadingError(template, selection, data, settings, e);
191 | }
192 | });
193 | }
194 |
195 | function loadTemplateFromDocument($template, selection, data, settings) {
196 | if ($template.is("script") || $template.is("template")) {
197 | $template = $.parseHTML($.trim($template.html()));
198 | }
199 |
200 | prepareTemplate.call(selection, $template, data, settings);
201 |
202 | if (typeof settings.success === "function") {
203 | settings.success();
204 | }
205 | }
206 |
207 | function prepareTemplate(template, data, settings) {
208 | var template = $("
").append(template);
209 | bindData(template, data, settings);
210 |
211 | $(this).each(function () {
212 | var $templateHtml = template.children().clone(true);
213 | $("select", $templateHtml).each(function (key, value) {
214 | $(this).val($("select", template).eq(key).val())
215 | });
216 | if (settings.beforeInsert) {
217 | settings.beforeInsert($templateHtml, data);
218 | }
219 | if (settings.append) {
220 |
221 | $(this).append($templateHtml);
222 | } else if (settings.prepend) {
223 | $(this).prepend($templateHtml);
224 | } else {
225 | $(this).html("").append($templateHtml);
226 | }
227 | if (settings.afterInsert) {
228 | settings.afterInsert($templateHtml, data);
229 | }
230 | });
231 |
232 | if (typeof settings.complete === "function") {
233 | settings.complete.call($(this), data);
234 | }
235 | }
236 |
237 | function handleTemplateLoadingError(template, selection, data, settings, error) {
238 | var value;
239 |
240 | if (typeof settings.error === "function") {
241 | settings.error.call(selection, error);
242 | }
243 |
244 | $(queue[template]).each(function (key, value) {
245 | if (typeof value.settings.error === "function") {
246 | value.settings.error.call(value.selection, error);
247 | }
248 | });
249 |
250 | if (typeof settings.complete === "function") {
251 | settings.complete.call(selection);
252 | }
253 |
254 | while (queue[template] && (value = queue[template].shift())) {
255 | if (typeof value.settings.complete === "function") {
256 | value.settings.complete.call(value.selection);
257 | }
258 | }
259 |
260 | if (typeof queue[template] !== 'undefined' && queue[template].length > 0) {
261 | queue[template] = [];
262 | }
263 | }
264 |
265 | function handleTemplateLoadingSuccess($templateContainer, template, selection, data, settings) {
266 | var value;
267 |
268 | templates[template] = $templateContainer.clone();
269 | prepareTemplate.call(selection, $templateContainer, data, settings);
270 |
271 | if (typeof settings.success === "function") {
272 | settings.success.call(selection);
273 | }
274 |
275 | while (queue[template] && (value = queue[template].shift())) {
276 | prepareTemplate.call(value.selection, templates[template].clone(), value.data, value.settings);
277 | if (typeof value.settings.success === "function") {
278 | value.settings.success.call(value.selection);
279 | }
280 | }
281 | }
282 |
283 | function bindData(template, data, settings) {
284 | data = data || {};
285 |
286 | processElements("data-content", template, data, settings, function ($elem, value) {
287 | $elem.html(applyFormatters($elem, value, "content", settings));
288 | });
289 |
290 | processElements("data-content-append", template, data, settings, function ($elem, value) {
291 | $elem.append(applyFormatters($elem, value, "content", settings));
292 | });
293 |
294 | processElements("data-content-prepend", template, data, settings, function ($elem, value) {
295 | $elem.prepend(applyFormatters($elem, value, "content", settings));
296 | });
297 |
298 | processElements("data-content-text", template, data, settings, function ($elem, value) {
299 | $elem.text(applyFormatters($elem, value, "content", settings));
300 | });
301 |
302 | processElements("data-innerHTML", template, data, settings, function ($elem, value) {
303 | $elem.html(applyFormatters($elem, value, "content", settings));
304 | });
305 |
306 | processElements("data-src", template, data, settings, function ($elem, value) {
307 | $elem.attr("src", applyFormatters($elem, value, "src", settings));
308 | }, function ($elem) {
309 | $elem.remove();
310 | });
311 |
312 | processElements("data-href", template, data, settings, function ($elem, value) {
313 | $elem.attr("href", applyFormatters($elem, value, "href", settings));
314 | }, function ($elem) {
315 | $elem.remove();
316 | });
317 |
318 | processElements("data-alt", template, data, settings, function ($elem, value) {
319 | $elem.attr("alt", applyFormatters($elem, value, "alt", settings));
320 | });
321 |
322 | processElements("data-title", template, data, settings, function ($elem, value) {
323 | $elem.attr("title", applyFormatters($elem, value, "title", settings));
324 | });
325 |
326 | processElements("data-id", template, data, settings, function ($elem, value) {
327 | $elem.attr("id", applyFormatters($elem, value, "id", settings));
328 | });
329 |
330 | processElements("data-css", template, data, settings, function ($elem, value) {
331 | $elem.css(applyFormatters($elem, value, "css", settings))
332 | });
333 |
334 | processElements("data-class", template, data, settings, function ($elem, value) {
335 | $elem.addClass(applyFormatters($elem, value, "class", settings));
336 | });
337 |
338 | processElements("data-link", template, data, settings, function ($elem, value) {
339 | var $linkElem = $(" ");
340 | $linkElem.attr("href", applyFormatters($elem, value, "link", settings));
341 | $linkElem.html($elem.html());
342 | $elem.html($linkElem);
343 | });
344 |
345 | processElements("data-link-wrap", template, data, settings, function ($elem, value) {
346 | var $linkElem = $(" ");
347 | $linkElem.attr("href", applyFormatters($elem, value, "link-wrap", settings));
348 | $elem.wrap($linkElem);
349 | });
350 |
351 | processElements("data-options", template, data, settings, function ($elem, value) {
352 | $(value).each(function () {
353 | var $option = $(" ");
354 | $option.attr('value', this).text(this).appendTo($elem);
355 | });
356 | });
357 |
358 | processAllElements(template, data, settings);
359 |
360 | processElements("data-value", template, data, settings, function ($elem, value) {
361 | $elem.val(applyFormatters($elem, value, "value", settings));
362 | });
363 |
364 |
365 | }
366 |
367 | function processElements(attribute, template, data, settings, dataBindFunction, noDataFunction) {
368 | $("[" + attribute + "]", template).each(function () {
369 | var $this = $(this),
370 | param = $this.attr(attribute),
371 | value = getValue(data, param);
372 |
373 | if (!valueIsAllowedByBindingOptions($this, value, settings)) {
374 | $this.remove();
375 | return;
376 | }
377 |
378 | $this.removeAttr(attribute);
379 |
380 | if (typeof value !== 'undefined' && dataBindFunction) {
381 | dataBindFunction($this, value);
382 | } else if (noDataFunction) {
383 | noDataFunction($this);
384 | }
385 | });
386 | return;
387 | }
388 |
389 | function valueIsAllowedByBindingOptions(bindingOptionsContainer, value, settings) {
390 |
391 | var bindingOptions = getBindingOptions(bindingOptionsContainer, settings);
392 |
393 | if (bindingOptions.ignoreUndefined && typeof value === "undefined") {
394 | return false;
395 |
396 | } else if (bindingOptions.ignoreNull && value === null) {
397 | return false;
398 |
399 | } else if (bindingOptions.ignoreEmptyString && value === "") {
400 | return false;
401 |
402 | } else {
403 | return true;
404 | }
405 | }
406 |
407 | function getBindingOptions(bindingOptionsContainer, settings) {
408 |
409 | var bindingOptions = {};
410 |
411 | // binding options passed as template attribute, i.e. 'data-binding-options'
412 | if (bindingOptionsContainer instanceof jQuery && bindingOptionsContainer.attr("data-binding-options")) {
413 |
414 | bindingOptions = $.parseJSON(bindingOptionsContainer.attr("data-binding-options"));
415 | bindingOptionsContainer.removeAttr("data-binding-options");
416 |
417 | // binding options defined in a "data-template-bind" attribute
418 | } else if (typeof bindingOptionsContainer === "object" && bindingOptionsContainer.hasOwnProperty('bindingOptions')) {
419 | bindingOptions = bindingOptionsContainer.bindingOptions;
420 | }
421 |
422 | // extend general bindingOptions with specific settings
423 | return $.extend({}, settings.bindingOptions, bindingOptions);
424 | }
425 |
426 | function processAllElements(template, data, settings) {
427 | $("[data-template-bind]", template).each(function () {
428 | var $this = $(this),
429 | param = $.parseJSON($this.attr("data-template-bind"));
430 |
431 | $this.removeAttr("data-template-bind");
432 |
433 | $(param).each(function () {
434 | var value;
435 |
436 | if (typeof (this.value) === 'object') {
437 | value = getValue(data, this.value.data);
438 | } else {
439 | value = getValue(data, this.value);
440 | }
441 | if (this.attribute) {
442 |
443 | if (!valueIsAllowedByBindingOptions(this, value, settings)) {
444 | $this.remove();
445 | return;
446 | }
447 |
448 | switch (this.attribute) {
449 | case "content":
450 | case "innerHTML":
451 | $this.html(applyDataBindFormatters($this, value, this));
452 | break;
453 | case "contentAppend":
454 | $this.append(applyDataBindFormatters($this, value, this));
455 | break;
456 | case "contentPrepend":
457 | $this.prepend(applyDataBindFormatters($this, value, this));
458 | break;
459 | case "contentText":
460 | $this.text(applyDataBindFormatters($this, value, this));
461 | break;
462 | case "options":
463 | var optionsData = this;
464 | $(value).each(function () {
465 | var $option = $(" ");
466 | $option
467 | .attr('value', this[optionsData.value.value])
468 | .text(applyDataBindFormatters($this, this[optionsData.value.content], optionsData))
469 | .attr('selected', typeof this[optionsData.value.selected] == undefined ? false : this[optionsData.value.selected])
470 | .appendTo($this);
471 | });
472 | break;
473 | default:
474 | $this.attr(this.attribute, applyDataBindFormatters($this, value, this));
475 | }
476 | }
477 | });
478 | });
479 | }
480 |
481 | function applyDataBindFormatters($elem, value, data, settings) {
482 | if (data.formatter && formatters[data.formatter]) {
483 | return (function (formatterSettings) {
484 | return formatters[data.formatter].call($elem, value, data.formatOptions, formatterSettings);
485 | })(settings);
486 | }
487 | return value;
488 | }
489 |
490 | function getValue(data, param) {
491 | if (param === "this") {
492 | return data;
493 | }
494 | var paramParts = param.split('.'),
495 | part,
496 | value = data;
497 |
498 | while ((part = paramParts.shift()) && typeof value !== "undefined" && value != null) {
499 | value = value[part];
500 | }
501 |
502 | return value;
503 | }
504 |
505 | function applyFormatters($elem, value, attr, settings) {
506 | var formatterTarget = $elem.attr("data-format-target"),
507 | formatter;
508 |
509 | if (formatterTarget === attr || (!formatterTarget && attr === "content")) {
510 | formatter = $elem.attr("data-format");
511 | if (formatter && typeof formatters[formatter] === "function") {
512 | var formatOptions = $elem.attr("data-format-options");
513 | return (function (formatterSettings) {
514 | return formatters[formatter].call($elem[0], value, formatOptions, $.extend({}, formatterSettings));
515 | })(settings);
516 | }
517 | }
518 |
519 | return value;
520 | }
521 | addTemplateFormatter("nestedTemplateFormatter", function (value, options, internalSettings) {
522 | if (!options) {
523 | return;
524 | }
525 |
526 | if (typeof options === "string" && options[0] === "{") {
527 | options = $.parseJSON(options);
528 | }
529 |
530 | var parentElement = options.parentElement || "div";
531 | var template = options.template || options;
532 |
533 | //If a parent is specified, return it; otherwise only return the generated children.
534 | if (options.parentElement)
535 | return $("<" + parentElement + "/>").loadTemplate(template, value, internalSettings);
536 | else
537 | return $("<" + parentElement + "/>").loadTemplate(template, value, internalSettings).children();
538 | });
539 | $.fn.loadTemplate = loadTemplate;
540 | $.addTemplateFormatter = addTemplateFormatter;
541 |
542 | })(jQuery);
543 |
--------------------------------------------------------------------------------
/dist/jquery.loadTemplate.min.js:
--------------------------------------------------------------------------------
1 | !function(t){"use strict";function e(e,n,c){var s,f,p,d=this;return n=n||{},p=t.extend(!0,{async:!0,overwriteCache:!1,complete:null,success:null,error:function(){t(this).each(function(){t(this).html(p.errorMessage)})},errorMessage:"There was an error loading the template.",paged:!1,pageNo:1,elemPerPage:10,append:!1,prepend:!1,beforeInsert:null,afterInsert:null,bindingOptions:{ignoreUndefined:!1,ignoreNull:!1,ignoreEmptyString:!1}},c),"array"===t.type(n)?(T=!0,i.call(this,e,n,p)):(a(e)||(s=t(e),"string"==typeof e&&0===e.indexOf("#")&&(p.isFile=!1)),f=p.isFile||void 0===p.isFile&&(void 0===s||0===s.length),f&&!p.overwriteCache&&w[e]?o(e,d,n,p):f&&!p.overwriteCache&&w.hasOwnProperty(e)?r(e,d,n,p):f?l(e,d,n,p):u(s,d,n,p),this)}function n(e,n){n?P[e]=n:P=t.extend(P,e)}function a(t){return"string"==typeof t&&t.indexOf("/")>-1}function i(n,a,i){i=i||{};var r,o=this,c=a.length,s=i.prepend&&!i.append,l=0,u=0,f=!1,p=[];if(i.paged){var d=(i.pageNo-1)*i.elemPerPage;a=a.slice(d,d+i.elemPerPage),c=a.length}return i.append||i.prepend||o.html(""),r=t.extend({},i,{append:!i.prepend&&!0,complete:function(t){(++l===c||f)&&(f&&i&&"function"==typeof i.error&&i.error.call(o,p),i&&"function"==typeof i.complete&&i.complete())},success:function(){++u===c&&i&&"function"==typeof i.success&&i.success()},error:function(t){f=!0,p.push(t)}}),s&&a.reverse(),t(a).each(function(){if(e.call(o,n,this,r),f)return!1}),this}function r(t,e,n,a){k[t]?k[t].push({data:n,selection:e,settings:a}):k[t]=[{data:n,selection:e,settings:a}]}function o(t,e,n,a){var i=w[t].clone();f.call(e,i,n,a),"function"==typeof a.success&&a.success()}function c(){return(new Date).getTime()}function s(t){return-1!==t.indexOf("?")?t+"&_="+c():t+"?_="+c()}function l(e,n,a,i){w[e]=null;var r=e;i.overwriteCache&&(r=s(r)),t.ajax({url:r,async:i.async,success:function(r){d(t(r),e,n,a,i)},error:function(t){p(e,n,a,i,t)}})}function u(e,n,a,i){(e.is("script")||e.is("template"))&&(e=t.parseHTML(t.trim(e.html()))),f.call(n,e,a,i),"function"==typeof i.success&&i.success()}function f(e,n,a){h(e=t("
").append(e),n,a),t(this).each(function(){var i=e.children().clone(!0);t("select",i).each(function(n,a){t(this).val(t("select",e).eq(n).val())}),a.beforeInsert&&a.beforeInsert(i,n),a.append?t(this).append(i):a.prepend?t(this).prepend(i):t(this).html("").append(i),a.afterInsert&&a.afterInsert(i,n)}),"function"==typeof a.complete&&a.complete.call(t(this),n)}function p(e,n,a,i,r){var o;for("function"==typeof i.error&&i.error.call(n,r),t(k[e]).each(function(t,e){"function"==typeof e.settings.error&&e.settings.error.call(e.selection,r)}),"function"==typeof i.complete&&i.complete.call(n);k[e]&&(o=k[e].shift());)"function"==typeof o.settings.complete&&o.settings.complete.call(o.selection);void 0!==k[e]&&k[e].length>0&&(k[e]=[])}function d(t,e,n,a,i){var r;for(w[e]=t.clone(),f.call(n,t,a,i),"function"==typeof i.success&&i.success.call(n);k[e]&&(r=k[e].shift());)f.call(r.selection,w[e].clone(),r.data,r.settings),"function"==typeof r.settings.success&&r.settings.success.call(r.selection)}function h(e,n,a){v("data-content",e,n=n||{},a,function(t,e){t.html(O(t,e,"content",a))}),v("data-content-append",e,n,a,function(t,e){t.append(O(t,e,"content",a))}),v("data-content-prepend",e,n,a,function(t,e){t.prepend(O(t,e,"content",a))}),v("data-content-text",e,n,a,function(t,e){t.text(O(t,e,"content",a))}),v("data-innerHTML",e,n,a,function(t,e){t.html(O(t,e,"content",a))}),v("data-src",e,n,a,function(t,e){t.attr("src",O(t,e,"src",a))},function(t){t.remove()}),v("data-href",e,n,a,function(t,e){t.attr("href",O(t,e,"href",a))},function(t){t.remove()}),v("data-alt",e,n,a,function(t,e){t.attr("alt",O(t,e,"alt",a))}),v("data-title",e,n,a,function(t,e){t.attr("title",O(t,e,"title",a))}),v("data-id",e,n,a,function(t,e){t.attr("id",O(t,e,"id",a))}),v("data-css",e,n,a,function(t,e){t.css(O(t,e,"css",a))}),v("data-class",e,n,a,function(t,e){t.addClass(O(t,e,"class",a))}),v("data-link",e,n,a,function(e,n){var i=t(" ");i.attr("href",O(e,n,"link",a)),i.html(e.html()),e.html(i)}),v("data-link-wrap",e,n,a,function(e,n){var i=t(" ");i.attr("href",O(e,n,"link-wrap",a)),e.wrap(i)}),v("data-options",e,n,a,function(e,n){t(n).each(function(){t(" ").attr("value",this).text(this).appendTo(e)})}),y(e,n,a),v("data-value",e,n,a,function(t,e){t.val(O(t,e,"value",a))})}function v(e,n,a,i,r,o){t("["+e+"]",n).each(function(){var n=t(this),c=n.attr(e),s=x(a,c);m(n,s,i)?(n.removeAttr(e),void 0!==s&&r?r(n,s):o&&o(n)):n.remove()})}function m(t,e,n){var a=g(t,n);return(!a.ignoreUndefined||void 0!==e)&&((!a.ignoreNull||null!==e)&&(!a.ignoreEmptyString||""!==e))}function g(e,n){var a={};return e instanceof jQuery&&e.attr("data-binding-options")?(a=t.parseJSON(e.attr("data-binding-options")),e.removeAttr("data-binding-options")):"object"==typeof e&&e.hasOwnProperty("bindingOptions")&&(a=e.bindingOptions),t.extend({},n.bindingOptions,a)}function y(e,n,a){t("[data-template-bind]",e).each(function(){var e=t(this),i=t.parseJSON(e.attr("data-template-bind"));e.removeAttr("data-template-bind"),t(i).each(function(){var i;if(i="object"==typeof this.value?x(n,this.value.data):x(n,this.value),this.attribute){if(!m(this,i,a))return void e.remove();switch(this.attribute){case"content":case"innerHTML":e.html(b(e,i,this));break;case"contentAppend":e.append(b(e,i,this));break;case"contentPrepend":e.prepend(b(e,i,this));break;case"contentText":e.text(b(e,i,this));break;case"options":var r=this;t(i).each(function(){t(" ").attr("value",this[r.value.value]).text(b(e,this[r.value.content],r)).attr("selected",void 0!=typeof this[r.value.selected]&&this[r.value.selected]).appendTo(e)});break;default:e.attr(this.attribute,b(e,i,this))}}})})}function b(t,e,n,a){return n.formatter&&P[n.formatter]?function(a){return P[n.formatter].call(t,e,n.formatOptions,a)}(a):e}function x(t,e){if("this"===e)return t;for(var n,a=e.split("."),i=t;(n=a.shift())&&void 0!==i&&null!=i;)i=i[n];return i}function O(e,n,a,i){var r,o=e.attr("data-format-target");if((o===a||!o&&"content"===a)&&(r=e.attr("data-format"))&&"function"==typeof P[r]){var c=e.attr("data-format-options");return function(a){return P[r].call(e[0],n,c,t.extend({},a))}(i)}return n}var T,w={},k={},P={};n("nestedTemplateFormatter",function(e,n,a){if(n){"string"==typeof n&&"{"===n[0]&&(n=t.parseJSON(n));var i=n.parentElement||"div",r=n.template||n;return n.parentElement?t("<"+i+"/>").loadTemplate(r,e,a):t("<"+i+"/>").loadTemplate(r,e,a).children()}}),t.fn.loadTemplate=e,t.addTemplateFormatter=n}(jQuery);
--------------------------------------------------------------------------------
/loadTemplate.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "loadTemplate",
3 | "title": "jQuery Templating",
4 | "description": "jQuery plugin for loading and using templates. The plugin is designed to be simple yet powerful, and supports loading templates from within the page, or using AJAX to load html files.",
5 | "keywords": [
6 | "templates",
7 | "templating"
8 | ],
9 | "version": "1.5.10",
10 | "author": {
11 | "name": "Paul Burgess and other contributors",
12 | "url": "https://github.com/codepb/jquery-template"
13 | },
14 | "licenses": [
15 | {
16 | "type": "MIT",
17 | "url": "https://github.com/codepb/jquery-template/blob/master/MIT-LICENSE.txt"
18 | }
19 | ],
20 | "bugs": "https://github.com/codepb/jquery-template/issues",
21 | "homepage": "https://github.com/codepb/jquery-template",
22 | "docs": "https://github.com/codepb/jquery-template#jqueryloadtemplate",
23 | "dependencies": {
24 | "jquery": ">=1.8"
25 | }
26 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery.loadtemplate",
3 | "version": "1.5.10",
4 | "main": "dist/jquery.loadTemplate.js",
5 | "author": "Paul Burgess",
6 | "description": "jQuery plugin for loading and using templates. The plugin is designed to be simple yet powerful, and supports loading templates from within the page, or using AJAX to load html files.",
7 | "homepage": "http://codepb.github.io/jquery-template/",
8 | "keywords": [
9 | "templates",
10 | "templating",
11 | "jquery-plugin"
12 | ],
13 | "bugs": "https://github.com/codepb/jquery-template/issues",
14 | "license": "MIT",
15 | "repository" : {
16 | "type" : "git",
17 | "url" : "https://github.com/codepb/jquery-template"
18 | },
19 | "dependencies" : {
20 | "jquery": ">=1.8"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # jQuery.loadTemplate
2 | [](https://cdnjs.com/libraries/jquery.loadtemplate)
3 |
4 | jQuery Template is a jQuery plugin that makes using templates easy and quick. The plugin supports loading HTML files as templates, or taking a jQuery object as the template (usually using script tags to hold the template).
5 |
6 | ## Features
7 |
8 | jQuery.LoadTemplate provides the following:
9 |
10 | - Define Templates to display data
11 | - Provide Formatters to process data into a more readable format
12 | - Cache templates and data client side and pass processing to the client, allowing for a great user experience.
13 | - Powerful but simple syntax, utilising pure html for templates.
14 |
15 | ## Getting Started
16 |
17 | Simply clone the repo. The only file required is the jquery.loadTemplate-version.js file in the jquery.loadTemplate folder. There is also a folder for examples in the same folder. Take a look at the index file in here for examples and the code to create the examples.
18 |
19 | To see examples of usage, visit the project page: [http://codepb.github.io/jquery-template/](http://codepb.github.io/jquery-template/)
20 |
21 | ## Potential Applications
22 |
23 | jQuery.loadTemplate was originally designed with a single page application for a blog in mind. The idea was to create templates for blog posts, post snippets, etc. This could then be called from the client when required, and cached. The post data was sent as a JSON object from the server, and processed into the templates using the plugin. This meant a very light load on the server, and a great user experience, with smooth page transitions, and JavaScript engines doing all the work.
24 |
25 | However I saw many other potential applications for this. Any application that deals with a large amount of data displayed in a regular format, for example search results, live commentary, blogs, online stores, social media sites, and the list could go on.
26 |
27 | ## How it works
28 |
29 | The plugin parses a template using data attributes to populate the data. Simply pass in a JavaScript object, and the plugin does the rest.
30 |
31 | An example template is below:
32 |
33 |
39 |
40 | And to use this do the following:
41 |
42 | $("#template-container").loadTemplate($("#template"),
43 | {
44 | author: 'Joe Bloggs',
45 | date: '25th May 2013',
46 | authorPicture: 'Authors/JoeBloggs.jpg',
47 | post: 'This is the contents of my post'
48 | });
49 |
50 | Similarly the content of the template could be held in a separate html file without the enclosing script tag, and used like the following:
51 |
52 | $("#template-container").loadTemplate("Templates/template.html",
53 | {
54 | author: 'Joe Bloggs',
55 | date: '25th May 2013',
56 | authorPicture: 'Authors/JoeBloggs.jpg',
57 | post: 'This is the contents of my post'
58 | });
59 |
60 | The plugin has a number of data-... attributes that can be used to populate various attributes with the data. There is also the powerful data-template-bind attribute that accepts a JSON object, enabling binding to any attribute, or the content of the element.
61 |
62 | #### Arrays
63 |
64 | You can pass an array of objects instead of a single object and the template will be populated and added to the container for each item in the array. There are options built in that allow you to page the results from an array as well. See the options section below and the included examples.
65 |
66 | ### Data Formatters
67 |
68 | It is also possible to define data formatters. These are assigned through the `$.addTemplateFormatter` method. This function either accepts a map of functions and the keys that they will be referenced by, or a single function with a single key as two separate parameters. Each formatter takes two values, the value being assigned, and a template to use to define how this data is displayed. The data-format-options may be empty. Example usage of this is below:
69 |
70 | $.addTemplateFormatter("UpperCaseFormatter",
71 | function(value, template) {
72 | return value.toUpperCase();
73 | });
74 |
75 | Alternatively with a map:
76 |
77 | $.addTemplateFormatter({
78 | UpperCaseFormatter : function(value, template) {
79 | return value.toUpperCase();
80 | },
81 | LowerCaseFormatter : function(value, template) {
82 | return value.toLowerCase();
83 | },
84 | SameCaseFormatter : function(value, template) {
85 | if(template == "upper") {
86 | return value.toUpperCase();
87 | } else {
88 | return value.toLowerCase();
89 | }
90 | }
91 | });
92 |
93 | To call these formatters, simply the following will work:
94 |
95 |
97 |
98 | Formatters must be added before they are used else a template will not be able to access them. Formatters are used at the time of populating the data. You can also target any binding with the "data-format-target". The value of this is the binding to target so to target data-alt binding, set 'data-format-target="alt"'.
99 |
100 | ### Bindings
101 | There are a number of different bindings and ways to bind the data. The following attributes are available:
102 |
103 | - "data-innerHTML" (>= 1.4.5) - binds the value supplied to the content (innerHTML) of the element (uses $(elem).html(value))
104 | - "data-content" - alias for the newer "data-innerHTML"
105 | - "data-content-text" - binds the value supplied to the content of the element as text (uses $(elem).text(value))
106 | - "data-content-append" - appends the value to the end of the element (uses $(elem).append(value))
107 | - "data-content-prepend" - prepends the value to the beginning of the element (uses $(elem).prepend(value))
108 | - "data-id" - sets the id of the element to the value provided (uses $(elem).attr("id", value));
109 | - "data-href" - sets the href value of the element to the value provided (uses $(elem).attr("href", value));
110 | - "data-alt" - sets the alt value of the element to the value provided (uses $(elem).attr("alt", value));
111 | - "data-value" - sets the value attribute of the element to the value provided (uses $(elem).val(value))
112 | - "data-class" - sets the class attribute of the element to the value provided (uses $(elem).class(value))
113 | - "data-css" - sets the CSS attribute of the element to the value provided (uses $(elem).css(value))
114 | - "data-link" - sets the innerHtml of the element to be a link to the value provided (wraps the content in an <a> tag).
115 | - "data-link-wrap" - wraps the element in a link to the value provided. Same as "data-link", but the <a> tag wraps the element as well as the content.
116 | - "data-options" - adds options to a select box. The value for this should reference an array of strings, each option will be output as a separate option. The value will be the same as the displayed text for each option. For a more powerful version of this look at the data-template-bind option.
117 |
118 | On top of the attributes above, there is also a "data-template-bind" attribute. This is designed to handle more complex situations and allows a wide range of control. The attribute takes a JSON string and allows multiple bindings and options to be set in the one attribute.
119 |
120 | The "data-template-bind" value should be an array of objects. Each object represents one complete binding. Each object can contain the following properties:
121 |
122 | - "value" (required) - The property representing the value to bind to.
123 | - "attribute" (required) - The attribute to bind to. This can be any attribute accepted by the jQuery.attr() method or one of the following: "content" - same as data-content, binds the innerHTML, "contentAppend" - same as data-append, appends the value, "contentPrepend" - same as data-prepend, prepends the value, "options" - same as data-options, but provides greater control. The value attribute for this is an object with a value property and a content property, and this will bind the value of the option to the value property, and the innerText of the option to the content property.
124 | - "formatter" (optional) - provides the formatter to apply to the specific binding. Multiple different attributes can use different formatters using this syntax.
125 | - "formatOptions" (optional) - the options to pass to the formatter applied.
126 |
127 | An example of using the "data-template-bind" attribute would be the following:
128 |
129 |
133 |
134 | ### Options
135 |
136 | There are a number of options the plugin accepts. These can be set by passing an object containing the settings you would like to set as the third parameter to .loadTemplate:
137 |
138 | $(container).loadTemplate(template, data, { append: true, elemPerPage: 20 });
139 |
140 | The full list of options are:
141 |
142 | - "overwriteCache" (default false) - Whether to ignore the cache and reload the template (if you've previously loaded the template, but it might have changed, you'll want to set this to true.
143 | - "async" (default true) - Whether to load templates asynchronously (if templates require an Ajax call)
144 | - "complete" (default null) - Callback function to call on complete. Will always be called regardless of success or failure.
145 | - "success" (default null) - Callback function to call on successful completion.
146 | - "error" (default, outputting error message to template container) - Callback function to call on error.
147 | - "errorMessage" (default "There was an error loading the template.") - Error message for the default error callback to use. This will not be used if you set an error callback function.
148 | - "isFile" (default undefined) - flag to help speed up the process of deciding where to load the template from. Set to true if the template is an external file to load via ajax, false if it's a jQuery selector for an element in the document. Default undefined means the plugin will check first in the document, then attempt to load external file.
149 | - "paged" (default false) - A boolean flag to indicate whether arrays should be paged.
150 | - "pageNo" (default 1) - An integer for which page to display if the data is being paged.
151 | - "elemPerPage" (default 10) - The number of elements to display per page if the data is being paged.
152 | - "append" (default false) - If set to true, the template will be appended to the element rather than replacing the contents of the element.
153 | - "prepend" (default false) - If set to true, the template will be prepended to the element rather than replacing the contents of the element. The append option takes priority over prepend, so if both options are set to true, the element is appended and not prepended.
154 | - "beforeInsert" (default null) - Callback function to be called before inserting the template into the document. The format of the function is function($elem) where $elem is the jQuery object of the populated template about to be inserted into the document.
155 | - "afterInsert" (default null) - As above, a callback function to be called after inserting the template into the document. The format is the same as above.
156 | - "bindingOptions" (default all flags false): add flags to ignore certain types of values. {"ignoreUndefined": false, "ignoreNull": false, "ignoreEmptyString": false}. The flags you set here, are overwritten on an element level by those specified in a template with a "data-binding-options" or a "data-template-bind" attribute. Examples can be found in the Examples/OptionalBinding folder.
157 |
158 | ## Future Plans
159 |
160 | I would like to develop the plugin further so it would be possible to watch the objects holding the data, so any changes to the data would be reflected in the UI. This would have to be simple, lightweight, and ideally would work just with natural JavaScript objects. I also welcome any ideas as to how the plugin could be improved.
161 |
--------------------------------------------------------------------------------
/tests/files/bindingOptions.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | var data = [{
4 | "title": "Super Mario World 2",
5 | "subtitle": "Yoshi's Island"
6 | },{
7 | "title": "The Little Prince",
8 | "subtitle": null
9 | },{
10 | "title": "The Da Vinci Code"
11 | },{
12 | "title": "Lord of the Flies",
13 | "subtitle": ""
14 | }];
15 |
16 | function testLoadWithFunctionParameters() {
17 | $("#bindingOptionsContainer").loadTemplate($("#templateWithoutBindingOptions"), data, {bindingOptions: {"ignoreUndefined": true, "ignoreNull": true, "ignoreEmptyString": true}});
18 | }
19 |
20 | function testLoadWithTemplateAttributes() {
21 | $("#bindingOptionsContainer").loadTemplate($("#templateWithBindingOptions"), data);
22 | }
23 |
24 | function testLoadTemplateWithBindingOptionsInDataTemplateBindAttribute() {
25 | $("#bindingOptionsContainer").loadTemplate($("#templateWithBindingOptionsAsDataBindTemplateAttribute"), data);
26 | }
27 |
28 | function test(assert, async) {
29 | return [
30 | {
31 | name: "jQuery Loaded",
32 | test: function () {
33 | assert(jQuery);
34 | }
35 | },
36 | {
37 | name: "loadTemplate Loaded",
38 | test: function () {
39 | assert(typeof $.fn.loadTemplate === 'function');
40 | }
41 | },
42 | {
43 | name: "Load template with binding options as function parameters",
44 | test: function () {
45 | testLoadWithFunctionParameters();
46 |
47 | var childs = $('#bindingOptionsContainer h4').length;
48 | assert(childs === 1);
49 | }
50 | },
51 | {
52 | name: "Load template with binding options as template attributes",
53 | setup: function () {
54 | $('#bindingOptionsContainer').empty();
55 | },
56 | test: function () {
57 | testLoadWithTemplateAttributes();
58 |
59 | var childs = $('#bindingOptionsContainer h4').length;
60 | assert(childs === 1);
61 | }
62 | },
63 | {
64 | name: "Load template with binding options in 'data-template-bind' attribute",
65 | setup: function () {
66 | $('#bindingOptionsContainer').empty();
67 | },
68 | test: function () {
69 | testLoadTemplateWithBindingOptionsInDataTemplateBindAttribute();
70 |
71 | var childs = $('#bindingOptionsContainer h4').length;
72 | assert(childs === 1);
73 | }
74 | }];
75 | }
76 |
77 | addTests(test);
78 |
79 | })();
80 |
--------------------------------------------------------------------------------
/tests/files/callback.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var data = [
3 | {
4 | "name" : "test1",
5 | "id" : 1
6 | },
7 | {
8 | "name" : "test2",
9 | "id" : 2
10 | },
11 | {
12 | "name" : "test3",
13 | "id" : 3
14 | },
15 | {
16 | "name" : "test4",
17 | "id" : 4
18 | },
19 | {
20 | "name" : "test5",
21 | "id" : 5
22 | }
23 | ];
24 |
25 | function callback(assert){
26 | var beforeInsertCounter = 0,
27 | afterInsertCounter = 0,
28 | successCounter = 0,
29 | errorCounter = 0,
30 | completeCounter = 0;
31 |
32 | var sequence = [];
33 | var elemSequence = [];
34 | return {
35 | name: "Number of Call Callbacks",
36 | setup: function(){
37 | $("#render").loadTemplate("#template", data, {
38 | beforeInsert : function(elem){
39 | sequence.push('before');
40 |
41 | if ($('#render').children().length === beforeInsertCounter) {
42 | elemSequence.push(true);
43 | }
44 |
45 | ++beforeInsertCounter;
46 |
47 |
48 | },
49 | afterInsert : function(elem){
50 | sequence.push('after');
51 |
52 | ++afterInsertCounter;
53 |
54 | if ($('#render').children().length === afterInsertCounter) {
55 | elemSequence.push(true);
56 | }
57 |
58 | },
59 | complete : function(){
60 | sequence.push('complete');
61 | ++completeCounter;
62 | },
63 | success : function(){
64 | sequence.push('success');
65 | ++successCounter;
66 | },
67 | error : function(){
68 | sequence.push('error');
69 | ++errorCounter;
70 | }
71 | });
72 | },
73 | test: function () {
74 |
75 | //make sure every callback is being called as expected
76 |
77 | //before Insert & after Insert counter == 5
78 | //if we have an array of elements before & after inserts will be
79 | //called before and after inserting each element
80 | assert("Before Insert Counter 5 = " + beforeInsertCounter, 5 === beforeInsertCounter);
81 | assert("After Insert Counter 5 = " + afterInsertCounter, 5 === afterInsertCounter);
82 |
83 | //no error expected
84 | assert("Error Counter 0 = " + errorCounter, 0 === errorCounter);
85 |
86 | //those should be called once
87 | assert("Success Counter 1 = " + successCounter, 1 === successCounter);
88 | assert("Complete Counter 1 = " + completeCounter, 1 === completeCounter);
89 |
90 | //sequence by now should be
91 | var expected = 'before,after,before,after,before,after,before,after,before,after,complete,success';
92 | var got = sequence.toString();
93 | assert("Sequence " + got + " = " + expected, expected == got );
94 |
95 | assert("Element Sequence Call 10 == " + elemSequence.length, elemSequence.length === 10);
96 |
97 | }
98 | };
99 | }
100 |
101 | addTests(callback);
102 |
103 | })();
104 |
--------------------------------------------------------------------------------
/tests/files/error.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | var data = [
3 | {
4 | "name": "test1",
5 | "id": 1
6 | },
7 | {
8 | "name": "test2",
9 | "id": 2
10 | },
11 | {
12 | "name": "test3",
13 | "id": 3
14 | },
15 | {
16 | "name": "test4",
17 | "id": 4
18 | },
19 | {
20 | "name": "test5",
21 | "id": 5
22 | }
23 | ];
24 |
25 | function errorsHandle(assert, async) {
26 | var successCounter = 0,
27 | errorCounter = 0,
28 | completeCounter = 0,
29 | sequence = [];
30 |
31 | return {
32 | name: "Custom Error Callback",
33 | setup: function () {
34 |
35 | },
36 | test: function () {
37 | $("#render").loadTemplate('test.html', data, {
38 | complete: function () {
39 | sequence.push('complete');
40 | ++completeCounter;
41 | //success callback will not be fired
42 | assert("Success Counter 0 = " + successCounter, 0 === successCounter);
43 |
44 | //error should be fired
45 | assert("Error Counter 1 = " + errorCounter, 1 === errorCounter);
46 |
47 | //complete should be called even with errors
48 | assert("Complete Counter 1 = " + completeCounter, 1 === completeCounter);
49 |
50 | //sequence by now should be
51 | var expected = 'error,complete';
52 | var got = sequence.toString();
53 | assert("Sequence " + expected + " = " + got, expected == got);
54 | },
55 | success: function () {
56 | sequence.push('success');
57 | ++successCounter;
58 | },
59 | error: function () {
60 | sequence.push('error');
61 | ++errorCounter;
62 | }
63 | });
64 |
65 |
66 | }
67 | };
68 | }
69 |
70 | function deafultError(assert, async) {
71 | var successCounter = 0,
72 | errorCounter = 0,
73 | completeCounter = 0,
74 | sequence = [];
75 |
76 | return {
77 | name: "Default Error Callback",
78 | setup: function () {
79 |
80 | },
81 | test: function () {
82 | $("#render").loadTemplate('doesnotexists/test.html', data, {
83 | complete: function () {
84 | var got = $('#render').text();
85 | var expected = "There was an error loading the template.";
86 | assert("Error Message " + expected + " = " + got, got == expected);
87 | }
88 | });
89 | }
90 | };
91 | }
92 |
93 | addTests(errorsHandle, deafultError);
94 |
95 | })();
96 |
--------------------------------------------------------------------------------
/tests/files/formatters.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | var data = [
4 | {
5 | "name" : "test1",
6 | "id" : 1
7 | },
8 | {
9 | "name" : "test2",
10 | "id" : 2
11 | },
12 | {
13 | "name" : "test3",
14 | "id" : 3
15 | }
16 | ];
17 |
18 | //data formatters
19 | //single data formatter
20 | $.addTemplateFormatter("singleFormatter",
21 | function(value, template) {
22 | $(this).addClass('red');
23 | return value.toUpperCase();
24 | }
25 | );
26 |
27 | $.addTemplateFormatter("singleFormatter2",
28 | function(value, template) {
29 | return 99;
30 | }
31 | );
32 |
33 | //multiple data formatters
34 | $.addTemplateFormatter({
35 | UpperCaseFormatter : function(value, template) {
36 | $(this).addClass('red');
37 | return value.toUpperCase();
38 | },
39 | idFormatter : function(value, template) {
40 | if (value === 2){
41 | $(this).addClass('yellow');
42 | }
43 | return 'xx';
44 | },
45 | SameCaseFormatter : function(value, template) {
46 | if(template == "upper") {
47 | return value.toUpperCase();
48 | } else {
49 | return value.toLowerCase();
50 | }
51 | }
52 | });
53 |
54 | /**
55 | * general Formatter test
56 | */
57 | function general(assert){
58 | return {
59 | name: "Templates Formatter",
60 | setup: function(){
61 | $("#render").loadTemplate("#template2", data);
62 | },
63 | test: function () {
64 | $('#render div.name').each(function(i){
65 | var val = $(this).text();
66 |
67 | //all names must be in uppercases
68 | var match = data[i].name.toUpperCase();
69 | assert("match " + val + " !== " + match, val === match);
70 |
71 | //upperCaseFormatter manipulate element by adding a red class
72 | //check if we got that correctly
73 | var red = $(this).hasClass('red');
74 | assert("has class red", red);
75 |
76 | //id also has been overriden to 'xx'
77 | var id = $(this).next().text();
78 | assert("xx id", "xx" === id);
79 | });
80 | }
81 | };
82 | }
83 |
84 | /**
85 | * This test make sure that all added formatters will be available
86 | * even if we mix by adding single function formatter or multiple key
87 | * value style
88 | */
89 | function singleFormatter (assert){
90 | return {
91 | name: "Single Formatter",
92 | setup: function(){
93 | $("#render").loadTemplate("#template3", data);
94 | },
95 | test: function () {
96 | $('#render div.name').each(function(i){
97 | var val = $(this).text();
98 |
99 | var match = data[i].name.toUpperCase();
100 | assert("match " + val + " !== " + match, val === match);
101 |
102 | //upperCaseFormatter manipulate element by adding a red class
103 | //check if we got that correctly
104 | var red = $(this).hasClass('red');
105 | assert("has class red", red);
106 |
107 | //id also has been overriden to 'xx'
108 | var id = $(this).next().text();
109 | assert("id xx !== " + id, "xx" === id);
110 |
111 | //hidden value manipulated by singleFormatter2
112 | //using data-format-target to target value attr
113 | var hidVal = $(this).nextAll('.hidden').val();
114 | assert( "Hidden Value " + hidVal +" !== 99", hidVal == 99);
115 |
116 | });
117 | }
118 | };
119 | }
120 |
121 | //TODO
122 | function SameCaseFormatter (assert){
123 |
124 | }
125 |
126 | addTests(general,singleFormatter);
127 |
128 | })();
129 |
--------------------------------------------------------------------------------
/tests/files/general.js:
--------------------------------------------------------------------------------
1 | (function () {
2 |
3 | var data = [
4 | {
5 | "name": "test1",
6 | "id": 1
7 | },
8 | {
9 | "name": "test2",
10 | "id": 2
11 | },
12 | {
13 | "name": "test3",
14 | "id": 3
15 | }
16 | ];
17 |
18 | var nestedData = {
19 | testSingle: "singleString",
20 | testArray: ["testString1", "testString2", "testString3", 4]
21 | }
22 |
23 | function testRender() {
24 | $("#render").loadTemplate("#template", data, {
25 | beforeInsert: function (ele) {
26 | var val = ele.find('.hidden').val();
27 | if (val == 2) {
28 | ele.css({
29 | background: "red"
30 | });
31 | }
32 | }
33 | });
34 | }
35 |
36 | function testRenderSingle(inputData) {
37 | $("#render").loadTemplate("#template", inputData);
38 | }
39 |
40 | function test(assert, async) {
41 | return [
42 | {
43 | name: "jQuery Loaded",
44 | test: function () {
45 | assert(jQuery);
46 | }
47 | },
48 | {
49 | name: "loadTemplate Loaded",
50 | test: function () {
51 | assert(typeof $.fn.loadTemplate === 'function');
52 | }
53 | },
54 | {
55 | name: "Simple Rendering",
56 | test: function () {
57 | testRender();
58 | //we rendered 3 elements
59 | var childs = $('#render').children().length;
60 | assert(childs === data.length);
61 | }
62 | },
63 | {
64 | name: "Override Rendering",
65 | test: function () {
66 | testRender();
67 | //we rendered another 3 elements should overwrite
68 | var childs = $('#render').children().length;
69 | assert(childs === data.length);
70 | }
71 | },
72 | {
73 | name: "Append Elements To the View",
74 | setup: function () {
75 | $("#render").loadTemplate("#template", [
76 | {
77 | name: "befor last",
78 | id: 4
79 | },
80 | {
81 | name: "last",
82 | id: 5
83 | }
84 | ], { append: true });
85 | },
86 | test: function () {
87 | //append 2 more elements so we should get 5
88 | var childs = $('#render').children().length;
89 | assert("5 elements", childs === 5);
90 | //must be last element
91 | var val = $('#render div.container:last input.hidden').val();
92 | assert("last element id", val == 5);
93 | }
94 | },
95 | {
96 | name: "Prepend Elements To the View",
97 | setup: function () {
98 | $("#render").loadTemplate("#template", [
99 | {
100 | name: "first",
101 | id: 1
102 | },
103 | {
104 | name: "second",
105 | id: 999
106 | }
107 | ], { prepend: true });
108 | },
109 | test: function () {
110 | //we prepend another 2 element = 7 elements total
111 | var childs = $('#render').children().length;
112 | assert("7 elements", childs === 7);
113 |
114 | //since it's a prepend "first" must be first element
115 | var ele = $('#render').children().first();
116 | var id = ele.find('input.hidden').val();
117 | var name = ele.find(".name").text();
118 | assert("first id 1 !== " + id, id == 1);
119 | assert("first name 'first' !== " + name, "first" == name);
120 | }
121 | },
122 | {
123 | name: "Bind Value",
124 | test: function () {
125 | testRenderSingle(data[0]);
126 | var val = $('#render input.hidden').val();
127 | assert(val + " != " + data[0]['id'], val == data[0]['id']);
128 | }
129 | },
130 | {
131 | name: "Paged Render First Page",
132 | setup: function () {
133 | $("#render").loadTemplate("#template", data, {
134 | paged: true,
135 | pageNo: 1,
136 | elemPerPage: 2
137 | });
138 | },
139 | test: function () {
140 | //we should have 2 elements
141 | var childs = $('#render').children();
142 | assert('2 elements', childs.length === 2);
143 | //first id is 1
144 | var val = childs.first().find('input.hidden').val();
145 | assert('match first id', val == 1);
146 | //second id is 2
147 | var val = childs.last().find('input.hidden').val();
148 | assert('match second id', val == 2);
149 | }
150 | },
151 | {
152 | name: "Paged Render Second Page",
153 | setup: function () {
154 | $("#render").loadTemplate("#template", data, {
155 | paged: true,
156 | pageNo: 2,
157 | elemPerPage: 2
158 | });
159 | },
160 | test: function () {
161 | //we expect 1 element "the last one"
162 | var childs = $('#render').children();
163 | assert('One element', childs.length === 1);
164 | //it's id is 3
165 | var val = childs.last().find('input.hidden').val();
166 | assert('match last id', val == 3);
167 | }
168 | },
169 | {
170 | name: 'Test binding to "this"',
171 | test: function () {
172 | $("#render").loadTemplate("#thisTemplate", nestedData.testArray);
173 | var childs = $('#render').children();
174 | assert('All four rendered', childs.length === 4);
175 | assert('Binding to "this"', $(childs[0]).text() === nestedData.testArray[0]);
176 | }
177 | },
178 |
179 | {
180 | name: "Test nesting templates",
181 | test: function () {
182 | $("#render").loadTemplate("#nestTemplate", nestedData);
183 | var $rendered = $('#render');
184 | assert('Single Worked', $(".single-attribute", $rendered).text() === nestedData.testSingle);
185 | var $templateBind = $('.template-bind-nest ul', $rendered);
186 | assert('data-template-bind nest worked', $templateBind.length === 1 && $templateBind.children().length == 4);
187 | var $attribute = $('.attribute-nest ul', $rendered);
188 | assert('attributes nest worked"', $attribute.length === 1 && $attribute.children().length == 4);
189 | }
190 | }
191 | //,
192 | //{
193 | // name : "Find what is async",
194 | // test : function(){
195 | // setTimeout(async(function () { // wru.async
196 | // assert(1==1); // wru.assert
197 | // }), 1000);
198 | // }
199 | //}
200 | ];
201 | }
202 |
203 | addTests(test);
204 |
205 | })();
206 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | loadTemplate Test Suite
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
22 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
84 |
85 |
86 |
93 |
94 |
95 |
102 |
103 |
104 |
108 |
109 |
110 |
115 |
116 |
120 |
121 |
125 |
126 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/tests/phantom.js:
--------------------------------------------------------------------------------
1 |
2 | var page = require('webpage').create();
3 | var fs = require('fs');
4 |
5 | page.onConsoleMessage = function (msg, line, source) {
6 | console.error(msg);
7 | };
8 |
9 | page.onClosing = function(){
10 | console.log('closed');
11 | };
12 |
13 | function createReport(obj) {
14 |
15 | var totalErrors = 0,
16 | totalPasses = 0;
17 |
18 | console.log("======================================");
19 | for (key in obj){
20 | var report = obj[key],
21 | reportText = " \n " + key + " ";
22 |
23 | totalPasses += report.passes;
24 | totalErrors += report.fails;
25 | if (report.fails > 0) {
26 | reportText += '.....(faild)';
27 | for (name in report.errors){
28 | reportText += '\n -> ' + name;
29 | var list = report.errors[name];
30 | for(var i = 0; i < list.length; i++){
31 | reportText += '\n --> ' + list[i];
32 | }
33 | }
34 | } else {
35 | reportText += '......(ok)';
36 | }
37 | console.log(reportText);
38 | }
39 |
40 | console.log("\n======================================");
41 | console.log("= Done Testing");
42 | console.log("= Total Tests : " + (totalPasses + totalErrors));
43 | console.log("= Passed Tests: " + totalPasses);
44 | console.log("= Failed Tests: " + totalErrors);
45 |
46 | //page.render('view.png');
47 | phantom.exit(totalErrors);
48 | }
49 |
50 | //this should be run on a server
51 | //but for now local file seems ok
52 | page.open('index.html', function(status) {
53 | //var timeout = setTimeout(function(){
54 | var evaluate = function(){
55 | return page.evaluate(function(phantom) {
56 | if (phantomExit) {
57 | return phantomReport;
58 | }
59 | return false;
60 | });
61 | };
62 |
63 | var timeout;
64 | var interv = setInterval(function(){
65 | var ret = evaluate();
66 | if (typeof ret === 'object') {
67 | createReport(ret);
68 | clearInterval(this);
69 | if (timeout) clearTimeout(timeout);
70 | }
71 | },100);
72 |
73 | //on timeout
74 | timeout = setTimeout(function(){
75 | clearInterval(interv);
76 | createReport(ret);
77 | },10000);
78 |
79 | });
80 |
--------------------------------------------------------------------------------
/tests/testRunner.js:
--------------------------------------------------------------------------------
1 | var testObject = [];
2 | var scriptTestFiles = [];
3 | var currnetTestFileCounter = 0;
4 | var phantomReport = {};
5 | var phantomExit = false;
6 |
7 | function addTests() {
8 |
9 | for (var i = 0; i < arguments.length; i++){
10 | testObject.push(arguments[i]);
11 | }
12 |
13 | var file = scriptTestFiles[currnetTestFileCounter++];
14 | //hack wru to get better reporting
15 | testObject.push(function(){
16 | return {
17 | teardown :function(){
18 | var ele = $('#wru').find('div:last');
19 | var report = $(' Done Testsing ' + file + '
');
20 | $('#wru').append(report);
21 |
22 | var elements = $('.testfile:last').prevUntil('.testfile'),
23 | errors = 0,
24 | passes = 0;
25 |
26 | var phantom = phantomReport[file] = {
27 | errors : {},
28 | passes : 0,
29 | fails : 0
30 | };
31 |
32 | elements.each(function(){
33 | var error = $(this).find('li');
34 | errors += error.length;
35 | if (error.length > 0) {
36 | var errorText = $(this).children().first().text();
37 | phantom.errors[errorText] = [];
38 | $(error).each(function(){
39 | phantom.errors[errorText].push($(this).text());
40 | });
41 | }
42 |
43 | var pass = $(this).find('span:first').text();
44 | if (pass) {
45 | var n = /\w+\s+\((\d+)/.exec(pass);
46 | if (n) passes += parseInt(n[1]);
47 | }
48 | });
49 |
50 | phantom.passes = passes;
51 | phantom.fails = errors;
52 | if (errors > 0) {
53 | report.text(report.text() + " ( " + errors + " Errors & " + passes + " Passed )").css({
54 | background : "red",
55 | color : "white"
56 | });
57 | } else {
58 | report.text(report.text() + " ( " + passes + " Tests Passed )");
59 | }
60 |
61 | ele.remove();
62 | }
63 | }
64 | return false;
65 | });
66 | }
67 |
68 | function loadTestFile(src){
69 | document.write('<' + 'script src="' + src + '"' +
70 | ' type="text/javascript"><' + '/script>');
71 | }
72 |
73 | function runTests(files) {
74 | $(files).each(function(i,val){
75 | scriptTestFiles.push(val);
76 | loadTestFile(val);
77 | });
78 | }
79 |
80 | function wru(wru){
81 | var assert = wru.assert,
82 | async = wru.async,
83 | log = wru.log;
84 |
85 | testObject.push(function(assert,async){
86 | return {
87 | name : ' ',
88 | teardown : function(){
89 | $('.final').parent().hide();
90 | phantomExit = true;
91 | }
92 | };
93 | });
94 |
95 | var run = function(obj){
96 | for (var i = 0; i < obj.length; i++){
97 | var action = obj[i];
98 | if ($.isArray(action)) {
99 | run(action);
100 | continue;
101 | }
102 | var ret = obj[i](assert,async);
103 | if (ret) wru.test(ret);
104 | }
105 | };
106 |
107 | run(testObject);
108 | //remove wru headers as we generated our own header reports
109 | $('#wru').find('strong:not(:first)').remove();
110 | $('#wru').find('.fail strong').remove();
111 | }
112 |
--------------------------------------------------------------------------------
/tests/wru.js:
--------------------------------------------------------------------------------
1 | wru(function(Y){function j(){A=K.call(m);if(A){if(typeof A=="function"){A={name:A[S]||"anonymous",test:A}}(P=l(l(Z.node,"div"),"span"))[E]=((ag(A,S)&&A[S])||(ag(A,e)&&A[e])||Q)+i+i;a=[];u=[];T=[];ab={};b("setup");T[ah]||b("test");N||r()}else{t()}}function p(aj){try{return O.call(h,aj)}catch(ai){return h.createElement(aj)}}function l(ai,aj){return ai.appendChild(p(aj))}function g(ai){P[E]=x.call(P[E],0,-2)+i+ai}function t(){var ak=Z.node.insertBefore(p("div"),Z.node.firstChild),al,aj,ai;if(ad){ai=aj="error";al="There Are Errors: "+ad}else{if(C){ai=aj="fail";al=C+" Tests Failed"}else{ai=aj="pass";al="Passed "+s+" Tests"}}Z.status=ai;ak[E]=""+al+" ";ak.className=aj}function G(){var ai=this.lastChild.style;ai.display=ai.display=="none"?"block":"none"}function c(ai){P[E]+="";(P.onclick=G).call(P)}function r(){f();s+=a[ah];C+=u[ah];ad+=T[ah];g("("+v.call([a[ah],M=u[ah],T[ah]],", ")+")");P=P.parentNode;T[ah]?c(T,W="error"):(M?c(u,W="fail"):W="pass");P.className=W;M=0;W=i;j()}function b(ai){if(ag(A,ai)){try{A[ai](ab)}catch(aj){aa.call(T,i+aj)}}}function ag(aj,ai){return q.call(aj,ai)}function w(){return F()<0.5?-1:1}function f(){if(R){H(R);R=0}b("teardown")}var Z={timeout:y,assert:function U(aj,ai){if(arguments[ah]==1){ai=aj;aj=Q}z=I;aa.call(ai?a:u,W+aj);return ai},async:function V(ak,an,al,am){var ai=al||Z.timeout||(Z.timeout=y);am=++N;if(typeof ak=="function"){ai=an||Z.timeout;an=ak;ak="asynchronous test #"+am}al=X(function(){am=0;aa.call(u,ak);--N||(R=X(r,0))},L(ai)||Z.timeout);return function aj(){if(!am){return}z=ae;W=ak+": ";try{an.apply(this,arguments)}catch(ao){z=I;aa.call(T,W+ao)}W=i;if(z){H(al);--N||(R=X(r,0))}}},test:function n(ai,aj){Z.after=aj||function(){};m=J.apply(m,[ai]);Z.random&&af.call(m,w);N||j()}},I=true,ae=!I,y=100,i=" ",Q="unknown",ah="length",S="name",e="description",D="",d=" ",k="\\|/-",q=Z.hasOwnProperty,W=i,ac=W.charAt,x=W.slice,m=[],J=m.concat,v=m.join,aa=m.push,K=m.shift,af=m.sort,N=0,M=0,s=0,C=0,ad=0,R=0,E="innerHTML",h=Y.document,O=h.createElement,B,L,F,X,H,A,P,a,u,T,ab,z;B=Y.Math;L=B.abs;F=B.random;X=Y.setTimeout;H=Y.clearTimeout;Z.node=(h.getElementById("wru")||h.body||h.documentElement);Y.setInterval(function(){N&&g(ac.call(k,M++%4))},y);undefined;Z.log=function o(aj,ai){ai?alert(aj):(typeof console!="undefined")&&console.log(aj)};y*=y;Z.random=ae;return Z}(this));
2 |
--------------------------------------------------------------------------------