566 | Snippet:
567 |
568 |
569 | Filter |
570 | Source |
571 | Rendered |
572 |
573 |
574 | linky filter |
575 |
576 | <div ng-bind-html="snippet | linky"> </div>
577 | |
578 |
579 |
580 | |
581 |
582 |
583 | linky target |
584 |
585 | <div ng-bind-html="snippetWithTarget | linky:'_blank'"> </div>
586 | |
587 |
588 |
589 | |
590 |
591 |
592 | no filter |
593 | <div ng-bind="snippet"> </div> |
594 | |
595 |
596 |
597 |
598 |
599 | it('should linkify the snippet with urls', function() {
600 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
601 | toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
602 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
603 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
604 | });
605 |
606 | it('should not linkify snippet without the linky filter', function() {
607 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
608 | toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
609 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
610 | expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
611 | });
612 |
613 | it('should update', function() {
614 | element(by.model('snippet')).clear();
615 | element(by.model('snippet')).sendKeys('new http://link.');
616 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
617 | toBe('new http://link.');
618 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
619 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
620 | .toBe('new http://link.');
621 | });
622 |
623 | it('should work with the target property', function() {
624 | expect(element(by.id('linky-target')).
625 | element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
626 | toBe('http://angularjs.org/');
627 | expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
628 | });
629 |
630 |
631 | */
632 | angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
633 | var LINKY_URL_REGEXP =
634 | /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/i,
635 | MAILTO_REGEXP = /^mailto:/i;
636 |
637 | return function(text, target) {
638 | if (!text) return text;
639 | var match;
640 | var raw = text;
641 | var html = [];
642 | var url;
643 | var i;
644 | while ((match = raw.match(LINKY_URL_REGEXP))) {
645 | // We can not end in these as they are sometimes found at the end of the sentence
646 | url = match[0];
647 | // if we did not match ftp/http/www/mailto then assume mailto
648 | if (!match[2] && !match[4]) {
649 | url = (match[3] ? 'http://' : 'mailto:') + url;
650 | }
651 | i = match.index;
652 | addText(raw.substr(0, i));
653 | addLink(url, match[0].replace(MAILTO_REGEXP, ''));
654 | raw = raw.substring(i + match[0].length);
655 | }
656 | addText(raw);
657 | return $sanitize(html.join(''));
658 |
659 | function addText(text) {
660 | if (!text) {
661 | return;
662 | }
663 | html.push(sanitizeText(text));
664 | }
665 |
666 | function addLink(url, text) {
667 | html.push('
');
676 | addText(text);
677 | html.push('');
678 | }
679 | };
680 | }]);
681 |
682 |
683 | })(window, window.angular);
684 |
--------------------------------------------------------------------------------
/classes/PageScript.php:
--------------------------------------------------------------------------------
1 | template = $template;
19 | }
20 |
21 | public static function fromTemplate($template)
22 | {
23 | return new static($template);
24 | }
25 |
26 | /**
27 | * Populate the template object with the script attribute
28 | * @return void
29 | */
30 | public function populate()
31 | {
32 | if (strlen($this->template->script))
33 | return;
34 |
35 | $this->template->script = $this->readTemplateScript();
36 | }
37 |
38 | /**
39 | * Save script content against a template, if the content does
40 | * not differ from the default, nothing is saved.
41 | * @param string $content
42 | * @return void
43 | */
44 | public function save($content)
45 | {
46 | if ($content == $this->getDefaultScriptContent())
47 | return;
48 |
49 | $this->writeTemplateScript($content);
50 | }
51 |
52 | /**
53 | * Returns the public path to the template script file.
54 | * @return string
55 | */
56 | public function getPublicPath()
57 | {
58 | $path = $this->getTemplateScriptPath();
59 | if (!File::isFile($path))
60 | return;
61 |
62 | return File::localToPublic($path);
63 | }
64 |
65 | protected function getTemplateScriptPath()
66 | {
67 | $theme = Theme::getEditTheme();
68 | $assetPath = $theme->getPath() . '/assets';
69 | $fileName = $this->template->getBaseFileName();
70 |
71 | $jsPath = $assetPath.'/javascript';
72 | if (!File::isDirectory($jsPath)) $jsPath = $assetPath.'/js';
73 |
74 | return $jsPath.'/controllers/'.$fileName.'.js';
75 | }
76 |
77 | protected function readTemplateScript()
78 | {
79 | $path = $this->getTemplateScriptPath();
80 | if (!File::isFile($path))
81 | return $this->getDefaultScriptContent();
82 |
83 | return $this->removeControllerDefinition(File::get($path));
84 | }
85 |
86 | protected function writeTemplateScript($content)
87 | {
88 | $path = $this->getTemplateScriptPath();
89 | $dir = dirname($path);
90 | if (!File::isDirectory($dir))
91 | File::makeDirectory($dir, 0755, true);
92 |
93 | File::put($path, $this->addControllerDefinition($content));
94 | }
95 |
96 | protected function getDefaultScriptContent()
97 | {
98 | return 'function ($scope, $request) {'.PHP_EOL.'}';
99 | }
100 |
101 | protected function removeControllerDefinition($content)
102 | {
103 | return preg_replace('#^([\s]*'.static::JS_OBJECT.'.controllers\[[^\]]+\][\s]*=[\s]*)#si', '', $content);
104 | }
105 |
106 | protected function addControllerDefinition($content)
107 | {
108 | return sprintf("%s.controllers['%s'] = ", static::JS_OBJECT, $this->template->getBaseFileName()) . $content;
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/classes/ScopeBag.php:
--------------------------------------------------------------------------------
1 | toArray();
28 |
29 | $this->vars[$key] = $value;
30 | return $this;
31 | }
32 |
33 | /**
34 | * Removes a value to the bag.
35 | * @param string $key
36 | * @param string $value
37 | * @return $this
38 | */
39 | public function remove($key)
40 | {
41 | unset($this->vars[$key]);
42 | return $this;
43 | }
44 |
45 | /**
46 | * Merge a new array of vars into the bag.
47 | * @param array $vars
48 | * @return $this
49 | */
50 | public function merge($vars)
51 | {
52 | $this->vars = array_merge_recursive($this->vars, $vars);
53 | return $this;
54 | }
55 |
56 | /**
57 | * Determine if value exist for a given key.
58 | * @param string $key
59 | * @return bool
60 | */
61 | public function has($key = null)
62 | {
63 | return $this->first($key) !== '';
64 | }
65 |
66 | /**
67 | * Get the first value from the bag for a given key.
68 | * @param string $key
69 | * @return string
70 | */
71 | public function first($key = null)
72 | {
73 | $vars = is_null($key) ? $this->all() : $this->get($key);
74 |
75 | return (count($vars) > 0) ? $vars[0] : '';
76 | }
77 |
78 | /**
79 | * Get all of the values from the bag for a given key.
80 | * @param string $key
81 | * @return array
82 | */
83 | public function get($key)
84 | {
85 | if (array_key_exists($key, $this->vars))
86 | return $this->vars[$key];
87 |
88 | return null;
89 | }
90 |
91 | /**
92 | * Get all of the values for every key in the bag.
93 | * @param string $format
94 | * @return array
95 | */
96 | public function all()
97 | {
98 | return $this->vars;
99 | }
100 |
101 | /**
102 | * Determine if the value bag has any vars.
103 | *
104 | * @return bool
105 | */
106 | public function isEmpty()
107 | {
108 | return ! $this->any();
109 | }
110 |
111 | /**
112 | * Determine if the value bag has any vars.
113 | *
114 | * @return bool
115 | */
116 | public function any()
117 | {
118 | return $this->count() > 0;
119 | }
120 |
121 | /**
122 | * Get the number of vars in the container.
123 | *
124 | * @return int
125 | */
126 | public function count()
127 | {
128 | return count($this->vars);
129 | }
130 |
131 | /**
132 | * Determine if the given attribute exists.
133 | * @param mixed $offset
134 | * @return bool
135 | */
136 | public function offsetExists($offset)
137 | {
138 | return $this->has($offset);
139 | }
140 |
141 | /**
142 | * Get the value for a given offset.
143 | * @param mixed $offset
144 | * @return mixed
145 | */
146 | public function offsetGet($offset)
147 | {
148 | return $this->get($offset);
149 | }
150 |
151 | /**
152 | * Set the value for a given offset.
153 | * @param mixed $offset
154 | * @param mixed $value
155 | * @return void
156 | */
157 | public function offsetSet($offset, $value)
158 | {
159 | $this->add($offset, $value);
160 | }
161 |
162 | /**
163 | * Unset the value for a given offset.
164 | * @param mixed $offset
165 | * @return void
166 | */
167 | public function offsetUnset($offset)
168 | {
169 | $this->remove($offset);
170 | }
171 |
172 | /**
173 | * Get the instance as an array.
174 | *
175 | * @return array
176 | */
177 | public function toArray()
178 | {
179 | return $this->vars;
180 | }
181 |
182 | /**
183 | * Convert the object into something JSON serializable.
184 | *
185 | * @return array
186 | */
187 | public function jsonSerialize()
188 | {
189 | return $this->toArray();
190 | }
191 |
192 | /**
193 | * Convert the object to its JSON representation.
194 | *
195 | * @param int $options
196 | * @return string
197 | */
198 | public function toJson($options = 0)
199 | {
200 | return json_encode($this->toArray(), $options);
201 | }
202 |
203 | /**
204 | * Convert the value bag to its string representation.
205 | *
206 | * @return string
207 | */
208 | public function __toString()
209 | {
210 | return $this->toJson();
211 | }
212 |
213 | }
214 |
--------------------------------------------------------------------------------
/components/Layout.php:
--------------------------------------------------------------------------------
1 | 'Angular Layout',
44 | 'description' => 'Allocate a layout for use with AngularJS.',
45 | ];
46 | }
47 |
48 | public function defineProperties()
49 | {
50 | return [
51 | 'idParam' => [
52 | 'title' => 'Slug param name',
53 | 'description' => 'The URL route parameter used for looking up the channel by its slug. A hard coded slug can also be used.',
54 | 'default' => ':slug',
55 | 'type' => 'string',
56 | ],
57 |
58 | ];
59 | }
60 |
61 | public function init()
62 | {
63 | $this->scope = $this->page['scope'] = new ScopeBag;
64 | // $this->addJs('assets/js/angular-bridge.js');
65 | }
66 |
67 | public function onRun()
68 | {
69 | $this->pages = $this->page['pages'] = Page::all();
70 | $this->dependencyString = $this->page['dependencyString'] = $this->getModuleDependencies();
71 | }
72 |
73 | public function onGetPageDependencies()
74 | {
75 | $response = [];
76 | $this->pageCycle();
77 |
78 | /*
79 | * Add the front-end controller, if available.
80 | */
81 | $page = array_get($this->page['this'], 'page');
82 | $pageScript = PageScript::fromTemplate($page);
83 |
84 | if ($scriptPath = $pageScript->getPublicPath())
85 | $this->addJs($scriptPath.'?v='.$page->mtime);
86 |
87 | /*
88 | * Detect assets
89 | */
90 | if ($this->controller->hasAssetsDefined()) {
91 | $response['X_OCTOBER_ASSETS'] = $this->controller->getAssetPaths();
92 | }
93 |
94 | $response['scope'] = $this->scope;
95 |
96 | return $response;
97 | }
98 |
99 | public function addModuleDependency($name)
100 | {
101 | if (in_array($name, $this->dependencies))
102 | return false;
103 |
104 | $this->dependencies[] = $name;
105 | return true;
106 | }
107 |
108 | public function getModuleDependencies()
109 | {
110 | return "['".implode("', '", $this->dependencies)."']";
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/components/layout/default.htm:
--------------------------------------------------------------------------------
1 |
8 |
62 |
--------------------------------------------------------------------------------
/updates/version.yaml:
--------------------------------------------------------------------------------
1 | 1.0.1: First version of Angular
--------------------------------------------------------------------------------