├── jQueryTmpl
├── Exception.php
├── Data
│ ├── Exception.php
│ └── Factory.php
├── Markup.php
├── Element
│ ├── Exception.php
│ ├── TypeControl.php
│ ├── TypeInline.php
│ ├── Comment.php
│ ├── TypeBlock.php
│ ├── NoOp.php
│ ├── ValueNotEscaped.php
│ ├── Base.php
│ ├── ValueEscaped.php
│ ├── Else.php
│ ├── TypeRenderable.php
│ ├── Factory.php
│ ├── Tmpl.php
│ ├── If.php
│ └── Each.php
├── Markup
│ ├── Exception.php
│ ├── Factory.php
│ ├── String.php
│ └── File.php
├── Parser
│ ├── Exception.php
│ └── Factory.php
├── Tokenizer
│ ├── Exception.php
│ └── Factory.php
├── Token
│ ├── TypeControl.php
│ ├── TypeInline.php
│ ├── Else.php
│ ├── NoOp.php
│ ├── Tmpl.php
│ ├── Comment.php
│ ├── ValueEscaped.php
│ ├── ValueNotEscaped.php
│ ├── Factory.php
│ ├── TypeBlock.php
│ ├── IfEnd.php
│ ├── EachEnd.php
│ ├── IfStart.php
│ ├── EachStart.php
│ └── Base.php
├── Factory.php
├── Tag
│ ├── IfEnd.php
│ ├── Comment.php
│ ├── EachEnd.php
│ ├── IfStart.php
│ ├── ValueEscaped.php
│ ├── ValueEscapedShorthand.php
│ ├── ValueNotEscaped.php
│ ├── Else.php
│ ├── EachStart.php
│ └── Tmpl.php
├── Element.php
├── Token.php
├── Tag.php
├── Parser.php
├── Tokenizer.php
└── Data.php
├── test
├── phpunit.xml
├── Tag
│ ├── TestCase.php
│ ├── IfEndTest.php
│ ├── EachEndTest.php
│ ├── CommentTest.php
│ ├── IfStartTest.php
│ ├── ValueEscapedTest.php
│ ├── ValueEscapedShorthandTest.php
│ ├── ValueNotEscapedTest.php
│ ├── EachStartTest.php
│ ├── ElseTest.php
│ └── TmplTest.php
├── Element
│ ├── CommentTest.php
│ ├── NoOpTest.php
│ ├── TestCase.php
│ ├── ValueEscapedTest.php
│ ├── ValueNotEscapedTest.php
│ ├── IfTest.php
│ ├── EachTest.php
│ └── TmplTest.php
├── TokenTest.php
├── TokenizerTest.php
├── DataTest.php
└── ParserTest.php
├── example.js
├── LICENSE.txt
├── jQueryTmpl.php
├── example.php
└── README.md
/jQueryTmpl/Exception.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ./
5 |
6 |
7 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/TypeControl.php:
--------------------------------------------------------------------------------
1 | _parser = $parser;
10 | }
11 |
12 | abstract public function parseTokens(array $tokens);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/jQueryTmpl/Markup/String.php:
--------------------------------------------------------------------------------
1 | _template = (string)$template;
10 | }
11 |
12 | public function getTemplate()
13 | {
14 | return $this->_template;
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/jQueryTmpl/Token/TypeBlock.php:
--------------------------------------------------------------------------------
1 | create(),
13 | $pFactory->create()
14 | );
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/jQueryTmpl/Token/IfEnd.php:
--------------------------------------------------------------------------------
1 | _token = $token;
10 | }
11 |
12 | public function render()
13 | {
14 | return $this->_token->getRawContent();
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/test/Tag/TestCase.php:
--------------------------------------------------------------------------------
1 | preg_match_all($regex, $string, $matches, PREG_OFFSET_CAPTURE),
14 | 'match' => $matches[0]
15 | );
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/IfEnd.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | Movie Name |
5 | Release Year |
6 | Director |
7 | Rating |
8 |
9 |
10 |
11 | {{each movies}}
12 |
13 | | ${this.name} |
14 | ${this.year} |
15 | ${this.director} |
16 | {{tmpl "#ratingTemplate"}} |
17 |
18 | {{/each}}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/ValueNotEscaped.php:
--------------------------------------------------------------------------------
1 | _token = $token;
10 | }
11 |
12 | public function render()
13 | {
14 | $options = $this->_token->getOptions();
15 | return $this->_data->getValueOf($options['name']);
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/Base.php:
--------------------------------------------------------------------------------
1 | _data = $data;
11 | return $this;
12 | }
13 |
14 | public function setCompiledTemplates(array $compiledTemplates)
15 | {
16 | $this->_compiledTemplates = $compiledTemplates;
17 | return $this;
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/ValueEscaped.php:
--------------------------------------------------------------------------------
1 | _token = $token;
10 | }
11 |
12 | public function render()
13 | {
14 | $options = $this->_token->getOptions();
15 | return htmlspecialchars($this->_data->getValueOf($options['name']), ENT_COMPAT, 'UTF-8');
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/test/Element/CommentTest.php:
--------------------------------------------------------------------------------
1 | _elementFactory->createInline
10 | (
11 | 'Comment',
12 | new jQueryTmpl_Token_Comment(0, array(), '{{! Some Comment !}}')
13 | );
14 |
15 | $this->assertEquals
16 | (
17 | '',
18 | $element->setData($this->_data)->render()
19 | );
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/Else.php:
--------------------------------------------------------------------------------
1 | _token = $token;
10 | }
11 |
12 | public function displayNextBlock()
13 | {
14 | $options = $this->_token->getOptions();
15 |
16 | if ($options == array())
17 | {
18 | // Final {{else}} tag
19 | return true;
20 | }
21 |
22 | return (bool)$this->_data->getValueOf($options['name']);
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/test/Element/NoOpTest.php:
--------------------------------------------------------------------------------
1 | string and {{not a tag}}.';
10 |
11 | $element = $this->_elementFactory->createInline
12 | (
13 | 'NoOp',
14 | new jQueryTmpl_Token_NoOp(0, array(), $str)
15 | );
16 |
17 | $this->assertEquals
18 | (
19 | $str,
20 | $element->setData($this->_data)->render()
21 | );
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/IfStart.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
28 | );
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/ValueEscaped.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
28 | );
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/ValueEscapedShorthand.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
28 | );
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/ValueNotEscaped.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
28 | );
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/jQueryTmpl/Token/Base.php:
--------------------------------------------------------------------------------
1 | _level = $level;
12 | $this->_options = $options;
13 | $this->_rawContent = $rawContent;
14 | }
15 |
16 | public function getLevel()
17 | {
18 | return $this->_level;
19 | }
20 |
21 | public function getOptions()
22 | {
23 | return $this->_options;
24 | }
25 |
26 | public function getRawContent()
27 | {
28 | return $this->_rawContent;
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/TypeRenderable.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
35 | );
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element.php:
--------------------------------------------------------------------------------
1 | _getFileContents($filename);
10 | }
11 |
12 | public function getTemplate()
13 | {
14 | return $this->_template;
15 | }
16 |
17 | private function _getFileContents($filename)
18 | {
19 | try
20 | {
21 | $this->_template = file_get_contents($filename);
22 | }
23 | catch (Exception $e)
24 | {
25 | throw new jQueryTmpl_Markup_Exception($e->getMessage());
26 | }
27 |
28 | if ($this->_template == FALSE)
29 | {
30 | throw new jQueryTmpl_Markup_Exception("Could not open markup file '$filename'.");
31 | }
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/Factory.php:
--------------------------------------------------------------------------------
1 | create());
11 | $element
12 | ->parseTokens($tokens);
13 |
14 | return $element;
15 | }
16 |
17 | public function createControl($type, jQueryTmpl_Token $token)
18 | {
19 | $class = "jQueryTmpl_Element_$type";
20 | $element = new $class();
21 | $element
22 | ->parseToken($token);
23 |
24 | return $element;
25 | }
26 |
27 | public function createInline($type, jQueryTmpl_Token $token)
28 | {
29 | $class = "jQueryTmpl_Element_$type";
30 | $element = new $class();
31 | $element
32 | ->parseToken($token);
33 |
34 | return $element;
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/Tmpl.php:
--------------------------------------------------------------------------------
1 | _token = $token;
10 | }
11 |
12 | public function render()
13 | {
14 | $options = $this->_token->getOptions();
15 |
16 | $elements = $this->_compiledTemplates[$options['template']];
17 | $localData = $this->_data->getDataSice($options['data']);
18 |
19 | if (empty($elements) || empty($localData))
20 | {
21 | return '';
22 | }
23 |
24 | $rendered = '';
25 |
26 | foreach ($elements as $element)
27 | {
28 | $rendered .= $element
29 | ->setData($localData)
30 | ->setCompiledTemplates($this->_compiledTemplates)
31 | ->render();
32 | }
33 |
34 | return $rendered;
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/EachStart.php:
--------------------------------------------------------------------------------
1 | trim($matches[1])
30 | );
31 | }
32 |
33 | // Matched optional params as well
34 | return array
35 | (
36 | 'name' => trim($matches[4]),
37 | 'index' => trim($matches[2]),
38 | 'value' => trim($matches[3])
39 | );
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tokenizer/Factory.php:
--------------------------------------------------------------------------------
1 | addTag(new jQueryTmpl_Tag_Comment())
15 | // Each block
16 | ->addTag(new jQueryTmpl_Tag_EachStart())
17 | ->addTag(new jQueryTmpl_Tag_EachEnd())
18 | // If/Else Block
19 | ->addTag(new jQueryTmpl_Tag_IfStart())
20 | ->addTag(new jQueryTmpl_Tag_Else())
21 | ->addTag(new jQueryTmpl_Tag_IfEnd())
22 | // Tmpl Tag
23 | ->addTag(new jQueryTmpl_Tag_Tmpl())
24 | // Values
25 | ->addTag(new jQueryTmpl_Tag_ValueEscaped())
26 | ->addTag(new jQueryTmpl_Tag_ValueEscapedShorthand())
27 | ->addTag(new jQueryTmpl_Tag_ValueNotEscaped());
28 |
29 | return $tokenizer;
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/test/TokenTest.php:
--------------------------------------------------------------------------------
1 | assertContainsOnly
23 | (
24 | 'jQueryTmpl_Token',
25 | $tokens
26 | );
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2010 Xiao Yu, @HypertextRanch
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/jQueryTmpl/Data/Factory.php:
--------------------------------------------------------------------------------
1 | getMessage());
14 | }
15 |
16 | if (!($obj instanceof stdClass))
17 | {
18 | throw new jQueryTmpl_Data_Exception('Could not create data object from JSON string');
19 | }
20 |
21 | return new jQueryTmpl_Data
22 | (
23 | $obj,
24 | new jQueryTmpl_Data_Factory()
25 | );
26 | }
27 |
28 | public function createFromArray(array $array)
29 | {
30 | return new jQueryTmpl_Data
31 | (
32 | json_decode
33 | (
34 | json_encode($array)
35 | ),
36 | new jQueryTmpl_Data_Factory()
37 | );
38 | }
39 |
40 | public function createFromStdClass(stdClass $obj)
41 | {
42 | return new jQueryTmpl_Data
43 | (
44 | $obj,
45 | new jQueryTmpl_Data_Factory()
46 | );
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/jQueryTmpl/Token.php:
--------------------------------------------------------------------------------
1 | parseTag()
15 | * @param string $rawContent The entire tag or raw html content
16 | */
17 | public function __construct($level, array $options, $rawContent);
18 |
19 | /**
20 | * The element type that should be created by this token type.
21 | * Closing tags will not create elements and should return an
22 | * empty string.
23 | * @return string Internal name for element type.
24 | */
25 | public function getElementType();
26 |
27 | /**
28 | * Simply gets the values set in the constructor.
29 | * @return integer Level of nesting.
30 | */
31 | public function getLevel();
32 |
33 | /**
34 | * Simply gets the values set in the constructor.
35 | * @return array Hash of options.
36 | */
37 | public function getOptions();
38 |
39 | /**
40 | * Simply gets the values set in the constructor.
41 | * @return string Raw string extracted from template.
42 | */
43 | public function getRawContent();
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag/Tmpl.php:
--------------------------------------------------------------------------------
1 | $this->_extractId($matches[1]),
30 | 'data' => null,
31 | 'options' => null
32 | );
33 | }
34 |
35 | if (count($matches) == 7)
36 | {
37 | return array
38 | (
39 | 'template' => $this->_extractId($matches[6]),
40 | 'data' => trim($matches[5]),
41 | 'options' => null
42 | );
43 | }
44 |
45 | // Matched optional params as well
46 | return array
47 | (
48 | 'template' => $this->_extractId($matches[4]),
49 | 'data' => trim($matches[2]),
50 | 'options' => trim($matches[3])
51 | );
52 | }
53 |
54 | private function _extractId($str)
55 | {
56 | return trim($str, "'\"# \t\n\r\0\x0B");
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/test/Tag/IfEndTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_IfEnd();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{/if}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{/if}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldGetAllTags()
37 | {
38 | $str = '{{/if}} and {{/If}}';
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 2,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | '{{/if}}',
50 | 0
51 | ),
52 | array
53 | (
54 | '{{/If}}',
55 | 12
56 | )
57 | )
58 | ),
59 | $this->_evalRegex($this->_cut->getRegex(), $str)
60 | );
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/test/Tag/EachEndTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_EachEnd();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{/each}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{/each}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldGetAllTags()
37 | {
38 | $str = '{{/each}} and {{/EaCh}}';
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 2,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | '{{/each}}',
50 | 0
51 | ),
52 | array
53 | (
54 | '{{/EaCh}}',
55 | 14
56 | )
57 | )
58 | ),
59 | $this->_evalRegex($this->_cut->getRegex(), $str)
60 | );
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/test/Element/TestCase.php:
--------------------------------------------------------------------------------
1 | Some Text & marks \"'\".",
55 | "\$wtfKey" : "It works!",
56 | "attn" : "!"
57 | }
58 | EOF;
59 |
60 | $this->_data = new jQueryTmpl_Data
61 | (
62 | json_decode($testData),
63 | new jQueryTmpl_Data_Factory()
64 | );
65 |
66 | $this->_elementFactory = new jQueryTmpl_Element_Factory();
67 | }
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tag.php:
--------------------------------------------------------------------------------
1 | _firstToken = array_shift($tokens);
12 | $lastToken = array_pop($tokens);
13 |
14 | // Make sure we have the right types of tokens
15 | if (!($this->_firstToken instanceof jQueryTmpl_Token_IfStart && $lastToken instanceof jQueryTmpl_Token_IfEnd))
16 | {
17 | throw new jQueryTmpl_Element_Exception('Token mismatch, cannot create {{if}} element.');
18 | }
19 |
20 | $this->_elements = $this->_parser->parse($tokens);
21 | }
22 |
23 | public function render()
24 | {
25 | $options = $this->_firstToken->getOptions();
26 |
27 | $startDisplay = (bool)$this->_data->getValueOf($options['name']);
28 |
29 | $rendered = '';
30 |
31 | foreach($this->_elements as $element)
32 | {
33 | if ($element instanceof jQueryTmpl_Element_Else)
34 | {
35 | if ($startDisplay)
36 | {
37 | break;
38 | }
39 | else
40 | {
41 | $startDisplay = $element->setData($this->_data)->displayNextBlock();
42 | continue;
43 | }
44 | }
45 |
46 | if ($startDisplay)
47 | {
48 | $rendered .= $element
49 | ->setData($this->_data)
50 | ->setCompiledTemplates($this->_compiledTemplates)
51 | ->render();
52 | }
53 | }
54 |
55 | return $rendered;
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/test/Element/ValueEscapedTest.php:
--------------------------------------------------------------------------------
1 | _elementFactory->createInline
10 | (
11 | 'ValueEscaped',
12 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'key1'), '')
13 | );
14 |
15 | $this->assertEquals
16 | (
17 | 123.45,
18 | $element->setData($this->_data)->render()
19 | );
20 | }
21 |
22 | public function testShouldReturnTagReplacedWithValueWithUncommonKey()
23 | {
24 | $element = $this->_elementFactory->createInline
25 | (
26 | 'ValueEscaped',
27 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'$wtfKey'), '')
28 | );
29 |
30 | $this->assertEquals
31 | (
32 | 'It works!',
33 | $element->setData($this->_data)->render()
34 | );
35 | }
36 |
37 | public function testShouldReturnNonexistantTagReplacedWithEmptyString()
38 | {
39 | $element = $this->_elementFactory->createInline
40 | (
41 | 'ValueEscaped',
42 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'dneKey'), '')
43 | );
44 |
45 | $this->assertEquals
46 | (
47 | '',
48 | $element->setData($this->_data)->render()
49 | );
50 | }
51 |
52 | public function testShouldReturnTagReplacedWithEscapedValue()
53 | {
54 | $element = $this->_elementFactory->createInline
55 | (
56 | 'ValueEscaped',
57 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'htmlKey'), '')
58 | );
59 |
60 | $this->assertEquals
61 | (
62 | "<span>Some Text & marks "'".</span>",
63 | $element->setData($this->_data)->render()
64 | );
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/test/Element/ValueNotEscapedTest.php:
--------------------------------------------------------------------------------
1 | _elementFactory->createInline
10 | (
11 | 'ValueNotEscaped',
12 | new jQueryTmpl_Token_ValueNotEscaped(0, array('name'=>'key1'), '')
13 | );
14 |
15 | $this->assertEquals
16 | (
17 | 123.45,
18 | $element->setData($this->_data)->render()
19 | );
20 | }
21 |
22 | public function testShouldReturnTagReplacedWithValueWithUncommonKey()
23 | {
24 | $element = $this->_elementFactory->createInline
25 | (
26 | 'ValueNotEscaped',
27 | new jQueryTmpl_Token_ValueNotEscaped(0, array('name'=>'$wtfKey'), '')
28 | );
29 |
30 | $this->assertEquals
31 | (
32 | 'It works!',
33 | $element->setData($this->_data)->render()
34 | );
35 | }
36 |
37 | public function testShouldReturnNonexistantTagReplacedWithEmptyString()
38 | {
39 | $element = $this->_elementFactory->createInline
40 | (
41 | 'ValueNotEscaped',
42 | new jQueryTmpl_Token_ValueNotEscaped(0, array('name'=>'dneKey'), '')
43 | );
44 |
45 | $this->assertEquals
46 | (
47 | '',
48 | $element->setData($this->_data)->render()
49 | );
50 | }
51 |
52 | public function testShouldReturnTagReplacedWithEscapedValue()
53 | {
54 | $element = $this->_elementFactory->createInline
55 | (
56 | 'ValueNotEscaped',
57 | new jQueryTmpl_Token_ValueNotEscaped(0, array('name'=>'htmlKey'), '')
58 | );
59 |
60 | $this->assertEquals
61 | (
62 | "Some Text & marks \"'\".",
63 | $element->setData($this->_data)->render()
64 | );
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/jQueryTmpl/Element/Each.php:
--------------------------------------------------------------------------------
1 | _firstToken = array_shift($tokens);
12 | $lastToken = array_pop($tokens);
13 |
14 | // Make sure we have the right types of tokens
15 | if (!($this->_firstToken instanceof jQueryTmpl_Token_EachStart && $lastToken instanceof jQueryTmpl_Token_EachEnd))
16 | {
17 | throw new jQueryTmpl_Element_Exception('Token mismatch, cannot create {{each}} element.');
18 | }
19 |
20 | $this->_elements = $this->_parser->parse($tokens);
21 | }
22 |
23 | public function render()
24 | {
25 | $defaultOptions = array
26 | (
27 | 'index' => '$index',
28 | 'value' => '$value'
29 | );
30 | $options = array_merge($defaultOptions, $this->_firstToken->getOptions());
31 |
32 | $blockData = $this->_data->getValueOf($options['name']);
33 |
34 | if (!(is_array($blockData) || $blockData instanceof stdClass))
35 | {
36 | // If there is no valid data for this each block it becomes nothing.
37 | return '';
38 | }
39 |
40 | $rendered = '';
41 |
42 | foreach($blockData as $index => $value)
43 | {
44 | // Make a copy of the data to use as local
45 | $localData = $this->_data;
46 |
47 | // Add our local vars to copy of data
48 | $localData
49 | ->addDataPair($options['index'], $index)
50 | ->addDataPair($options['value'], $value)
51 | ->addDataPair('this', $value);
52 |
53 | // Now call each element and give them the data as well.
54 | foreach ($this->_elements as $element)
55 | {
56 | $rendered .= $element
57 | ->setData($localData)
58 | ->setCompiledTemplates($this->_compiledTemplates)
59 | ->render();
60 | }
61 | }
62 |
63 | return $rendered;
64 | }
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/jQueryTmpl.php:
--------------------------------------------------------------------------------
1 | _tokenizer = $tokenizer;
16 | $this->_parser = $parser;
17 |
18 | $this->_outputBuffer = '';
19 | }
20 |
21 | public function getHtml()
22 | {
23 | $ob = $this->_outputBuffer;
24 | $this->_outputBuffer = '';
25 | return $ob;
26 | }
27 |
28 | public function renderHtml()
29 | {
30 | echo $this->getHtml();
31 | return $this;
32 | }
33 |
34 | public function template($name, jQueryTmpl_Markup $markup)
35 | {
36 | $this->_compiledTemplates[$name] = $this->_compileTemplate($markup);
37 | return $this;
38 | }
39 |
40 | public function tmpl($nameOrMarkup, jQueryTmpl_Data $data)
41 | {
42 | if ($nameOrMarkup instanceof jQueryTmpl_Markup)
43 | {
44 | $elements = $this->_compileTemplate($nameOrMarkup);
45 | }
46 | else
47 | {
48 | $elements = $this->_compiledTemplates[$nameOrMarkup];
49 | }
50 |
51 | if (!empty($elements))
52 | {
53 | $this->_renderElements
54 | (
55 | $elements,
56 | $data
57 | );
58 | }
59 |
60 | return $this;
61 | }
62 |
63 | private function _compileTemplate(jQueryTmpl_Markup $markup)
64 | {
65 | return $this->_parser->parse
66 | (
67 | $this->_tokenizer->tokenize
68 | (
69 | $markup->getTemplate()
70 | )
71 | );
72 | }
73 |
74 | private function _renderElements(array $elements, jQueryTmpl_Data $data)
75 | {
76 | foreach ($elements as $element)
77 | {
78 | $this->_outputBuffer .= $element
79 | ->setData($data)
80 | ->setCompiledTemplates($this->_compiledTemplates)
81 | ->render();
82 | }
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/test/Tag/CommentTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_Comment();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Some text{{! Some Comment}} and more text.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{! Some Comment}}',
28 | 9
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Some text{{!Some{\n}Comment{{!}} and more text.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{!Some{\n}Comment{{!}}",
50 | 9
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{!First Comment}} and {{! Second !}}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{!First Comment}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{! Second !}}',
77 | 23
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/example.php:
--------------------------------------------------------------------------------
1 | array
8 | (
9 | array
10 | (
11 | 'name' => 'The Red Violin',
12 | 'year' => '1998',
13 | 'director' => 'François Girard',
14 | 'imdb' => 7.7
15 | ),
16 | array
17 | (
18 | 'name' => 'Eyes Wide Shut',
19 | 'year' => '1999',
20 | 'director' => 'Stanley Kubrick',
21 | 'imdb' => 7.2
22 | ),
23 | array
24 | (
25 | 'name' => 'The Inheritance',
26 | 'year' => '1976',
27 | 'director' => 'Mauro Bolognini',
28 | 'imdb' => 6.7
29 | )
30 | ),
31 | 'greeting' => array
32 | (
33 | 'name' => 'Xiao',
34 | 'from' => 'Boston'
35 | )
36 | );
37 |
38 | // Create factory classes
39 | $jQueryTmpl_Factory = new jQueryTmpl_Factory();
40 | $jQueryTmpl_Markup_Factory = new jQueryTmpl_Markup_Factory();
41 | $jQueryTmpl_Data_Factory = new jQueryTmpl_Data_Factory();
42 |
43 | // Create jQueryTmpl object
44 | $jQueryTmpl = $jQueryTmpl_Factory->create();
45 |
46 | // Create some data from our PHP array
47 | $jQueryTmpl_Data = $jQueryTmpl_Data_Factory->createFromArray($data);
48 |
49 | // Compile a template using a shared template file, or pass in text
50 | $jQueryTmpl
51 | ->template
52 | (
53 | 'movieTemplate',
54 | $jQueryTmpl_Markup_Factory->createFromFile('example.js')
55 | )
56 | ->template
57 | (
58 | 'nameTemplate',
59 | $jQueryTmpl_Markup_Factory->createFromString('Hello {{=greeting.name}}!')
60 | )
61 | ->template
62 | (
63 | 'ratingTemplate',
64 | $jQueryTmpl_Markup_Factory->createFromString("I'd give it a {{=this.imdb}}")
65 | );
66 |
67 | // Use pre compiled templates to render
68 | $jQueryTmpl
69 | ->tmpl('movieTemplate', $jQueryTmpl_Data)
70 | ->renderHtml();
71 |
72 | echo "
\n";
73 |
74 | // Mix in the use by non compiled templates as well
75 | $rendered = $jQueryTmpl
76 | ->tmpl('nameTemplate', $jQueryTmpl_Data)
77 | ->tmpl
78 | (
79 | $jQueryTmpl_Markup_Factory->createFromString(' I hear ${greeting.from} is lovely.'),
80 | $jQueryTmpl_Data
81 | )
82 | ->getHtml();
83 |
84 | // Do whatever we want with the output, in this case just print it
85 | echo "$rendered
\n";
86 |
87 |
--------------------------------------------------------------------------------
/test/Tag/IfStartTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_IfStart();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{if myVar}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{if myVar}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{if\nmyVar}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{if\nmyVar}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{ifmyVar1}} and {{If myVar2 }}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{ifmyVar1}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{If myVar2 }}',
77 | 17
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'name' => 'myVar'
92 | ),
93 | $this->_cut->parseTag('{{if myVar}}')
94 | );
95 | }
96 |
97 | public function testShouldExtractAdvancedVarName()
98 | {
99 | $this->assertEquals
100 | (
101 | array
102 | (
103 | 'name' => "myVar.foo['bar baz'].length"
104 | ),
105 | $this->_cut->parseTag("{{if\nmyVar.foo['bar baz'].length }}")
106 | );
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/test/Tag/ValueEscapedTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_ValueEscaped();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{= myVar}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{= myVar}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{=\nmyVar}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{=\nmyVar}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{=myVar1}} and {{= myVar2 }}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{=myVar1}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{= myVar2 }}',
77 | 16
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'name' => 'myVar'
92 | ),
93 | $this->_cut->parseTag('{{=myVar}}')
94 | );
95 | }
96 |
97 | public function testShouldExtractAdvancedVarName()
98 | {
99 | $this->assertEquals
100 | (
101 | array
102 | (
103 | 'name' => "myVar.foo['bar baz'].length"
104 | ),
105 | $this->_cut->parseTag("{{=\nmyVar.foo['bar baz'].length }}")
106 | );
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/jQueryTmpl/Parser.php:
--------------------------------------------------------------------------------
1 | _elementFactory = $elementFactory;
10 | }
11 |
12 | public function parse(array $tokens)
13 | {
14 | $elements = array();
15 | $count = count($tokens);
16 |
17 | for ($i=0;$i<$count;$i++)
18 | {
19 | /**
20 | * If the type of token is a block we need to provide
21 | * the element an array of tokens, not just one.
22 | */
23 | if ($tokens[$i] instanceof jQueryTmpl_Token_TypeBlock)
24 | {
25 | $startToken = $tokens[$i];
26 |
27 | if (!$startToken->isBlockStart())
28 | {
29 | throw new jQueryTmpl_Parser_Exception('A unmatched block end tag has been encountered.');
30 | }
31 |
32 | $blockNest = $startToken->getLevel();
33 | $endToken = 'jQueryTmpl_Token_'.$startToken->getBlockEndToken();
34 |
35 | $childTokens = array();
36 |
37 | for ($j=$i;$j<$count;$j++)
38 | {
39 | $childTokens[] = $tokens[$j];
40 |
41 | if (($tokens[$j]->getLevel() == $blockNest) && ($tokens[$j] instanceof $endToken))
42 | {
43 | $i = $j;
44 | break;
45 | }
46 | }
47 |
48 | $elements[] = $this->_elementFactory->createBlock
49 | (
50 | $startToken->getElementType(),
51 | $childTokens
52 | );
53 |
54 | continue;
55 | }
56 |
57 | /**
58 | * If the type of token is control call specific factory
59 | * create method.
60 | */
61 | if ($tokens[$i] instanceof jQueryTmpl_Token_TypeControl)
62 | {
63 | $elements[] = $this->_elementFactory->createControl
64 | (
65 | $tokens[$i]->getElementType(),
66 | $tokens[$i]
67 | );
68 |
69 | continue;
70 | }
71 |
72 | /**
73 | * If the type of token is inline call specific factory
74 | * create method.
75 | */
76 | if ($tokens[$i] instanceof jQueryTmpl_Token_TypeInline)
77 | {
78 | $elements[] = $this->_elementFactory->createInline
79 | (
80 | $tokens[$i]->getElementType(),
81 | $tokens[$i]
82 | );
83 |
84 | continue;
85 | }
86 |
87 | throw new jQueryTmpl_Parser_Exception('Uncaught token type detected.');
88 | }
89 |
90 | return $elements;
91 | }
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/test/Tag/ValueEscapedShorthandTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_ValueEscapedShorthand();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar ${myVar}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '${myVar}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar \${\nmyVar }.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "\${\nmyVar }",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '${myVar1} not {{= myVar2 }} and ${ myVar3 }';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '${myVar1}',
72 | 0
73 | ),
74 | array
75 | (
76 | '${ myVar3 }',
77 | 32
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'name' => 'myVar'
92 | ),
93 | $this->_cut->parseTag('${myVar}')
94 | );
95 | }
96 |
97 | public function testShouldExtractAdvancedVarName()
98 | {
99 | $this->assertEquals
100 | (
101 | array
102 | (
103 | 'name' => "myVar.foo['bar baz'].length"
104 | ),
105 | $this->_cut->parseTag("\${\nmyVar.foo['bar baz'].length }")
106 | );
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/test/Tag/ValueNotEscapedTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_ValueNotEscaped();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{html myVar}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{html myVar}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{html\nmyVar}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{html\nmyVar}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{htmlmyVar1}} and {{HtMl myVar2 }}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{htmlmyVar1}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{HtMl myVar2 }}',
77 | 19
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'name' => 'myVar'
92 | ),
93 | $this->_cut->parseTag('{{htmlmyVar}}')
94 | );
95 | }
96 |
97 | public function testShouldExtractAdvancedVarName()
98 | {
99 | $this->assertEquals
100 | (
101 | array
102 | (
103 | 'name' => "myVar.foo['bar baz'].length"
104 | ),
105 | $this->_cut->parseTag("{{html\nmyVar.foo['bar baz'].length }}")
106 | );
107 | }
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/test/Tag/EachStartTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_EachStart();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{each myVar}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{each myVar}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{each\nmyVar}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{each\nmyVar}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{eachmyVar1}} and {{EaCh myVar2 }}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{eachmyVar1}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{EaCh myVar2 }}',
77 | 19
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'name' => 'myVar'
92 | ),
93 | $this->_cut->parseTag('{{each myVar}}')
94 | );
95 | }
96 |
97 | public function testShouldExtractAdvancedVarName()
98 | {
99 | $this->assertEquals
100 | (
101 | array
102 | (
103 | 'name' => "myVar.foo['bar baz'].length",
104 | 'index' => 'idx',
105 | 'value' =>'$val'
106 | ),
107 | $this->_cut->parseTag("{{each(idx, \$val)\nmyVar.foo['bar baz'].length }}")
108 | );
109 | }
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | jQuery-tmpl-PHP
2 | ===============
3 |
4 | A PHP library for rendering jQuery templates server-side. Inspired by
5 | [jQuery-tmpl.Net][1] and of course [jQuery Templates][2].
6 |
7 | Usage
8 | -----
9 |
10 | jQuery-tmpl-PHP was written to mimic to a certain extent the jQuery Templates
11 | method calls. An example file has been included showing various usage cases.
12 |
13 | The `jQueryTmpl()` object supports the following method calls:
14 |
15 | * **`getHtml()`**
16 | * Parameters: None
17 | * Purpose: Returns the generated HTML in the buffer and clears it.
18 | * **`renderHtml()`**
19 | * Parameters: None
20 | * Purpose: Prints the generated HTML in the buffer and clears it.
21 | * **`template(name, jQueryTmpl_Markup)`**
22 | * Parameters:
23 | * String name for the template.
24 | * The markup to be compiled.
25 | * Purpose: Compiles the given template markup.
26 | * **`tmpl(name/jQueryTmpl_Markup, jQueryTmpl_Data)`**
27 | * Parameters:
28 | * String name of a precompiled template or the markup to be compiled.
29 | * The data to be applied to the template.
30 | * Purpose: Renders the template with the given data and stores it in the
31 | output buffer.
32 |
33 | Supported Tags
34 | --------------
35 |
36 | * **`${property}` and `{{= property}}`**
37 |
38 | Both the shorthand `${}` and `{{= }}` are supported. Will print out the
39 | value of the indicated property on the provided data object. Nested property
40 | resolution is supported. However expression/function evaluation is not
41 | currently supported. (See roadmap.)
42 |
43 | * **`{{html property}}`**
44 |
45 | Renders the value of the property without HTML encoding. Otherwise identical
46 | to `${}`.
47 |
48 | * **`{{each(index, value) property}}...{{/each}}`**
49 |
50 | Renders an instance of the tag contents for each item in the property value
51 | on the provided data object. Custom index and value variables can be
52 | optionally passed in.
53 |
54 | * **`{{if property}}...{{/if}`**
55 |
56 | Renders the content of the tag if the property value on the provided data
57 | object evaluates to `true`. This is javascript-style evaluation so 0, null,
58 | empty string are all `false`.
59 |
60 | * **`{{else property}}`**
61 |
62 | Used within the `{{if}}` tag to evaluate else conditions. The property value
63 | is optional.
64 |
65 | * **`{{tmpl(data, options) template}}`**
66 |
67 | This tag takes data and options as optional parameters. The tag will render a
68 | existing rendered template (using the `template()` method) in place. When a
69 | data property is passed in only the portion of data referenced by that
70 | property is passed to the template specified. Options is currently not
71 | supported.
72 |
73 | * **`{{! comments}}`**
74 |
75 | This tag does not appear to be documented on the official jQuery site
76 | however it does exist in code. The same functionality is preserved here,
77 | comments are simply discarded in rendered output.
78 |
79 | Roadmap
80 | -------
81 |
82 | The following is on my todo list.
83 |
84 | * Support for JavaScript expresion evaluation.
85 | * Support for `{{wrap}}`
86 |
87 | [1]: http://github.com/awhatley/jquery-tmpl.net
88 | [2]: http://github.com/jquery/jquery-tmpl
89 |
--------------------------------------------------------------------------------
/test/Element/IfTest.php:
--------------------------------------------------------------------------------
1 | _elementFactory->createBlock
10 | (
11 | 'If',
12 | array
13 | (
14 | new jQueryTmpl_Token_IfStart(0, array('name'=>'key5.child3'), ''),
15 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST'),
16 | new jQueryTmpl_Token_IfEnd(0, array(), '')
17 | )
18 | );
19 |
20 | $this->assertEquals
21 | (
22 | '',
23 | $element->setData($this->_data)->render()
24 | );
25 | }
26 |
27 | public function testShouldReturnWhenCondIsTrue()
28 | {
29 | $element = $this->_elementFactory->createBlock
30 | (
31 | 'If',
32 | array
33 | (
34 | new jQueryTmpl_Token_IfStart(0, array('name'=>'key3'), ''),
35 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST'),
36 | new jQueryTmpl_Token_Else(0, array(), ''),
37 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 2'),
38 | new jQueryTmpl_Token_IfEnd(0, array(), '')
39 | )
40 | );
41 |
42 | $this->assertEquals
43 | (
44 | 'TEST',
45 | $element->setData($this->_data)->render()
46 | );
47 | }
48 |
49 | public function testShouldReturnElseWhenCondIsFalse()
50 | {
51 | $element = $this->_elementFactory->createBlock
52 | (
53 | 'If',
54 | array
55 | (
56 | new jQueryTmpl_Token_IfStart(0, array('name'=>'key5.child3'), ''),
57 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 1'),
58 | new jQueryTmpl_Token_Else(0, array('name'=>'key5.child3'), ''),
59 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 2'),
60 | new jQueryTmpl_Token_Else(0, array(), ''),
61 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 3'),
62 | new jQueryTmpl_Token_IfEnd(0, array(), '')
63 | )
64 | );
65 |
66 | $this->assertEquals
67 | (
68 | 'TEST 3',
69 | $element->setData($this->_data)->render()
70 | );
71 | }
72 |
73 | public function testShouldReturnElseIfWhenCondIsTrue()
74 | {
75 | $element = $this->_elementFactory->createBlock
76 | (
77 | 'If',
78 | array
79 | (
80 | new jQueryTmpl_Token_IfStart(0, array('name'=>'key5.child3'), ''),
81 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 1'),
82 | new jQueryTmpl_Token_Else(0, array('name'=>'key3'), ''),
83 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 2'),
84 | new jQueryTmpl_Token_Else(0, array(), ''),
85 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST 3'),
86 | new jQueryTmpl_Token_IfEnd(0, array(), '')
87 | )
88 | );
89 |
90 | $this->assertEquals
91 | (
92 | 'TEST 2',
93 | $element->setData($this->_data)->render()
94 | );
95 | }
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/jQueryTmpl/Tokenizer.php:
--------------------------------------------------------------------------------
1 | _tokenFactory = $tokenFactory;
11 | }
12 |
13 | public function addTag(jQueryTmpl_Tag $tag)
14 | {
15 | $this->_tags[] = $tag;
16 | return $this;
17 | }
18 |
19 | public function tokenize($template)
20 | {
21 | $markers = $this->_findAllTokens($template);
22 | return $this->_createTokensFromMarkers($template, $markers);
23 | }
24 |
25 | private function _findAllTokens($template)
26 | {
27 | $markers = array();
28 |
29 | foreach ($this->_tags as $tag)
30 | {
31 | $matches = array();
32 | preg_match_all($tag->getRegex(), $template, $matches, PREG_OFFSET_CAPTURE);
33 |
34 | foreach ($matches[0] as $match)
35 | {
36 | $markers[$match[1]] = array
37 | (
38 | 'rawMatch' => $match[0],
39 | 'tokenType' => $tag->getTokenType(),
40 | 'nestingValue' => $tag->getNestingValue(),
41 | 'options' => $tag->parseTag($match[0])
42 | );
43 | }
44 | }
45 |
46 | ksort($markers);
47 |
48 | return $markers;
49 | }
50 |
51 | private function _createTokensFromMarkers($template, array $markers)
52 | {
53 | $tokens = array();
54 | $currPos = 0;
55 | $currNest = 0;
56 |
57 | foreach ($markers as $pos => $marker)
58 | {
59 | /**
60 | * If next marker is beyond where we are the delta is
61 | * just a NoOp token.
62 | */
63 | if ($currPos < $pos)
64 | {
65 | $tokens[] = $this->_tokenFactory->create
66 | (
67 | 'NoOp',
68 | $currNest,
69 | array(),
70 | substr($template, $currPos, $pos-$currPos)
71 | );
72 | }
73 |
74 | /**
75 | * Add the tag that was found as a token advancing the
76 | * nesting and position as needed.
77 | */
78 | $currNest += $marker['nestingValue'][0];
79 |
80 | $tokens[] = $this->_tokenFactory->create
81 | (
82 | $marker['tokenType'],
83 | $currNest,
84 | $marker['options'],
85 | substr($template, $pos, strlen($marker['rawMatch']))
86 | );
87 |
88 | $currNest += $marker['nestingValue'][1];
89 | $currPos = $pos + strlen($marker['rawMatch']);
90 | }
91 |
92 | if ($currPos < strlen($template))
93 | {
94 | $tokens[] = $this->_tokenFactory->create
95 | (
96 | 'NoOp',
97 | $currNest,
98 | array(),
99 | substr($template, $currPos)
100 | );
101 | }
102 |
103 | if ($currNest != 0)
104 | {
105 | throw new jQueryTmpl_Tokenizer_Exception('jQuery Template can not be tokenized, there exists an unclosed tag.');
106 | }
107 |
108 | return $tokens;
109 | }
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/test/Tag/ElseTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_Else();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{else myVar}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{else myVar}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{else\nmyVar}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{else\nmyVar}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{elsemyVar1}} and {{ElSe myVar2 }} and {{else}}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 3,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{elsemyVar1}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{ElSe myVar2 }}',
77 | 19
78 | ),
79 | array
80 | (
81 | '{{else}}',
82 | 40
83 | )
84 | )
85 | ),
86 | $this->_evalRegex($this->_cut->getRegex(), $str)
87 | );
88 | }
89 |
90 | public function testShouldExtractSimpleVarName()
91 | {
92 | $this->assertEquals
93 | (
94 | array
95 | (
96 | 'name' => 'myVar'
97 | ),
98 | $this->_cut->parseTag('{{else myVar}}')
99 | );
100 | }
101 |
102 | public function testShouldExtractAdvancedVarName()
103 | {
104 | $this->assertEquals
105 | (
106 | array
107 | (
108 | 'name' => "myVar.foo['bar baz'].length"
109 | ),
110 | $this->_cut->parseTag("{{else\nmyVar.foo['bar baz'].length }}")
111 | );
112 | }
113 |
114 | public function testShouldExtractNothingForFinalElse()
115 | {
116 | $this->assertEquals
117 | (
118 | array
119 | (
120 | ),
121 | $this->_cut->parseTag('{{else}}')
122 | );
123 |
124 | $this->assertEquals
125 | (
126 | array
127 | (
128 | ),
129 | $this->_cut->parseTag('{{else }}')
130 | );
131 | }
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/jQueryTmpl/Data.php:
--------------------------------------------------------------------------------
1 | _data = $data;
12 | $this->_dataFactory = $dataFactory;
13 | }
14 |
15 | public function addDataPair($key, $value)
16 | {
17 | $this->_data->$key = $value;
18 | return $this;
19 | }
20 |
21 | public function getValueOf($jsNotation)
22 | {
23 | $dataTokens = $this->_parseJsNotation($jsNotation);
24 |
25 | $localData = $this->_data;
26 |
27 | foreach ($dataTokens as $token)
28 | {
29 | $localData = $this->_getDataPart($localData, $token);
30 | }
31 |
32 | return $localData;
33 | }
34 |
35 | public function getDataSice($jsNotation)
36 | {
37 | if (empty($jsNotation))
38 | {
39 | return $this;
40 | }
41 |
42 | $dataSlice = $this->getValueOf($jsNotation);
43 |
44 | if ($dataSlice instanceof stdClass)
45 | {
46 | return $this->_dataFactory->createFromStdClass($dataSlice);
47 | }
48 | else
49 | {
50 | return null;
51 | }
52 | }
53 |
54 | private function _parseJsNotation($js)
55 | {
56 | $dataTokens = array();
57 |
58 | // Split based on '.' to traverse into object. Could be smarter...
59 | $loc = explode('.', $js);
60 |
61 | foreach($loc as $idx => $name)
62 | {
63 | $match = array();
64 |
65 | if (!preg_match('/^([a-z_$][0-9a-z_$]*)(\[.*\])?$/i', $name, $match))
66 | {
67 | throw new jQueryTmpl_Data_Exception('jQueryTmpl_Data can not evaluate expressions.');
68 | }
69 |
70 | $dataTokens[] = $match[1];
71 |
72 | if (!empty($match[2]))
73 | {
74 | $subparts = explode('][', $match[2]);
75 |
76 | foreach ($subparts as $subname)
77 | {
78 | $dataTokens[] = trim($subname, ' \'"[]');
79 | }
80 | }
81 | }
82 |
83 | return $dataTokens;
84 | }
85 |
86 | private function _getDataPart($data, $token)
87 | {
88 | if ($data instanceof stdClass)
89 | {
90 | return $this->_getDataPartObject($data, $token);
91 | }
92 |
93 | if (is_array($data))
94 | {
95 | return $this->_getDataPartArray($data, $token);
96 | }
97 |
98 | if (is_string($data))
99 | {
100 | return $this->_getDataPartString($data, $token);
101 | }
102 |
103 | // At this point we are told to go into a primitive type, this is undefined
104 | return '';
105 | }
106 |
107 | private function _getDataPartObject(stdClass $data, $token)
108 | {
109 | if ($token == 'length')
110 | {
111 | $i = 0;
112 |
113 | foreach ($data as $element)
114 | {
115 | $i++;
116 | }
117 |
118 | return $i;
119 | }
120 |
121 | return $data->$token;
122 | }
123 |
124 | private function _getDataPartArray(array $data, $token)
125 | {
126 | if ($token == 'length')
127 | {
128 | return count($data);
129 | }
130 |
131 | if (!$this->_isInt($token))
132 | {
133 | return '';
134 | }
135 |
136 | return $data[$token];
137 | }
138 |
139 | private function _getDataPartString($data, $token)
140 | {
141 | if ($token == 'length')
142 | {
143 | return strlen($data);
144 | }
145 |
146 | if (!$this->_isInt($token))
147 | {
148 | return '';
149 | }
150 |
151 | return $data[$token];
152 | }
153 |
154 | private function _isInt($var)
155 | {
156 | return ((int)$var == $var);
157 | }
158 | }
159 |
160 |
--------------------------------------------------------------------------------
/test/Tag/TmplTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Tag_Tmpl();
12 | }
13 |
14 | public function testShouldFindTag()
15 | {
16 | $str = 'Foo bar {{tmpl "#myTemplate"}}.';
17 |
18 | $this->assertEquals
19 | (
20 | array
21 | (
22 | 'total' => 1,
23 | 'match' => array
24 | (
25 | array
26 | (
27 | '{{tmpl "#myTemplate"}}',
28 | 8
29 | )
30 | )
31 | ),
32 | $this->_evalRegex($this->_cut->getRegex(), $str)
33 | );
34 | }
35 |
36 | public function testShouldFindTagSpanningLines()
37 | {
38 | $str = "Foo bar {{tmpl\n'#myTemplate'}}.";
39 |
40 | $this->assertEquals
41 | (
42 | array
43 | (
44 | 'total' => 1,
45 | 'match' => array
46 | (
47 | array
48 | (
49 | "{{tmpl\n'#myTemplate'}}",
50 | 8
51 | )
52 | )
53 | ),
54 | $this->_evalRegex($this->_cut->getRegex(), $str)
55 | );
56 | }
57 |
58 | public function testShouldGetAllTags()
59 | {
60 | $str = '{{tmpl "#myTemplate1"}} and {{TmPl "#myTemplate2" }}';
61 |
62 | $this->assertEquals
63 | (
64 | array
65 | (
66 | 'total' => 2,
67 | 'match' => array
68 | (
69 | array
70 | (
71 | '{{tmpl "#myTemplate1"}}',
72 | 0
73 | ),
74 | array
75 | (
76 | '{{TmPl "#myTemplate2" }}',
77 | 28
78 | )
79 | )
80 | ),
81 | $this->_evalRegex($this->_cut->getRegex(), $str)
82 | );
83 | }
84 |
85 | public function testShouldExtractSimpleVarName()
86 | {
87 | $this->assertEquals
88 | (
89 | array
90 | (
91 | 'template' => 'myTemplate',
92 | 'data' => null,
93 | 'options' => null
94 | ),
95 | $this->_cut->parseTag('{{tmpl "#myTemplate"}}')
96 | );
97 | }
98 |
99 | public function testShouldExtractAdvancedVarName()
100 | {
101 | $this->assertEquals
102 | (
103 | array
104 | (
105 | 'template' => "some-template-name",
106 | 'data' => 'data',
107 | 'options' => null
108 | ),
109 | $this->_cut->parseTag
110 | (
111 | "{{tmpl(data) '#some-template-name'}}"
112 | )
113 | );
114 |
115 | $this->assertEquals
116 | (
117 | array
118 | (
119 | 'template' => "some-template-name",
120 | 'data' => 'myVar.foo["bar baz"]',
121 | 'options' => null
122 | ),
123 | $this->_cut->parseTag
124 | (
125 | '{{tmpl(myVar.foo["bar baz"]) "#some-template-name"}}'
126 | )
127 | );
128 |
129 | $this->assertEquals
130 | (
131 | array
132 | (
133 | 'template' => "some-template-name",
134 | 'data' => 'data',
135 | 'options' =>'options'
136 | ),
137 | $this->_cut->parseTag
138 | (
139 | "{{tmpl(data, options) '#some-template-name'}}"
140 | )
141 | );
142 |
143 | $this->assertEquals
144 | (
145 | array
146 | (
147 | 'template' => "some-template-name",
148 | 'data' => 'myVar.foo["bar baz"]',
149 | 'options' =>'options'
150 | ),
151 | $this->_cut->parseTag
152 | (
153 | '{{tmpl(myVar.foo["bar baz"], options) "#some-template-name"}}'
154 | )
155 | );
156 | }
157 | }
158 |
159 |
--------------------------------------------------------------------------------
/test/TokenizerTest.php:
--------------------------------------------------------------------------------
1 | getMock('jQueryTmpl_Token_Factory', array('create'));
12 | $tokenFactory
13 | ->expects($this->any())
14 | ->method('create')
15 | ->will($this->returnCallback('jQueryTmpl_TokenizerTest__FactoryCallback'));
16 |
17 | $tag1 = $this->getMock('jQueryTmpl_Tag', array('getTokenType', 'getRegex', 'getNestingValue', 'parseTag'));
18 | $tag1
19 | ->expects($this->any())
20 | ->method('getTokenType')
21 | ->will($this->returnValue('>'));
22 | $tag1
23 | ->expects($this->any())
24 | ->method('getRegex')
25 | ->will($this->returnValue('/>/'));
26 | $tag1
27 | ->expects($this->any())
28 | ->method('getNestingValue')
29 | ->will($this->returnValue(array(0,1)));
30 | $tag1
31 | ->expects($this->any())
32 | ->method('parseTag')
33 | ->will($this->returnValue(array()));
34 |
35 | $tag2 = $this->getMock('jQueryTmpl_Tag', array('getTokenType', 'getRegex', 'getNestingValue', 'parseTag'));
36 | $tag2
37 | ->expects($this->any())
38 | ->method('getTokenType')
39 | ->will($this->returnValue('<'));
40 | $tag2
41 | ->expects($this->any())
42 | ->method('getRegex')
43 | ->will($this->returnValue('/'));
44 | $tag2
45 | ->expects($this->any())
46 | ->method('getNestingValue')
47 | ->will($this->returnValue(array(-1,0)));
48 | $tag2
49 | ->expects($this->any())
50 | ->method('parseTag')
51 | ->will($this->returnValue(array()));
52 |
53 | $tag3 = $this->getMock('jQueryTmpl_Tag', array('getTokenType', 'getRegex', 'getNestingValue', 'parseTag'));
54 | $tag3
55 | ->expects($this->any())
56 | ->method('getTokenType')
57 | ->will($this->returnValue('='));
58 | $tag3
59 | ->expects($this->any())
60 | ->method('getRegex')
61 | ->will($this->returnValue('/=/'));
62 | $tag3
63 | ->expects($this->any())
64 | ->method('getNestingValue')
65 | ->will($this->returnValue(array(0,0)));
66 | $tag3
67 | ->expects($this->any())
68 | ->method('parseTag')
69 | ->will($this->returnValue(array()));
70 |
71 | $this->_cut = new jQueryTmpl_Tokenizer($tokenFactory);
72 | $this->_cut
73 | ->addTag($tag1)
74 | ->addTag($tag2)
75 | ->addTag($tag3);
76 | }
77 |
78 | public function testShouldTokenizeSingleTag()
79 | {
80 | $tokens = $this->_cut->tokenize('=');
81 |
82 | $this->assertEquals
83 | (
84 | array
85 | (
86 | array('=', 0, array(), '=')
87 | ),
88 | $tokens
89 | );
90 | }
91 |
92 | public function testShouldTokenizeAllTags()
93 | {
94 | $tokens = $this->_cut->tokenize('123=456=789');
95 |
96 | $this->assertEquals
97 | (
98 | array
99 | (
100 | array('NoOp', 0, array(), '123'),
101 | array('=', 0, array(), '='),
102 | array('NoOp', 0, array(), '456'),
103 | array('=', 0, array(), '='),
104 | array('NoOp', 0, array(), '789')
105 | ),
106 | $tokens
107 | );
108 | }
109 |
110 | public function testShouldNestTokens()
111 | {
112 | $tokens = $this->_cut->tokenize('12=34>56=78<90=');
113 |
114 | $this->assertEquals
115 | (
116 | array
117 | (
118 | array('NoOp', 0, array(), '12'),
119 | array('=', 0, array(), '='),
120 | array('NoOp', 0, array(), '34'),
121 | array('>', 0, array(), '>'),
122 | array('NoOp', 1, array(), '56'),
123 | array('=', 1, array(), '='),
124 | array('NoOp', 1, array(), '78'),
125 | array('<', 0, array(), '<'),
126 | array('NoOp', 0, array(), '90'),
127 | array('=', 0, array(), '=')
128 | ),
129 | $tokens
130 | );
131 | }
132 |
133 | /**
134 | * @expectedException jQueryTmpl_Tokenizer_Exception
135 | */
136 | public function testShouldThrowExceptionWithNonMatchedBlocks()
137 | {
138 | $this->_cut->tokenize('12=34>56=78=90=');
139 | }
140 | }
141 |
142 | function jQueryTmpl_TokenizerTest__FactoryCallback()
143 | {
144 | return func_get_args();
145 | }
146 |
147 |
--------------------------------------------------------------------------------
/test/Element/EachTest.php:
--------------------------------------------------------------------------------
1 | _elementFactory->createBlock
10 | (
11 | 'Each',
12 | array
13 | (
14 | new jQueryTmpl_Token_EachStart(0, array('name'=>'key3'), ''),
15 | new jQueryTmpl_Token_NoOp(1, array(), 'TEST'),
16 | new jQueryTmpl_Token_EachEnd(0, array(), '')
17 | )
18 | );
19 |
20 | $this->assertEquals
21 | (
22 | '',
23 | $element->setData($this->_data)->render()
24 | );
25 | }
26 |
27 | public function testShoudLoopThroughArray()
28 | {
29 | $element = $this->_elementFactory->createBlock
30 | (
31 | 'Each',
32 | array
33 | (
34 | new jQueryTmpl_Token_EachStart(0, array('name'=>'array'), ''),
35 | new jQueryTmpl_Token_NoOp(1, array(), ''),
36 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'$index'), ''),
37 | new jQueryTmpl_Token_NoOp(1, array(), ': '),
38 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'$value'), ''),
39 | new jQueryTmpl_Token_NoOp(1, array(), ''),
40 | new jQueryTmpl_Token_EachEnd(0, array(), '')
41 | )
42 | );
43 |
44 | $this->assertEquals
45 | (
46 | '0: av11: av22: av3',
47 | $element->setData($this->_data)->render()
48 | );
49 | }
50 |
51 | public function testShoudLoopThroughArrayWithCustomIndex()
52 | {
53 | $element = $this->_elementFactory->createBlock
54 | (
55 | 'Each',
56 | array
57 | (
58 | new jQueryTmpl_Token_EachStart(0, array('name'=>'array','index'=>'idx','value'=>'val'), ''),
59 | new jQueryTmpl_Token_NoOp(1, array(), ''),
60 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'idx'), ''),
61 | new jQueryTmpl_Token_NoOp(1, array(), ': '),
62 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'val'), ''),
63 | new jQueryTmpl_Token_NoOp(1, array(), ''),
64 | new jQueryTmpl_Token_EachEnd(0, array(), '')
65 | )
66 | );
67 |
68 | $this->assertEquals
69 | (
70 | '0: av11: av22: av3',
71 | $element->setData($this->_data)->render()
72 | );
73 | }
74 |
75 | public function testShoudLoopThroughNestedEach()
76 | {
77 | $element = $this->_elementFactory->createBlock
78 | (
79 | 'Each',
80 | array
81 | (
82 | new jQueryTmpl_Token_EachStart(0, array('name'=>'array'), ''),
83 | new jQueryTmpl_Token_NoOp(1, array(), ''),
86 | new jQueryTmpl_Token_EachStart(1, array('name'=>'key5.child5'), ''),
87 | new jQueryTmpl_Token_NoOp(2, array(), '- '),
88 | new jQueryTmpl_Token_ValueEscaped(2, array('name'=>'$value'), ''),
89 | new jQueryTmpl_Token_NoOp(2, array(), '
'),
90 | new jQueryTmpl_Token_EachEnd(1, array(), ''),
91 | new jQueryTmpl_Token_NoOp(1, array(), '
'),
92 | new jQueryTmpl_Token_EachEnd(0, array(), '')
93 | )
94 | );
95 |
96 | $this->assertEquals
97 | (
98 | '',
99 | $element->setData($this->_data)->render()
100 | );
101 | }
102 |
103 | public function testShoudLoopThroughObject()
104 | {
105 | $element = $this->_elementFactory->createBlock
106 | (
107 | 'Each',
108 | array
109 | (
110 | new jQueryTmpl_Token_EachStart(0, array('name'=>'object'), ''),
111 | new jQueryTmpl_Token_NoOp(1, array(), ''),
114 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'$value.gender'), ''),
115 | new jQueryTmpl_Token_NoOp(1, array(), ': '),
116 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'this.name'), ''),
117 | new jQueryTmpl_Token_NoOp(1, array(), '('),
118 | new jQueryTmpl_Token_ValueEscaped(1, array('name'=>'attn'), ''),
119 | new jQueryTmpl_Token_NoOp(1, array(), ')'),
120 | new jQueryTmpl_Token_EachEnd(0, array(), '')
121 | )
122 | );
123 |
124 | $this->assertEquals
125 | (
126 | 'F: Sophia(!)M: Zack(!)F: Zoe(!): Morgan(!)',
127 | $element->setData($this->_data)->render()
128 | );
129 | }
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/test/Element/TmplTest.php:
--------------------------------------------------------------------------------
1 | _data = new jQueryTmpl_Data
31 | (
32 | json_decode($testData),
33 | new jQueryTmpl_Data_Factory()
34 | );
35 |
36 | $this->_elementFactory = new jQueryTmpl_Element_Factory();
37 |
38 | $this->_compiledTemplates['person'] = array
39 | (
40 | $this->_elementFactory->createInline
41 | (
42 | 'NoOp',
43 | new jQueryTmpl_Token_NoOp(0, array(), 'Name: ')
44 | ),
45 | $this->_elementFactory->createInline
46 | (
47 | 'ValueEscaped',
48 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'name'), '')
49 | ),
50 | $this->_elementFactory->createInline
51 | (
52 | 'NoOp',
53 | new jQueryTmpl_Token_NoOp(0, array(), ' (')
54 | ),
55 | $this->_elementFactory->createInline
56 | (
57 | 'ValueEscaped',
58 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'location'), '')
59 | ),
60 | $this->_elementFactory->createInline
61 | (
62 | 'NoOp',
63 | new jQueryTmpl_Token_NoOp(0, array(), ')')
64 | )
65 | );
66 |
67 | $this->_compiledTemplates['tree'] = array
68 | (
69 | $this->_elementFactory->createInline
70 | (
71 | 'NoOp',
72 | new jQueryTmpl_Token_NoOp(0, array(), '')
73 | ),
74 | $this->_elementFactory->createInline
75 | (
76 | 'ValueEscaped',
77 | new jQueryTmpl_Token_ValueEscaped(0, array('name'=>'name'), '')
78 | ),
79 | $this->_elementFactory->createInline
80 | (
81 | 'Tmpl',
82 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'tree', 'data'=>'child'), '')
83 | ),
84 | $this->_elementFactory->createInline
85 | (
86 | 'NoOp',
87 | new jQueryTmpl_Token_NoOp(0, array(), '')
88 | )
89 | );
90 | }
91 |
92 | public function testShouldNotPrintNonExistantTemplate()
93 | {
94 | $element = $this->_elementFactory->createInline
95 | (
96 | 'Tmpl',
97 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'dne'), '')
98 | );
99 |
100 | $this->assertEquals
101 | (
102 | '',
103 | $element
104 | ->setData($this->_data)
105 | ->setCompiledTemplates($this->_compiledTemplates)
106 | ->render()
107 | );
108 | }
109 |
110 | public function testShouldPrintTemplate()
111 | {
112 | $element = $this->_elementFactory->createInline
113 | (
114 | 'Tmpl',
115 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'person'), '')
116 | );
117 |
118 | $this->assertEquals
119 | (
120 | 'Name: Sophia (Denver, CO)',
121 | $element
122 | ->setData($this->_data)
123 | ->setCompiledTemplates($this->_compiledTemplates)
124 | ->render()
125 | );
126 | }
127 |
128 | public function testShouldPrintNestedTemplates()
129 | {
130 | $element = $this->_elementFactory->createInline
131 | (
132 | 'Tmpl',
133 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'person', 'data' => 'child'), '')
134 | );
135 |
136 | $this->assertEquals
137 | (
138 | 'Name: Zack (Portland, OR)',
139 | $element
140 | ->setData($this->_data)
141 | ->setCompiledTemplates($this->_compiledTemplates)
142 | ->render()
143 | );
144 |
145 | $element = $this->_elementFactory->createInline
146 | (
147 | 'Tmpl',
148 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'person', 'data' => 'child.child'), '')
149 | );
150 |
151 | $this->assertEquals
152 | (
153 | 'Name: Zoe (Boston, MA)',
154 | $element
155 | ->setData($this->_data)
156 | ->setCompiledTemplates($this->_compiledTemplates)
157 | ->render()
158 | );
159 | }
160 |
161 | public function testShouldPrintTemplateAndSliceData()
162 | {
163 | $element = $this->_elementFactory->createInline
164 | (
165 | 'Tmpl',
166 | new jQueryTmpl_Token_Tmpl(0, array('template'=>'tree'), '')
167 | );
168 |
169 | $this->assertEquals
170 | (
171 | 'SophiaZackZoe',
172 | $element
173 | ->setData($this->_data)
174 | ->setCompiledTemplates($this->_compiledTemplates)
175 | ->render()
176 | );
177 | }
178 | }
179 |
180 |
--------------------------------------------------------------------------------
/test/DataTest.php:
--------------------------------------------------------------------------------
1 | _cut = new jQueryTmpl_Data
50 | (
51 | json_decode($testData),
52 | new jQueryTmpl_Data_Factory()
53 | );
54 |
55 | $this->_cutPart = new jQueryTmpl_Data
56 | (
57 | json_decode($testDataPart),
58 | new jQueryTmpl_Data_Factory()
59 | );
60 | }
61 |
62 | public function testShouldAddDataToObject()
63 | {
64 | $this->_cut
65 | ->addDataPair('addedKey1', 'value 1')
66 | ->addDataPair('addedKey2', 'value 2');
67 |
68 | $this->assertEquals
69 | (
70 | 'a string',
71 | $this->_cut->getValueOf('key2')
72 | );
73 |
74 | $this->assertEquals
75 | (
76 | 'value 2',
77 | $this->_cut->getValueOf('addedKey2')
78 | );
79 | }
80 |
81 | /**
82 | * @expectedException jQueryTmpl_Data_Exception
83 | */
84 | public function testShouldNotParseExpresion()
85 | {
86 | $this->_cut->getValueOf('someFunc()');
87 | }
88 |
89 | public function testShouldReturnEmptyStringForDne()
90 | {
91 | $this->assertEquals
92 | (
93 | '',
94 | $this->_cut->getValueOf('dne')
95 | );
96 |
97 | $this->assertEquals
98 | (
99 | '',
100 | $this->_cut->getValueOf('key5.dne')
101 | );
102 |
103 | $this->assertEquals
104 | (
105 | '',
106 | $this->_cut->getValueOf('key1[0]')
107 | );
108 | }
109 |
110 | public function testShouldGetFromFirstLevel()
111 | {
112 | $this->assertEquals
113 | (
114 | 123.45,
115 | $this->_cut->getValueOf('key1')
116 | );
117 |
118 | $this->assertEquals
119 | (
120 | "a string",
121 | $this->_cut->getValueOf('key2')
122 | );
123 |
124 | $this->assertEquals
125 | (
126 | true,
127 | $this->_cut->getValueOf('key3')
128 | );
129 |
130 | $this->assertEquals
131 | (
132 | array('item1','item2','item3'),
133 | $this->_cut->getValueOf('key4')
134 | );
135 | }
136 |
137 | public function testShouldGetFromSecondLevel()
138 | {
139 | $this->assertEquals
140 | (
141 | 543.21,
142 | $this->_cut->getValueOf('key5.child1')
143 | );
144 |
145 | $this->assertEquals
146 | (
147 | "another string",
148 | $this->_cut->getValueOf('key5.child2')
149 | );
150 |
151 | $this->assertEquals
152 | (
153 | false,
154 | $this->_cut->getValueOf('key5.child3')
155 | );
156 |
157 | $this->assertEquals
158 | (
159 | array('item4','item5','item6'),
160 | $this->_cut->getValueOf('key5.child4')
161 | );
162 | }
163 |
164 | public function testShouldGetArrayElements()
165 | {
166 | $this->assertEquals
167 | (
168 | 'item2',
169 | $this->_cut->getValueOf('key4[1]')
170 | );
171 |
172 | $this->assertEquals
173 | (
174 | 'item4',
175 | $this->_cut->getValueOf('key5.child4[0]')
176 | );
177 | }
178 |
179 | public function testShouldGetHashElements()
180 | {
181 | $this->assertEquals
182 | (
183 | 'Naomi',
184 | $this->_cut->getValueOf('key5.child5["Grand Child 2"]')
185 | );
186 |
187 | $this->assertEquals
188 | (
189 | 'Rachel',
190 | $this->_cut->getValueOf('key5[child5]["Grand Child 1"]')
191 | );
192 | }
193 |
194 | public function testShouldGetCharInString()
195 | {
196 | $this->assertEquals
197 | (
198 | "e",
199 | $this->_cut->getValueOf('key5.child2[5]')
200 | );
201 | }
202 |
203 | public function testShouldGetLengths()
204 | {
205 | // Array length
206 | $this->assertEquals
207 | (
208 | 3,
209 | $this->_cut->getValueOf('key5.child4.length')
210 | );
211 |
212 | // Object length
213 | $this->assertEquals
214 | (
215 | 3,
216 | $this->_cut->getValueOf('key5.child5.length')
217 | );
218 |
219 | // String length
220 | $this->assertEquals
221 | (
222 | 5,
223 | $this->_cut->getValueOf('key5.child5["Grand Child 2"].length')
224 | );
225 | }
226 |
227 | public function testShouldReturnFullObjectIfNothingToSlice()
228 | {
229 | // Property not specified
230 | $this->assertEquals
231 | (
232 | $this->_cut,
233 | $this->_cut->getDataSice('')
234 | );
235 | }
236 |
237 | public function testShouldReturnNullIfSliceNotObject()
238 | {
239 | // Property is string
240 | $this->assertEquals
241 | (
242 | null,
243 | $this->_cut->getDataSice('key2')
244 | );
245 |
246 | // Property is array
247 | $this->assertEquals
248 | (
249 | null,
250 | $this->_cut->getDataSice('key4')
251 | );
252 | }
253 |
254 | public function testShouldSliceOutPartOfData()
255 | {
256 | $this->assertEquals
257 | (
258 | $this->_cutPart,
259 | $this->_cut->getDataSice('key5')
260 | );
261 | }
262 | }
263 |
264 |
--------------------------------------------------------------------------------
/test/ParserTest.php:
--------------------------------------------------------------------------------
1 | getMock('jQueryTmpl_Element_Factory', array('createBlock','createControl','createInline'));
12 | $elementFactory
13 | ->expects($this->any())
14 | ->method('createBlock')
15 | ->will($this->returnCallback('jQueryTmpl_ParserTest__FactoryBlockCallback'));
16 | $elementFactory
17 | ->expects($this->any())
18 | ->method('createControl')
19 | ->will($this->returnCallback('jQueryTmpl_ParserTest__FactoryControlCallback'));
20 | $elementFactory
21 | ->expects($this->any())
22 | ->method('createInline')
23 | ->will($this->returnCallback('jQueryTmpl_ParserTest__FactoryInlineCallback'));
24 |
25 | $this->_cut = new jQueryTmpl_Parser($elementFactory);
26 | }
27 |
28 | public function testShouldCreatInlineElements()
29 | {
30 | $elements = $this->_cut->parse
31 | (
32 | array
33 | (
34 | new jQueryTmpl_Token_NoOp(0, array(), 'a'),
35 | new jQueryTmpl_Token_Comment(0, array(), 'b'),
36 | new jQueryTmpl_Token_NoOp(0, array(), 'c'),
37 | new jQueryTmpl_Token_ValueEscaped(0, array(), 'd')
38 | )
39 | );
40 |
41 | $this->assertEquals
42 | (
43 | array
44 | (
45 | array
46 | (
47 | 'Inline',
48 | 'NoOp',
49 | new jQueryTmpl_Token_NoOp(0, array(), 'a')
50 | ),
51 | array
52 | (
53 | 'Inline',
54 | 'Comment',
55 | new jQueryTmpl_Token_Comment(0, array(), 'b')
56 | ),
57 | array
58 | (
59 | 'Inline',
60 | 'NoOp',
61 | new jQueryTmpl_Token_NoOp(0, array(), 'c')
62 | ),
63 | array
64 | (
65 | 'Inline',
66 | 'ValueEscaped',
67 | new jQueryTmpl_Token_ValueEscaped(0, array(), 'd')
68 | )
69 | ),
70 | $elements
71 | );
72 | }
73 |
74 | public function testShouldCreatNestedElements()
75 | {
76 | $elements = $this->_cut->parse
77 | (
78 | array
79 | (
80 | new jQueryTmpl_Token_NoOp(0, array(), 'a'),
81 | new jQueryTmpl_Token_Comment(0, array(), 'b'),
82 | new jQueryTmpl_Token_IfStart(0, array(), 'c1'),
83 | new jQueryTmpl_Token_NoOp(1, array(), 'c2'),
84 | new jQueryTmpl_Token_Else(0, array(), 'c3'),
85 | new jQueryTmpl_Token_ValueEscaped(1, array(), 'c4'),
86 | new jQueryTmpl_Token_IfEnd(0, array(), 'c5'),
87 | new jQueryTmpl_Token_ValueEscaped(0, array(), 'd')
88 | )
89 | );
90 |
91 | $this->assertEquals
92 | (
93 | array
94 | (
95 | array
96 | (
97 | 'Inline',
98 | 'NoOp',
99 | new jQueryTmpl_Token_NoOp(0, array(), 'a')
100 | ),
101 | array
102 | (
103 | 'Inline',
104 | 'Comment',
105 | new jQueryTmpl_Token_Comment(0, array(), 'b')
106 | ),
107 | array
108 | (
109 | 'Block',
110 | 'If',
111 | array
112 | (
113 | new jQueryTmpl_Token_IfStart(0, array(), 'c1'),
114 | new jQueryTmpl_Token_NoOp(1, array(), 'c2'),
115 | new jQueryTmpl_Token_Else(0, array(), 'c3'),
116 | new jQueryTmpl_Token_ValueEscaped(1, array(), 'c4'),
117 | new jQueryTmpl_Token_IfEnd(0, array(), 'c5'),
118 | )
119 | ),
120 | array
121 | (
122 | 'Inline',
123 | 'ValueEscaped',
124 | new jQueryTmpl_Token_ValueEscaped(0, array(), 'd')
125 | )
126 | ),
127 | $elements
128 | );
129 | }
130 |
131 | public function testShouldCreatControlElements()
132 | {
133 | $elements = $this->_cut->parse
134 | (
135 | array
136 | (
137 | new jQueryTmpl_Token_NoOp(1, array(), 'c2'),
138 | new jQueryTmpl_Token_Else(0, array(), 'c3'),
139 | new jQueryTmpl_Token_ValueEscaped(1, array(), 'c4'),
140 | )
141 | );
142 |
143 | $this->assertEquals
144 | (
145 | array
146 | (
147 | array
148 | (
149 | 'Inline',
150 | 'NoOp',
151 | new jQueryTmpl_Token_NoOp(1, array(), 'c2')
152 | ),
153 | array
154 | (
155 | 'Control',
156 | 'Else',
157 | new jQueryTmpl_Token_Else(0, array(), 'c3')
158 | ),
159 | array
160 | (
161 | 'Inline',
162 | 'ValueEscaped',
163 | new jQueryTmpl_Token_ValueEscaped(1, array(), 'c4')
164 | )
165 | ),
166 | $elements
167 | );
168 | }
169 |
170 | /**
171 | * @expectedException jQueryTmpl_Parser_Exception
172 | */
173 | public function testShouldThrowExceptionWithNonMatchedEndBlocks()
174 | {
175 | $elements = $this->_cut->parse
176 | (
177 | array
178 | (
179 | new jQueryTmpl_Token_NoOp(0, array(), 'a'),
180 | new jQueryTmpl_Token_Comment(0, array(), 'b'),
181 | new jQueryTmpl_Token_IfEnd(0, array(), 'c1'),
182 | new jQueryTmpl_Token_NoOp(1, array(), 'c2'),
183 | new jQueryTmpl_Token_Else(0, array(), 'c3'),
184 | new jQueryTmpl_Token_ValueEscaped(1, array(), 'c4'),
185 | new jQueryTmpl_Token_IfStart(0, array(), 'c5'),
186 | new jQueryTmpl_Token_ValueEscaped(0, array(), 'd')
187 | )
188 | );
189 | }
190 | }
191 |
192 | function jQueryTmpl_ParserTest__FactoryBlockCallback()
193 | {
194 | return array_merge(array('Block'), func_get_args());
195 | }
196 |
197 | function jQueryTmpl_ParserTest__FactoryControlCallback()
198 | {
199 | return array_merge(array('Control'), func_get_args());
200 | }
201 |
202 | function jQueryTmpl_ParserTest__FactoryInlineCallback()
203 | {
204 | return array_merge(array('Inline'), func_get_args());
205 | }
206 |
207 |
--------------------------------------------------------------------------------