├── cache └── .gitignore ├── test ├── Tpl │ └── Tests │ │ ├── _files │ │ └── template.html │ │ ├── Tpl │ │ ├── PluginTest.php │ │ └── PluginContainerTest.php │ │ └── TplTest.php └── bootstrap.php ├── .gitignore ├── templates ├── multiple-template-directories │ ├── base │ │ └── subfolder │ │ │ ├── head.html │ │ │ └── index.html │ └── newimplement │ │ └── subfolder │ │ ├── gnb.html │ │ └── index.html ├── raintpl3 │ ├── img │ │ ├── bg │ │ ├── bg.gif │ │ ├── list │ │ ├── easy.jpg │ │ ├── fast.jpg │ │ ├── list.jpg │ │ ├── logo.gif │ │ ├── logo.jpg │ │ ├── top.gif │ │ ├── bottom.gif │ │ ├── pencil.png │ │ ├── search.gif │ │ ├── useful.jpg │ │ ├── wysiwyg.jpg │ │ ├── easy_text.jpg │ │ ├── fast_text.jpg │ │ ├── googlegroup.jpg │ │ ├── home_title.jpg │ │ ├── useful_text.jpg │ │ ├── wysiwyg_text.jpg │ │ ├── groups_logo_sm.gif │ │ ├── groups_logo_sm.jpg │ │ └── groups_logo_sm.png │ ├── subfolder │ │ └── footer.html │ ├── css │ │ └── style.css │ └── page.html ├── raintpl2 │ ├── img │ │ ├── logo.gif │ │ ├── top.gif │ │ └── bottom.gif │ ├── style.css │ └── page.html ├── image_resize │ ├── img │ │ ├── logo.jpeg │ │ └── wysiwyg.jpg │ ├── page.html │ └── style.css ├── compress │ ├── test.js │ ├── style.css │ └── test_compress.html ├── simple │ ├── simple_template.html │ └── style.css ├── bootstrap │ ├── lib │ │ ├── bootstrap.less │ │ ├── variables.less │ │ ├── scaffolding.less │ │ ├── type.less │ │ ├── reset.less │ │ ├── tables.less │ │ ├── mixins.less │ │ └── forms.less │ ├── js │ │ ├── bootstrap-dropdown.js │ │ ├── bootstrap-buttons.js │ │ ├── bootstrap-tabs.js │ │ ├── bootstrap-popover.js │ │ ├── bootstrap-scrollspy.js │ │ ├── bootstrap-alerts.js │ │ ├── bootstrap-modal.js │ │ └── bootstrap-twipsy.js │ └── hero.html ├── nested_loop │ ├── test.html │ └── style.css └── test │ ├── style.css │ └── test.html ├── .travis.yml ├── composer.json ├── example-multiple-template-directories.php ├── phpunit.xml.dist ├── example-simple.php ├── library └── Rain │ ├── Tpl │ ├── NotFoundException.php │ ├── Exception.php │ ├── IPlugin.php │ ├── SyntaxException.php │ ├── Plugin.php │ ├── PluginContainer.php │ └── Plugin │ │ ├── PathReplace.php │ │ ├── ImageResize.php │ │ └── Compress.php │ ├── autoload.php │ └── Tpl.php ├── example-extend-class.php ├── example-webpage.php ├── example-webpage-new.php ├── example-plugin-compress.php ├── example-bootstrap.php ├── example-nested-loop.php ├── example-plugin-img-resize.php ├── README.md ├── example-all.php └── example-draw-string.php /cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /test/Tpl/Tests/_files/template.html: -------------------------------------------------------------------------------- 1 | template -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | nbproject 3 | vendor/* 4 | test/cache/* 5 | -------------------------------------------------------------------------------- /templates/multiple-template-directories/base/subfolder/head.html: -------------------------------------------------------------------------------- 1 | Old Header -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | -------------------------------------------------------------------------------- /templates/multiple-template-directories/base/subfolder/index.html: -------------------------------------------------------------------------------- 1 | {include="head"} 2 | Old Index -------------------------------------------------------------------------------- /templates/multiple-template-directories/newimplement/subfolder/gnb.html: -------------------------------------------------------------------------------- 1 | {include="head"} 2 | New GNB -------------------------------------------------------------------------------- /templates/raintpl3/img/bg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/bg -------------------------------------------------------------------------------- /templates/raintpl3/img/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/bg.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/list -------------------------------------------------------------------------------- /templates/raintpl2/img/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl2/img/logo.gif -------------------------------------------------------------------------------- /templates/raintpl2/img/top.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl2/img/top.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/easy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/easy.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/fast.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/fast.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/list.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/logo.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/logo.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/top.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/top.gif -------------------------------------------------------------------------------- /templates/raintpl2/img/bottom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl2/img/bottom.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/bottom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/bottom.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/pencil.png -------------------------------------------------------------------------------- /templates/raintpl3/img/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/search.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/useful.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/useful.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/wysiwyg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/wysiwyg.jpg -------------------------------------------------------------------------------- /templates/image_resize/img/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/image_resize/img/logo.jpeg -------------------------------------------------------------------------------- /templates/raintpl3/img/easy_text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/easy_text.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/fast_text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/fast_text.jpg -------------------------------------------------------------------------------- /templates/image_resize/img/wysiwyg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/image_resize/img/wysiwyg.jpg -------------------------------------------------------------------------------- /templates/multiple-template-directories/newimplement/subfolder/index.html: -------------------------------------------------------------------------------- 1 | {include="gnb"} 2 | This is new implement Index Source -------------------------------------------------------------------------------- /templates/raintpl3/img/googlegroup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/googlegroup.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/home_title.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/home_title.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/useful_text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/useful_text.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/wysiwyg_text.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/wysiwyg_text.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/groups_logo_sm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/groups_logo_sm.gif -------------------------------------------------------------------------------- /templates/raintpl3/img/groups_logo_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/groups_logo_sm.jpg -------------------------------------------------------------------------------- /templates/raintpl3/img/groups_logo_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feulf/raintpl3/HEAD/templates/raintpl3/img/groups_logo_sm.png -------------------------------------------------------------------------------- /templates/compress/test.js: -------------------------------------------------------------------------------- 1 | var testClass = { 2 | testFunction: function(){ 3 | alert('this is a test, Javascript can break easily so test your JS before use the CompressPlugin in production'); 4 | } 5 | }; 6 | testClass.testFunction(); -------------------------------------------------------------------------------- /templates/raintpl3/subfolder/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/simple/simple_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |






3
31 |
32 |
33 |
42 |
43 |
44 |
52 |
53 |
54 |
62 |
63 |
64 | Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
51 | 52 |Etiam porta sem malesuada magna mollis euismod. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.
59 | 60 |Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
64 | 65 |Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
69 | 70 |<img src="img/logo.gif" alt="logo"/>
136 | in the compiled template the path is replaced with the correct path tpl/img/logo.gif:
138 | dump \n---------------------- \n\n" . print_r( $mixed, true ) . "\n----------------------";
109 | if( $exit ) exit;
110 | }
111 |
112 |
113 |
114 | /**
115 | * Save the memory used at this point
116 | */
117 | function memoryUsageStart( $memName = "execution_time" ){
118 | return $GLOBALS['memoryCounter'][$memName] = memory_get_usage();
119 | }
120 |
121 |
122 |
123 | /**
124 | * Get the memory used
125 | */
126 | function memoryUsage( $memName = "execution_time", $byteFormat = true ){
127 | $totMem = memory_get_usage() - $GLOBALS['memoryCounter'][ $memName ];
128 | return $byteFormat ? byteFormat($totMem) : $totMem;
129 | }
130 |
131 |
132 | //-------------------------------------------------------------
133 | //
134 | // TIME FUNCTIONS
135 | //
136 | //-------------------------------------------------------------
137 |
138 | /**
139 | * Start the timer
140 | */
141 | function timerStart( $timeName = "execution_time" ){
142 | $stimer = explode( ' ', microtime( ) );
143 | $GLOBALS['timeCounter'][$timeName] = $stimer[ 1 ] + $stimer[ 0 ];
144 | }
145 |
146 | /**
147 | * Get the time passed
148 | */
149 | function timer( $timeName = "execution_time", $precision = 10 ){
150 | $etimer = explode( ' ', microtime( ) );
151 | $timeElapsed = $etimer[ 1 ] + $etimer[ 0 ] - $GLOBALS['timeCounter'][ $timeName ];
152 | return substr( $timeElapsed, 0, $precision );
153 | }
154 |
155 |
156 | /**
157 | * Convert byte to more readable format, like "1 KB" instead of "1024".
158 | * cut_zero, remove the 0 after comma ex: 10,00 => 10 14,30 => 14,3
159 | */
160 | function byteFormat( $size ){
161 | if( $size > 0 ){
162 | $unim = array("B","KB","MB","GB","TB","PB");
163 | for( $i=0; $size >= 1024; $i++ )
164 | $size = $size / 1024;
165 | return number_format($size,$i?2:0, ',', '.' )." ".$unim[$i];
166 | }
167 | }
168 |
169 | // end
--------------------------------------------------------------------------------
/templates/bootstrap/lib/tables.less:
--------------------------------------------------------------------------------
1 | /*
2 | * Tables.less
3 | * Tables for, you guessed it, tabular data
4 | * ---------------------------------------- */
5 |
6 |
7 | // BASELINE STYLES
8 | // ---------------
9 |
10 | table {
11 | width: 100%;
12 | margin-bottom: @baseline;
13 | padding: 0;
14 | font-size: @basefont;
15 | border-collapse: collapse;
16 | th,
17 | td {
18 | padding: 10px 10px 9px;
19 | line-height: @baseline;
20 | text-align: left;
21 | }
22 | th {
23 | padding-top: 9px;
24 | font-weight: bold;
25 | vertical-align: middle;
26 | }
27 | td {
28 | vertical-align: top;
29 | border-top: 1px solid #ddd;
30 | }
31 | // When scoped to row, fix th in tbody
32 | tbody th {
33 | border-top: 1px solid #ddd;
34 | vertical-align: top;
35 | }
36 | }
37 |
38 |
39 | // CONDENSED VERSION
40 | // -----------------
41 | .condensed-table {
42 | th,
43 | td {
44 | padding: 5px 5px 4px;
45 | }
46 | }
47 |
48 |
49 | // BORDERED VERSION
50 | // ----------------
51 |
52 | .bordered-table {
53 | border: 1px solid #ddd;
54 | border-collapse: separate; // Done so we can round those corners!
55 | *border-collapse: collapse; /* IE7, collapse table to remove spacing */
56 | .border-radius(4px);
57 | th + th,
58 | td + td,
59 | th + td {
60 | border-left: 1px solid #ddd;
61 | }
62 | thead tr:first-child th:first-child,
63 | tbody tr:first-child td:first-child {
64 | .border-radius(4px 0 0 0);
65 | }
66 | thead tr:first-child th:last-child,
67 | tbody tr:first-child td:last-child {
68 | .border-radius(0 4px 0 0);
69 | }
70 | tbody tr:last-child td:first-child {
71 | .border-radius(0 0 0 4px);
72 | }
73 | tbody tr:last-child td:last-child {
74 | .border-radius(0 0 4px 0);
75 | }
76 | }
77 |
78 |
79 | // TABLE CELL SIZES
80 | // ----------------
81 |
82 | // This is a duplication of the main grid .columns() mixin, but subtracts 20px to account for input padding and border
83 | .tableColumns(@columnSpan: 1) {
84 | width: ((@gridColumnWidth - 20) * @columnSpan) + ((@gridColumnWidth - 20) * (@columnSpan - 1));
85 | }
86 | table {
87 | // Default columns
88 | .span1 { .tableColumns(1); }
89 | .span2 { .tableColumns(2); }
90 | .span3 { .tableColumns(3); }
91 | .span4 { .tableColumns(4); }
92 | .span5 { .tableColumns(5); }
93 | .span6 { .tableColumns(6); }
94 | .span7 { .tableColumns(7); }
95 | .span8 { .tableColumns(8); }
96 | .span9 { .tableColumns(9); }
97 | .span10 { .tableColumns(10); }
98 | .span11 { .tableColumns(11); }
99 | .span12 { .tableColumns(12); }
100 | .span13 { .tableColumns(13); }
101 | .span14 { .tableColumns(14); }
102 | .span15 { .tableColumns(15); }
103 | .span16 { .tableColumns(16); }
104 | }
105 |
106 |
107 | // ZEBRA-STRIPING
108 | // --------------
109 |
110 | // Default zebra-stripe styles (alternating gray and transparent backgrounds)
111 | .zebra-striped {
112 | tbody {
113 | tr:nth-child(odd) td,
114 | tr:nth-child(odd) th {
115 | background-color: #f9f9f9;
116 | }
117 | tr:hover td,
118 | tr:hover th {
119 | background-color: #f5f5f5;
120 | }
121 | }
122 | }
123 |
124 | table {
125 | // Tablesorting styles w/ jQuery plugin
126 | .header {
127 | cursor: pointer;
128 | &:after {
129 | content: "";
130 | float: right;
131 | margin-top: 7px;
132 | border-width: 0 4px 4px;
133 | border-style: solid;
134 | border-color: #000 transparent;
135 | visibility: hidden;
136 | }
137 | }
138 | // Style the sorted column headers (THs)
139 | .headerSortUp,
140 | .headerSortDown {
141 | background-color: rgba(141,192,219,.25);
142 | text-shadow: 0 1px 1px rgba(255,255,255,.75);
143 | }
144 | // Style the ascending (reverse alphabetical) column header
145 | .header:hover {
146 | &:after {
147 | visibility:visible;
148 | }
149 | }
150 | // Style the descending (alphabetical) column header
151 | .headerSortDown,
152 | .headerSortDown:hover {
153 | &:after {
154 | visibility:visible;
155 | .opacity(60);
156 | }
157 | }
158 | // Style the ascending (reverse alphabetical) column header
159 | .headerSortUp {
160 | &:after {
161 | border-bottom: none;
162 | border-left: 4px solid transparent;
163 | border-right: 4px solid transparent;
164 | border-top: 4px solid #000;
165 | visibility:visible;
166 | .box-shadow(none); //can't add boxshadow to downward facing arrow :(
167 | .opacity(60);
168 | }
169 | }
170 | // Blue Table Headings
171 | .blue {
172 | color: @blue;
173 | border-bottom-color: @blue;
174 | }
175 | .headerSortUp.blue,
176 | .headerSortDown.blue {
177 | background-color: lighten(@blue, 40%);
178 | }
179 | // Green Table Headings
180 | .green {
181 | color: @green;
182 | border-bottom-color: @green;
183 | }
184 | .headerSortUp.green,
185 | .headerSortDown.green {
186 | background-color: lighten(@green, 40%);
187 | }
188 | // Red Table Headings
189 | .red {
190 | color: @red;
191 | border-bottom-color: @red;
192 | }
193 | .headerSortUp.red,
194 | .headerSortDown.red {
195 | background-color: lighten(@red, 50%);
196 | }
197 | // Yellow Table Headings
198 | .yellow {
199 | color: @yellow;
200 | border-bottom-color: @yellow;
201 | }
202 | .headerSortUp.yellow,
203 | .headerSortDown.yellow {
204 | background-color: lighten(@yellow, 40%);
205 | }
206 | // Orange Table Headings
207 | .orange {
208 | color: @orange;
209 | border-bottom-color: @orange;
210 | }
211 | .headerSortUp.orange,
212 | .headerSortDown.orange {
213 | background-color: lighten(@orange, 40%);
214 | }
215 | // Purple Table Headings
216 | .purple {
217 | color: @purple;
218 | border-bottom-color: @purple;
219 | }
220 | .headerSortUp.purple,
221 | .headerSortDown.purple {
222 | background-color: lighten(@purple, 40%);
223 | }
224 | }
--------------------------------------------------------------------------------
/library/Rain/Tpl/Plugin/PathReplace.php:
--------------------------------------------------------------------------------
1 | template_dir/url
37 | * url# => url
38 | * http://url => http://url
39 | *
40 | * @param \ArrayAccess $context
41 | */
42 | public function afterParse(\ArrayAccess $context){
43 |
44 | // set variables
45 | $html = $context->code;
46 | $template_basedir = $context->template_basedir;
47 | $tags = $this->tags;
48 | $basecode = "";
49 |
50 |
51 | // get the template base directory
52 | $template_directory = $basecode . $context->conf['tpl_dir'] . $context->template_basedir;
53 |
54 | // reduce the path
55 | $path = str_replace( "://", "@not_replace@", $template_directory );
56 | $path = preg_replace( "#(/+)#", "/", $path );
57 | $path = preg_replace( "#(/\./+)#", "/", $path );
58 | $path = str_replace( "@not_replace@", "://", $path );
59 |
60 | while( preg_match( '#\.\./#', $path ) ){
61 | $path = preg_replace('#\w+/\.\./#', '', $path );
62 | }
63 |
64 |
65 |
66 | $exp = $sub = array();
67 |
68 | if( in_array( "img", $tags ) ){
69 | $exp = array( '/
code = preg_replace( $exp, $sub, $html );
109 | }
110 |
111 |
112 |
113 | public function setTags($tags) {
114 | $this->tags = (array) $tags;
115 | return $this;
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/library/Rain/Tpl/Plugin/ImageResize.php:
--------------------------------------------------------------------------------
1 | code;
38 | $template_basedir = $context->template_basedir;
39 | $quality = $this->quality;
40 | $auto_crop = $this->crop;
41 | $conf = $context->conf;
42 |
43 | $img_cache_dir = $template_basedir = $conf['cache_dir'];
44 |
45 |
46 | // get the template base directory
47 | $template_directory = $conf['base_url'] . $conf['tpl_dir'] . $template_basedir;
48 |
49 | // reduce the path
50 | $path = preg_replace('/\w+\/\.\.\//', '', $template_directory );
51 |
52 | $exp = $sub = array();
53 |
54 | $image_resized = false;
55 |
56 | // match the images
57 | if( preg_match_all( '/
.*?)"))|(\s*(width="(?.*?)"))|(\s*height="(?.*?)")|(\s*resize="(?.*?)")|(\s*crop="(?.*?)"))*.*?>/', $html, $matches ) ){
58 |
59 | for( $i=0,$n=count($matches[0]); $i<$n; $i++ ){
60 | $tag = $matches[0][$i];
61 | $src = $matches['src'][$i];
62 | $w = $matches['width'][$i];
63 | $h = $matches['height'][$i];
64 | $resize = $matches['resize'][$i];
65 | if( $auto_crop )
66 | $crop = $matches['crop'][$i] == 'false' ? false : true;
67 | else
68 | $crop = $matches['crop'][$i] == 'true' ? true : false;
69 |
70 | if( $w > 0 && $h > 0 && $resize != 'false' ){
71 | echo $src;exit;
72 | $new_tag = preg_replace( '/(.*?)src="(.*?)"(.*?)/', '$1src=""$3', $tag );
73 | $html = str_replace( $tag, $new_tag, $html );
74 | $image_resized = true;
75 | }
76 |
77 | }
78 |
79 | if( $image_resized )
80 | $html = '' . $html;
81 |
82 | }
83 |
84 | $context->code = $html;
85 | }
86 |
87 | public function setQuality($quality) {
88 | $this->quality = (int) $quality;
89 | return $this;
90 | }
91 |
92 | public function setCrop($crop) {
93 | $this->crop = (string) $crop;
94 | return $this;
95 | }
96 |
97 | public static function imgResize( $src, $dest, $w, $h, $quality, $crop ){
98 |
99 | $ext = substr(strrchr($src, '.'),1);
100 | $dest = $dest . 'img.'. md5( $src . $crop . $quality ) . $w . 'x' . $h . '.' . $ext;
101 |
102 |
103 | if( !file_exists( $dest ) )
104 | static::rainImgResize( $src, $dest, $w, $h, $quality, $crop );
105 | return $dest;
106 |
107 | }
108 |
109 | public static function rainImgResize($src, $dst, $width, $height, $quality, $crop=0){
110 |
111 | if(!list($w, $h) = getimagesize($src)) return "Unsupported picture type!";
112 |
113 | $type = strtolower(substr(strrchr($src,"."),1));
114 | if($type == 'jpeg') $type = 'jpg';
115 | switch($type){
116 | case 'bmp': $img = imagecreatefromwbmp($src); break;
117 | case 'gif': $img = imagecreatefromgif($src); break;
118 | case 'jpg': $img = imagecreatefromjpeg($src); break;
119 | case 'png': $img = imagecreatefrompng($src); break;
120 | default : return "Unsupported picture type!";
121 | }
122 |
123 | // resize
124 | if($crop){
125 | if($w < $width or $h < $height) return "Picture is too small!";
126 | $ratio = max($width/$w, $height/$h);
127 | $h = $height / $ratio;
128 | $x = ($w - $width / $ratio) / 2;
129 | $w = $width / $ratio;
130 | }
131 | else{
132 | if($w < $width and $h < $height) return "Picture is too small!";
133 | $ratio = min($width/$w, $height/$h);
134 | $width = $w * $ratio;
135 | $height = $h * $ratio;
136 | $x = 0;
137 | }
138 |
139 | $new = imagecreatetruecolor($width, $height);
140 |
141 | // preserve transparency
142 | if($type == "gif" or $type == "png"){
143 | imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
144 | imagealphablending($new, false);
145 | imagesavealpha($new, true);
146 | }
147 |
148 | imagecopyresampled($new, $img, 0, 0, $x, 0, $width, $height, $w, $h);
149 |
150 | switch($type){
151 | case 'bmp': imagewbmp($new, $dst, $quality); break;
152 | case 'gif': imagegif($new, $dst, $quality); break;
153 | case 'jpg': imagejpeg($new, $dst, $quality); break;
154 | case 'png': imagepng($new, $dst, $quality); break;
155 | }
156 | return true;
157 | }
158 |
159 | }
--------------------------------------------------------------------------------
/templates/bootstrap/js/bootstrap-modal.js:
--------------------------------------------------------------------------------
1 | /* =========================================================
2 | * bootstrap-modal.js v1.4.0
3 | * http://twitter.github.com/bootstrap/javascript.html#modal
4 | * =========================================================
5 | * Copyright 2011 Twitter, Inc.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | * ========================================================= */
19 |
20 |
21 | !function( $ ){
22 |
23 | "use strict"
24 |
25 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
26 | * ======================================================= */
27 |
28 | var transitionEnd
29 |
30 | $(document).ready(function () {
31 |
32 | $.support.transition = (function () {
33 | var thisBody = document.body || document.documentElement
34 | , thisStyle = thisBody.style
35 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
36 | return support
37 | })()
38 |
39 | // set CSS transition event type
40 | if ( $.support.transition ) {
41 | transitionEnd = "TransitionEnd"
42 | if ( $.browser.webkit ) {
43 | transitionEnd = "webkitTransitionEnd"
44 | } else if ( $.browser.mozilla ) {
45 | transitionEnd = "transitionend"
46 | } else if ( $.browser.opera ) {
47 | transitionEnd = "oTransitionEnd"
48 | }
49 | }
50 |
51 | })
52 |
53 |
54 | /* MODAL PUBLIC CLASS DEFINITION
55 | * ============================= */
56 |
57 | var Modal = function ( content, options ) {
58 | this.settings = $.extend({}, $.fn.modal.defaults, options)
59 | this.$element = $(content)
60 | .delegate('.close', 'click.modal', $.proxy(this.hide, this))
61 |
62 | if ( this.settings.show ) {
63 | this.show()
64 | }
65 |
66 | return this
67 | }
68 |
69 | Modal.prototype = {
70 |
71 | toggle: function () {
72 | return this[!this.isShown ? 'show' : 'hide']()
73 | }
74 |
75 | , show: function () {
76 | var that = this
77 | this.isShown = true
78 | this.$element.trigger('show')
79 |
80 | escape.call(this)
81 | backdrop.call(this, function () {
82 | var transition = $.support.transition && that.$element.hasClass('fade')
83 |
84 | that.$element
85 | .appendTo(document.body)
86 | .show()
87 |
88 | if (transition) {
89 | that.$element[0].offsetWidth // force reflow
90 | }
91 |
92 | that.$element.addClass('in')
93 |
94 | transition ?
95 | that.$element.one(transitionEnd, function () { that.$element.trigger('shown') }) :
96 | that.$element.trigger('shown')
97 |
98 | })
99 |
100 | return this
101 | }
102 |
103 | , hide: function (e) {
104 | e && e.preventDefault()
105 |
106 | if ( !this.isShown ) {
107 | return this
108 | }
109 |
110 | var that = this
111 | this.isShown = false
112 |
113 | escape.call(this)
114 |
115 | this.$element
116 | .trigger('hide')
117 | .removeClass('in')
118 |
119 | $.support.transition && this.$element.hasClass('fade') ?
120 | hideWithTransition.call(this) :
121 | hideModal.call(this)
122 |
123 | return this
124 | }
125 |
126 | }
127 |
128 |
129 | /* MODAL PRIVATE METHODS
130 | * ===================== */
131 |
132 | function hideWithTransition() {
133 | // firefox drops transitionEnd events :{o
134 | var that = this
135 | , timeout = setTimeout(function () {
136 | that.$element.unbind(transitionEnd)
137 | hideModal.call(that)
138 | }, 500)
139 |
140 | this.$element.one(transitionEnd, function () {
141 | clearTimeout(timeout)
142 | hideModal.call(that)
143 | })
144 | }
145 |
146 | function hideModal (that) {
147 | this.$element
148 | .hide()
149 | .trigger('hidden')
150 |
151 | backdrop.call(this)
152 | }
153 |
154 | function backdrop ( callback ) {
155 | var that = this
156 | , animate = this.$element.hasClass('fade') ? 'fade' : ''
157 | if ( this.isShown && this.settings.backdrop ) {
158 | var doAnimate = $.support.transition && animate
159 |
160 | this.$backdrop = $('')
161 | .appendTo(document.body)
162 |
163 | if ( this.settings.backdrop != 'static' ) {
164 | this.$backdrop.click($.proxy(this.hide, this))
165 | }
166 |
167 | if ( doAnimate ) {
168 | this.$backdrop[0].offsetWidth // force reflow
169 | }
170 |
171 | this.$backdrop.addClass('in')
172 |
173 | doAnimate ?
174 | this.$backdrop.one(transitionEnd, callback) :
175 | callback()
176 |
177 | } else if ( !this.isShown && this.$backdrop ) {
178 | this.$backdrop.removeClass('in')
179 |
180 | $.support.transition && this.$element.hasClass('fade')?
181 | this.$backdrop.one(transitionEnd, $.proxy(removeBackdrop, this)) :
182 | removeBackdrop.call(this)
183 |
184 | } else if ( callback ) {
185 | callback()
186 | }
187 | }
188 |
189 | function removeBackdrop() {
190 | this.$backdrop.remove()
191 | this.$backdrop = null
192 | }
193 |
194 | function escape() {
195 | var that = this
196 | if ( this.isShown && this.settings.keyboard ) {
197 | $(document).bind('keyup.modal', function ( e ) {
198 | if ( e.which == 27 ) {
199 | that.hide()
200 | }
201 | })
202 | } else if ( !this.isShown ) {
203 | $(document).unbind('keyup.modal')
204 | }
205 | }
206 |
207 |
208 | /* MODAL PLUGIN DEFINITION
209 | * ======================= */
210 |
211 | $.fn.modal = function ( options ) {
212 | var modal = this.data('modal')
213 |
214 | if (!modal) {
215 |
216 | if (typeof options == 'string') {
217 | options = {
218 | show: /show|toggle/.test(options)
219 | }
220 | }
221 |
222 | return this.each(function () {
223 | $(this).data('modal', new Modal(this, options))
224 | })
225 | }
226 |
227 | if ( options === true ) {
228 | return modal
229 | }
230 |
231 | if ( typeof options == 'string' ) {
232 | modal[options]()
233 | } else if ( modal ) {
234 | modal.toggle()
235 | }
236 |
237 | return this
238 | }
239 |
240 | $.fn.modal.Modal = Modal
241 |
242 | $.fn.modal.defaults = {
243 | backdrop: false
244 | , keyboard: false
245 | , show: false
246 | }
247 |
248 |
249 | /* MODAL DATA- IMPLEMENTATION
250 | * ========================== */
251 |
252 | $(document).ready(function () {
253 | $('body').delegate('[data-controls-modal]', 'click', function (e) {
254 | e.preventDefault()
255 | var $this = $(this).data('show', true)
256 | $('#' + $this.attr('data-controls-modal')).modal( $this.data() )
257 | })
258 | })
259 |
260 | }( window.jQuery || window.ender );
261 |
--------------------------------------------------------------------------------
/templates/bootstrap/lib/mixins.less:
--------------------------------------------------------------------------------
1 | /* Mixins.less
2 | * Snippets of reusable CSS to develop faster and keep code readable
3 | * ----------------------------------------------------------------- */
4 |
5 |
6 | // Clearfix for clearing floats like a boss h5bp.com/q
7 | .clearfix() {
8 | zoom: 1;
9 | &:before,
10 | &:after {
11 | display: table;
12 | content: "";
13 | zoom: 1;
14 | }
15 | &:after {
16 | clear: both;
17 | }
18 | }
19 |
20 | // Center-align a block level element
21 | .center-block() {
22 | display: block;
23 | margin-left: auto;
24 | margin-right: auto;
25 | }
26 |
27 | // Sizing shortcuts
28 | .size(@height: 5px, @width: 5px) {
29 | height: @height;
30 | width: @width;
31 | }
32 | .square(@size: 5px) {
33 | .size(@size, @size);
34 | }
35 |
36 | // Input placeholder text
37 | .placeholder(@color: @grayLight) {
38 | :-moz-placeholder {
39 | color: @color;
40 | }
41 | ::-webkit-input-placeholder {
42 | color: @color;
43 | }
44 | }
45 |
46 | // Font Stacks
47 | #font {
48 | .shorthand(@weight: normal, @size: 14px, @lineHeight: 20px) {
49 | font-size: @size;
50 | font-weight: @weight;
51 | line-height: @lineHeight;
52 | }
53 | .sans-serif(@weight: normal, @size: 14px, @lineHeight: 20px) {
54 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
55 | font-size: @size;
56 | font-weight: @weight;
57 | line-height: @lineHeight;
58 | }
59 | .serif(@weight: normal, @size: 14px, @lineHeight: 20px) {
60 | font-family: "Georgia", Times New Roman, Times, serif;
61 | font-size: @size;
62 | font-weight: @weight;
63 | line-height: @lineHeight;
64 | }
65 | .monospace(@weight: normal, @size: 12px, @lineHeight: 20px) {
66 | font-family: "Monaco", Courier New, monospace;
67 | font-size: @size;
68 | font-weight: @weight;
69 | line-height: @lineHeight;
70 | }
71 | }
72 |
73 | // Grid System
74 | .fixed-container() {
75 | width: @siteWidth;
76 | margin-left: auto;
77 | margin-right: auto;
78 | .clearfix();
79 | }
80 | .columns(@columnSpan: 1) {
81 | width: (@gridColumnWidth * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1));
82 | }
83 | .offset(@columnOffset: 1) {
84 | margin-left: (@gridColumnWidth * @columnOffset) + (@gridGutterWidth * (@columnOffset - 1)) + @extraSpace;
85 | }
86 | // Necessary grid styles for every column to make them appear next to each other horizontally
87 | .gridColumn() {
88 | display: inline;
89 | float: left;
90 | margin-left: @gridGutterWidth;
91 | }
92 | // makeColumn can be used to mark any element (e.g., .content-primary) as a column without changing markup to .span something
93 | .makeColumn(@columnSpan: 1) {
94 | .gridColumn();
95 | .columns(@columnSpan);
96 | }
97 |
98 | // Border Radius
99 | .border-radius(@radius: 5px) {
100 | -webkit-border-radius: @radius;
101 | -moz-border-radius: @radius;
102 | border-radius: @radius;
103 | }
104 |
105 | // Drop shadows
106 | .box-shadow(@shadow: 0 1px 3px rgba(0,0,0,.25)) {
107 | -webkit-box-shadow: @shadow;
108 | -moz-box-shadow: @shadow;
109 | box-shadow: @shadow;
110 | }
111 |
112 | // Transitions
113 | .transition(@transition) {
114 | -webkit-transition: @transition;
115 | -moz-transition: @transition;
116 | -ms-transition: @transition;
117 | -o-transition: @transition;
118 | transition: @transition;
119 | }
120 |
121 | // Background clipping
122 | .background-clip(@clip) {
123 | -webkit-background-clip: @clip;
124 | -moz-background-clip: @clip;
125 | background-clip: @clip;
126 | }
127 |
128 | // CSS3 Content Columns
129 | .content-columns(@columnCount, @columnGap: 20px) {
130 | -webkit-column-count: @columnCount;
131 | -moz-column-count: @columnCount;
132 | column-count: @columnCount;
133 | -webkit-column-gap: @columnGap;
134 | -moz-column-gap: @columnGap;
135 | column-gap: @columnGap;
136 | }
137 |
138 | // Make any element resizable for prototyping
139 | .resizable(@direction: both) {
140 | resize: @direction; // Options are horizontal, vertical, both
141 | overflow: auto; // Safari fix
142 | }
143 |
144 | // Add an alphatransparency value to any background or border color (via Elyse Holladay)
145 | #translucent {
146 | .background(@color: @white, @alpha: 1) {
147 | background-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
148 | }
149 | .border(@color: @white, @alpha: 1) {
150 | border-color: hsla(hue(@color), saturation(@color), lightness(@color), @alpha);
151 | background-clip: padding-box;
152 | }
153 | }
154 |
155 | // Gradient Bar Colors for buttons and allerts
156 | .gradientBar(@primaryColor, @secondaryColor) {
157 | #gradient > .vertical(@primaryColor, @secondaryColor);
158 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
159 | border-color: @secondaryColor @secondaryColor darken(@secondaryColor, 15%);
160 | border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) fadein(rgba(0,0,0,.1), 15%);
161 | }
162 |
163 | // Gradients
164 | #gradient {
165 | .horizontal (@startColor: #555, @endColor: #333) {
166 | background-color: @endColor;
167 | background-repeat: repeat-x;
168 | background-image: -khtml-gradient(linear, left top, right top, from(@startColor), to(@endColor)); // Konqueror
169 | background-image: -moz-linear-gradient(left, @startColor, @endColor); // FF 3.6+
170 | background-image: -ms-linear-gradient(left, @startColor, @endColor); // IE10
171 | background-image: -webkit-gradient(linear, left top, right top, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+
172 | background-image: -webkit-linear-gradient(left, @startColor, @endColor); // Safari 5.1+, Chrome 10+
173 | background-image: -o-linear-gradient(left, @startColor, @endColor); // Opera 11.10
174 | background-image: linear-gradient(left, @startColor, @endColor); // Le standard
175 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",@startColor,@endColor)); // IE9 and down
176 | }
177 | .vertical (@startColor: #555, @endColor: #333) {
178 | background-color: @endColor;
179 | background-repeat: repeat-x;
180 | background-image: -khtml-gradient(linear, left top, left bottom, from(@startColor), to(@endColor)); // Konqueror
181 | background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+
182 | background-image: -ms-linear-gradient(top, @startColor, @endColor); // IE10
183 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @startColor), color-stop(100%, @endColor)); // Safari 4+, Chrome 2+
184 | background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+
185 | background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10
186 | background-image: linear-gradient(top, @startColor, @endColor); // The standard
187 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down
188 | }
189 | .directional (@startColor: #555, @endColor: #333, @deg: 45deg) {
190 | background-color: @endColor;
191 | background-repeat: repeat-x;
192 | background-image: -moz-linear-gradient(@deg, @startColor, @endColor); // FF 3.6+
193 | background-image: -ms-linear-gradient(@deg, @startColor, @endColor); // IE10
194 | background-image: -webkit-linear-gradient(@deg, @startColor, @endColor); // Safari 5.1+, Chrome 10+
195 | background-image: -o-linear-gradient(@deg, @startColor, @endColor); // Opera 11.10
196 | background-image: linear-gradient(@deg, @startColor, @endColor); // The standard
197 | }
198 | .vertical-three-colors(@startColor: #00b3ee, @midColor: #7a43b6, @colorStop: 50%, @endColor: #c3325f) {
199 | background-color: @endColor;
200 | background-repeat: no-repeat;
201 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), color-stop(@colorStop, @midColor), to(@endColor));
202 | background-image: -webkit-linear-gradient(@startColor, @midColor @colorStop, @endColor);
203 | background-image: -moz-linear-gradient(top, @startColor, @midColor @colorStop, @endColor);
204 | background-image: -ms-linear-gradient(@startColor, @midColor @colorStop, @endColor);
205 | background-image: -o-linear-gradient(@startColor, @midColor @colorStop, @endColor);
206 | background-image: linear-gradient(@startColor, @midColor @colorStop, @endColor);
207 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",@startColor,@endColor)); // IE9 and down, gets no color-stop at all for proper fallback
208 | }
209 | }
210 |
211 | // Reset filters for IE
212 | .reset-filter() {
213 | filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
214 | }
215 |
216 | // Opacity
217 | .opacity(@opacity: 100) {
218 | filter: e(%("alpha(opacity=%d)", @opacity));
219 | -khtml-opacity: @opacity / 100;
220 | -moz-opacity: @opacity / 100;
221 | opacity: @opacity / 100;
222 | }
--------------------------------------------------------------------------------
/templates/bootstrap/js/bootstrap-twipsy.js:
--------------------------------------------------------------------------------
1 | /* ==========================================================
2 | * bootstrap-twipsy.js v1.4.0
3 | * http://twitter.github.com/bootstrap/javascript.html#twipsy
4 | * Adapted from the original jQuery.tipsy by Jason Frame
5 | * ==========================================================
6 | * Copyright 2011 Twitter, Inc.
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * http://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | * ========================================================== */
20 |
21 |
22 | !function( $ ) {
23 |
24 | "use strict"
25 |
26 | /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
27 | * ======================================================= */
28 |
29 | var transitionEnd
30 |
31 | $(document).ready(function () {
32 |
33 | $.support.transition = (function () {
34 | var thisBody = document.body || document.documentElement
35 | , thisStyle = thisBody.style
36 | , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
37 | return support
38 | })()
39 |
40 | // set CSS transition event type
41 | if ( $.support.transition ) {
42 | transitionEnd = "TransitionEnd"
43 | if ( $.browser.webkit ) {
44 | transitionEnd = "webkitTransitionEnd"
45 | } else if ( $.browser.mozilla ) {
46 | transitionEnd = "transitionend"
47 | } else if ( $.browser.opera ) {
48 | transitionEnd = "oTransitionEnd"
49 | }
50 | }
51 |
52 | })
53 |
54 |
55 | /* TWIPSY PUBLIC CLASS DEFINITION
56 | * ============================== */
57 |
58 | var Twipsy = function ( element, options ) {
59 | this.$element = $(element)
60 | this.options = options
61 | this.enabled = true
62 | this.fixTitle()
63 | }
64 |
65 | Twipsy.prototype = {
66 |
67 | show: function() {
68 | var pos
69 | , actualWidth
70 | , actualHeight
71 | , placement
72 | , $tip
73 | , tp
74 |
75 | if (this.hasContent() && this.enabled) {
76 | $tip = this.tip()
77 | this.setContent()
78 |
79 | if (this.options.animate) {
80 | $tip.addClass('fade')
81 | }
82 |
83 | $tip
84 | .remove()
85 | .css({ top: 0, left: 0, display: 'block' })
86 | .prependTo(document.body)
87 |
88 | pos = $.extend({}, this.$element.offset(), {
89 | width: this.$element[0].offsetWidth
90 | , height: this.$element[0].offsetHeight
91 | })
92 |
93 | actualWidth = $tip[0].offsetWidth
94 | actualHeight = $tip[0].offsetHeight
95 |
96 | placement = maybeCall(this.options.placement, this, [ $tip[0], this.$element[0] ])
97 |
98 | switch (placement) {
99 | case 'below':
100 | tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
101 | break
102 | case 'above':
103 | tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}
104 | break
105 | case 'left':
106 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}
107 | break
108 | case 'right':
109 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}
110 | break
111 | }
112 |
113 | $tip
114 | .css(tp)
115 | .addClass(placement)
116 | .addClass('in')
117 | }
118 | }
119 |
120 | , setContent: function () {
121 | var $tip = this.tip()
122 | $tip.find('.twipsy-inner')[this.options.html ? 'html' : 'text'](this.getTitle())
123 | $tip[0].className = 'twipsy'
124 | }
125 |
126 | , hide: function() {
127 | var that = this
128 | , $tip = this.tip()
129 |
130 | $tip.removeClass('in')
131 |
132 | function removeElement () {
133 | $tip.remove()
134 | }
135 |
136 | $.support.transition && this.$tip.hasClass('fade') ?
137 | $tip.bind(transitionEnd, removeElement) :
138 | removeElement()
139 | }
140 |
141 | , fixTitle: function() {
142 | var $e = this.$element
143 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
144 | $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
145 | }
146 | }
147 |
148 | , hasContent: function () {
149 | return this.getTitle()
150 | }
151 |
152 | , getTitle: function() {
153 | var title
154 | , $e = this.$element
155 | , o = this.options
156 |
157 | this.fixTitle()
158 |
159 | if (typeof o.title == 'string') {
160 | title = $e.attr(o.title == 'title' ? 'data-original-title' : o.title)
161 | } else if (typeof o.title == 'function') {
162 | title = o.title.call($e[0])
163 | }
164 |
165 | title = ('' + title).replace(/(^\s*|\s*$)/, "")
166 |
167 | return title || o.fallback
168 | }
169 |
170 | , tip: function() {
171 | return this.$tip = this.$tip || $('').html(this.options.template)
172 | }
173 |
174 | , validate: function() {
175 | if (!this.$element[0].parentNode) {
176 | this.hide()
177 | this.$element = null
178 | this.options = null
179 | }
180 | }
181 |
182 | , enable: function() {
183 | this.enabled = true
184 | }
185 |
186 | , disable: function() {
187 | this.enabled = false
188 | }
189 |
190 | , toggleEnabled: function() {
191 | this.enabled = !this.enabled
192 | }
193 |
194 | , toggle: function () {
195 | this[this.tip().hasClass('in') ? 'hide' : 'show']()
196 | }
197 |
198 | }
199 |
200 |
201 | /* TWIPSY PRIVATE METHODS
202 | * ====================== */
203 |
204 | function maybeCall ( thing, ctx, args ) {
205 | return typeof thing == 'function' ? thing.apply(ctx, args) : thing
206 | }
207 |
208 | /* TWIPSY PLUGIN DEFINITION
209 | * ======================== */
210 |
211 | $.fn.twipsy = function (options) {
212 | $.fn.twipsy.initWith.call(this, options, Twipsy, 'twipsy')
213 | return this
214 | }
215 |
216 | $.fn.twipsy.initWith = function (options, Constructor, name) {
217 | var twipsy
218 | , binder
219 | , eventIn
220 | , eventOut
221 |
222 | if (options === true) {
223 | return this.data(name)
224 | } else if (typeof options == 'string') {
225 | twipsy = this.data(name)
226 | if (twipsy) {
227 | twipsy[options]()
228 | }
229 | return this
230 | }
231 |
232 | options = $.extend({}, $.fn[name].defaults, options)
233 |
234 | function get(ele) {
235 | var twipsy = $.data(ele, name)
236 |
237 | if (!twipsy) {
238 | twipsy = new Constructor(ele, $.fn.twipsy.elementOptions(ele, options))
239 | $.data(ele, name, twipsy)
240 | }
241 |
242 | return twipsy
243 | }
244 |
245 | function enter() {
246 | var twipsy = get(this)
247 | twipsy.hoverState = 'in'
248 |
249 | if (options.delayIn == 0) {
250 | twipsy.show()
251 | } else {
252 | twipsy.fixTitle()
253 | setTimeout(function() {
254 | if (twipsy.hoverState == 'in') {
255 | twipsy.show()
256 | }
257 | }, options.delayIn)
258 | }
259 | }
260 |
261 | function leave() {
262 | var twipsy = get(this)
263 | twipsy.hoverState = 'out'
264 | if (options.delayOut == 0) {
265 | twipsy.hide()
266 | } else {
267 | setTimeout(function() {
268 | if (twipsy.hoverState == 'out') {
269 | twipsy.hide()
270 | }
271 | }, options.delayOut)
272 | }
273 | }
274 |
275 | if (!options.live) {
276 | this.each(function() {
277 | get(this)
278 | })
279 | }
280 |
281 | if (options.trigger != 'manual') {
282 | binder = options.live ? 'live' : 'bind'
283 | eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus'
284 | eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur'
285 | this[binder](eventIn, enter)[binder](eventOut, leave)
286 | }
287 |
288 | return this
289 | }
290 |
291 | $.fn.twipsy.Twipsy = Twipsy
292 |
293 | $.fn.twipsy.defaults = {
294 | animate: true
295 | , delayIn: 0
296 | , delayOut: 0
297 | , fallback: ''
298 | , placement: 'above'
299 | , html: false
300 | , live: false
301 | , offset: 0
302 | , title: 'title'
303 | , trigger: 'hover'
304 | , template: ''
305 | }
306 |
307 | $.fn.twipsy.rejectAttrOptions = [ 'title' ]
308 |
309 | $.fn.twipsy.elementOptions = function(ele, options) {
310 | var data = $(ele).data()
311 | , rejects = $.fn.twipsy.rejectAttrOptions
312 | , i = rejects.length
313 |
314 | while (i--) {
315 | delete data[rejects[i]]
316 | }
317 |
318 | return $.extend({}, options, data)
319 | }
320 |
321 | }( window.jQuery || window.ender );
--------------------------------------------------------------------------------
/library/Rain/Tpl/Plugin/Compress.php:
--------------------------------------------------------------------------------
1 | array('status'=>true),
37 | 'css' =>array('status'=>true),
38 | 'javascript'=>array('status'=>true, 'position'=>'bottom'),
39 | );
40 | /**
41 | * Initialize the local configuration
42 | */
43 | public function __construct(){
44 | $this->conf = self::$configure;
45 | }
46 |
47 | /**
48 | * Function called in the hook afterDraw
49 | * @param \ArrayAccess $context
50 | */
51 | public function afterDraw(\ArrayAccess $context) {
52 |
53 | // get the cache directory
54 | $this->cache_dir = $context->conf['cache_dir'];
55 |
56 | $html = $context->code;
57 | if( $this->conf['css']['status'] )
58 | $html = $this->compressCSS( $html );
59 |
60 | if( $this->conf['javascript']['status'] )
61 | $html = self::compressJavascript( $html );
62 |
63 | if( $this->conf['html']['status'] )
64 | $html = $this->compressHTML($html);
65 |
66 | // save the compressed code
67 | $context->code = $html;
68 | }
69 |
70 | /**
71 | * Compress the HTML
72 | * @param type $html
73 | * @return type
74 | */
75 | protected function compressHTML($html) {
76 |
77 | // Set PCRE recursion limit to sane value = STACKSIZE / 500
78 | // ini_set("pcre.recursion_limit", "524"); // 256KB stack. Win32 Apache
79 | ini_set("pcre.recursion_limit", "16777"); // 8MB stack. *nix
80 | $re = '%# Collapse whitespace everywhere but in blacklisted elements.
81 | (?> # Match all whitespans other than single space.
82 | [^\S ]\s* # Either one [\t\r\n\f\v] and zero or more ws,
83 | | \s{2,} # or two or more consecutive-any-whitespace.
84 | ) # Note: The remaining regex consumes no text at all...
85 | (?= # Ensure we are not in a blacklist tag.
86 | [^<]*+ # Either zero or more non-"<" {normal*}
87 | (?: # Begin {(special normal*)*} construct
88 | < # or a < starting a non-blacklist tag.
89 | (?!/?(?:textarea|pre|script)\b)
90 | [^<]*+ # more non-"<" {normal*}
91 | )*+ # Finish "unrolling-the-loop"
92 | (?: # Begin alternation group.
93 | < # Either a blacklist start tag.
94 | (?>textarea|pre|script)\b
95 | | \z # or end of file.
96 | ) # End alternation group.
97 | ) # If we made it here, we are not in a blacklist tag.
98 | %Six';
99 | $html = preg_replace($re, " ", $html);
100 | if ($html === null)
101 | exit("PCRE Error! File too big.\n");
102 | return $html;
103 | }
104 |
105 |
106 |
107 | /**
108 | * Compress the CSS
109 | * @param type $html
110 | * @return type
111 | */
112 | protected function compressCSS($html) {
113 |
114 | // search for all stylesheet
115 | if (!preg_match_all("//", $html, $matches))
116 | return $html; // return the HTML if doesn't find any
117 |
118 | // prepare the variables
119 | $externalUrl = array();
120 | $css = $cssName = "";
121 | $urlArray = array();
122 |
123 | $cssFiles = $matches[1];
124 | $md5Name = "";
125 | foreach( $cssFiles as $file ){
126 | $md5Name .= basename($file);
127 | }
128 |
129 | $cachedFilename = md5($md5Name);
130 | $cacheFolder = $this->cache_dir . "compress/css/"; // css cache folder
131 | $cachedFilepath = $cacheFolder . $cachedFilename . ".css";
132 |
133 | if( !file_exists($cachedFilepath) ){
134 |
135 | // read all the CSS found
136 | foreach ($cssFiles as $url) {
137 |
138 | // if a CSS is repeat it takes only the first
139 | if (empty($urlArray[$url])) {
140 |
141 | $urlArray[$url] = 1;
142 |
143 | // parse the URL
144 | $parse = parse_url($url);
145 |
146 | // read file
147 | $stylesheetFile = file_get_contents($url);
148 |
149 | // optimize image URL
150 | if (preg_match_all("/url\({0,1}(.*?)\)/", $stylesheetFile, $matches)) {
151 | foreach ($matches[1] as $imageUrl) {
152 | $imageUrl = preg_replace("/'|\"/", "", $imageUrl);
153 | dirname($url) . "/" . $imageUrl;
154 | $real_path = reduce_path("../../../" . dirname($url) . "/" . $imageUrl);
155 | $stylesheetFile = str_replace($imageUrl, $real_path, $stylesheetFile);
156 | }
157 | }
158 |
159 | // remove the comments
160 | $stylesheetFile = preg_replace("!/\*[^*]*\*+([^/][^*]*\*+)*/!", "", $stylesheetFile);
161 |
162 | // minify the CSS
163 | $stylesheetFile = preg_replace("/\n|\r|\t|\s{4}/", "", $stylesheetFile);
164 |
165 | $css .= "/*---\n CSS compressed in Rain \n {$url} \n---*/\n\n" . $stylesheetFile . "\n";
166 | }
167 | }
168 |
169 | if (!is_dir($cacheFolder))
170 | mkdir($cacheFolder, 0755, $recursive = true);
171 |
172 | // save the stylesheet
173 | file_put_contents($cachedFilepath, $css);
174 |
175 | }
176 |
177 | // remove all the old stylesheet from the page
178 | $html = preg_replace("//", "", $html);
179 |
180 | // create the tag for the stylesheet
181 | $tag = '';
182 |
183 | // add the tag to the end of the tag
184 | $html = str_replace("", $tag . "\n", $html);
185 |
186 | // return the stylesheet
187 | return $html;
188 | }
189 |
190 |
191 |
192 | /**
193 | * Compress the CSS
194 | * @param type $html
195 | * @return type
196 | */
197 | protected function compressJavascript($html) {
198 |
199 | $htmlToCheck = preg_replace("", "", $html);
200 |
201 | // search for javascript
202 | preg_match_all("//", $htmlToCheck, $matches);
203 | $externalUrl = array();
204 | $javascript = "";
205 |
206 | $javascriptFiles = $matches[1];
207 | $md5Name = "";
208 | foreach( $javascriptFiles as $file ){
209 | $md5Name .= basename($file);
210 | }
211 |
212 | $cachedFilename = md5($md5Name);
213 | $cacheFolder = $this->cache_dir . "compress/js/"; // css cache folder
214 | $cachedFilepath = $cacheFolder . $cachedFilename . ".js";
215 |
216 |
217 | if( !file_exists($cachedFilepath) ){
218 | foreach ($matches[1] as $url) {
219 |
220 | // if a JS is repeat it takes only the first
221 | if (empty($urlArray[$url])) {
222 | $urlArray[$url] = $url;
223 |
224 | // reduce the path
225 | $url = \Rain\Tpl\Parser::reducePath( $url );
226 |
227 | $javascriptFile = file_get_contents($url);
228 |
229 | // minify the js
230 | $javascriptFile = preg_replace("#/\*.*?\*/#", "", $javascriptFile);
231 | $javascriptFile = preg_replace("#\n+|\t+| +#", " ", $javascriptFile);
232 |
233 | $javascript .= "/*---\n Javascript compressed in Rain \n {$url} \n---*/\n\n" . $javascriptFile . "\n\n";
234 |
235 | }
236 | }
237 |
238 | if (!is_dir($cacheFolder))
239 | mkdir($cacheFolder, 0755, $recursive = true);
240 |
241 | // save the stylesheet
242 | file_put_contents($cachedFilepath, $javascript);
243 |
244 | }
245 |
246 | $html = preg_replace("//", "", $html);
247 | $tag = '';
248 |
249 | if( $this->conf['javascript']['position'] == 'bottom' ){
250 | $html = preg_replace("/<\/body>/", $tag . " ", $html); 251 | } 252 | else{ 253 | $html = preg_replace("/
/", "
\n".$tag, $html); 254 | } 255 | 256 | return $html; 257 | } 258 | 259 | public function configure( $setting, $value ){ 260 | $this->conf[$setting] = self::$configure[$setting] = $value; 261 | } 262 | 263 | public function configureLocal( $setting, $value ){ 264 | $this->conf[$setting] = $value; 265 | } 266 | 267 | } 268 | -------------------------------------------------------------------------------- /templates/bootstrap/lib/forms.less: -------------------------------------------------------------------------------- 1 | /* Forms.less 2 | * Base styles for various input types, form layouts, and states 3 | * ------------------------------------------------------------- */ 4 | 5 | 6 | // FORM STYLES 7 | // ----------- 8 | 9 | form { 10 | margin-bottom: @baseline; 11 | } 12 | 13 | // Groups of fields with labels on top (legends) 14 | fieldset { 15 | margin-bottom: @baseline; 16 | padding-top: @baseline; 17 | legend { 18 | display: block; 19 | padding-left: 150px; 20 | font-size: @basefont * 1.5; 21 | line-height: 1; 22 | color: @grayDark; 23 | *padding: 0 0 5px 145px; /* IE6-7 */ 24 | *line-height: 1.5; /* IE6-7 */ 25 | } 26 | } 27 | 28 | // Parent element that clears floats and wraps labels and fields together 29 | form .clearfix { 30 | margin-bottom: @baseline; 31 | .clearfix() 32 | } 33 | 34 | // Set font for forms 35 | label, 36 | input, 37 | select, 38 | textarea { 39 | #font > .sans-serif(normal,13px,normal); 40 | } 41 | 42 | // Float labels left 43 | label { 44 | padding-top: 6px; 45 | font-size: @basefont; 46 | line-height: @baseline; 47 | float: left; 48 | width: 130px; 49 | text-align: right; 50 | color: @grayDark; 51 | } 52 | 53 | // Shift over the inside div to align all label's relevant content 54 | form .input { 55 | margin-left: 150px; 56 | } 57 | 58 | // Checkboxs and radio buttons 59 | input[type=checkbox], 60 | input[type=radio] { 61 | cursor: pointer; 62 | } 63 | 64 | // Inputs, Textareas, Selects 65 | input, 66 | textarea, 67 | select, 68 | .uneditable-input { 69 | display: inline-block; 70 | width: 210px; 71 | height: @baseline; 72 | padding: 4px; 73 | font-size: @basefont; 74 | line-height: @baseline; 75 | color: @gray; 76 | border: 1px solid #ccc; 77 | .border-radius(3px); 78 | } 79 | 80 | // remove padding from select 81 | select { 82 | padding: initial; 83 | } 84 | 85 | // mini reset for non-html5 file types 86 | input[type=checkbox], 87 | input[type=radio] { 88 | width: auto; 89 | height: auto; 90 | padding: 0; 91 | margin: 3px 0; 92 | *margin-top: 0; /* IE6-7 */ 93 | line-height: normal; 94 | border: none; 95 | } 96 | 97 | input[type=file] { 98 | background-color: @white; 99 | padding: initial; 100 | border: initial; 101 | line-height: initial; 102 | .box-shadow(none); 103 | } 104 | 105 | input[type=button], 106 | input[type=reset], 107 | input[type=submit] { 108 | width: auto; 109 | height: auto; 110 | } 111 | 112 | select, 113 | input[type=file] { 114 | height: @baseline * 1.5; // In IE7, the height of the select element cannot be changed by height, only font-size 115 | *height: auto; // Reset for IE7 116 | line-height: @baseline * 1.5; 117 | *margin-top: 4px; /* For IE7, add top margin to align select with labels */ 118 | } 119 | 120 | // Make multiple select elements height not fixed 121 | select[multiple] { 122 | height: inherit; 123 | background-color: @white; // Fixes Chromium bug of unreadable items 124 | } 125 | 126 | textarea { 127 | height: auto; 128 | } 129 | 130 | // For text that needs to appear as an input but should not be an input 131 | .uneditable-input { 132 | background-color: @white; 133 | display: block; 134 | border-color: #eee; 135 | .box-shadow(inset 0 1px 2px rgba(0,0,0,.025)); 136 | cursor: not-allowed; 137 | } 138 | 139 | // Placeholder text gets special styles; can't be bundled together though for some reason 140 | :-moz-placeholder { 141 | color: @grayLight; 142 | } 143 | ::-webkit-input-placeholder { 144 | color: @grayLight; 145 | } 146 | 147 | // Focus states 148 | input, 149 | textarea { 150 | @transition: border linear .2s, box-shadow linear .2s; 151 | .transition(@transition); 152 | .box-shadow(inset 0 1px 3px rgba(0,0,0,.1)); 153 | } 154 | input:focus, 155 | textarea:focus { 156 | outline: 0; 157 | border-color: rgba(82,168,236,.8); 158 | @shadow: inset 0 1px 3px rgba(0,0,0,.1), 0 0 8px rgba(82,168,236,.6); 159 | .box-shadow(@shadow); 160 | } 161 | input[type=file]:focus, 162 | input[type=checkbox]:focus, 163 | select:focus { 164 | .box-shadow(none); // override for file inputs 165 | outline: 1px dotted #666; // Selet elements don't get box-shadow styles, so instead we do outline 166 | } 167 | 168 | 169 | // FORM FIELD FEEDBACK STATES 170 | // -------------------------- 171 | 172 | // Mixin for form field states 173 | .formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) { 174 | // Set the text color 175 | > label, 176 | .help-block, 177 | .help-inline { 178 | color: @textColor; 179 | } 180 | // Style inputs accordingly 181 | input, 182 | textarea { 183 | color: @textColor; 184 | border-color: @borderColor; 185 | &:focus { 186 | border-color: darken(@borderColor, 10%); 187 | .box-shadow(0 0 6px lighten(@borderColor, 20%)); 188 | } 189 | } 190 | // Give a small background color for input-prepend/-append 191 | .input-prepend .add-on, 192 | .input-append .add-on { 193 | color: @textColor; 194 | background-color: @backgroundColor; 195 | border-color: @textColor; 196 | } 197 | } 198 | // Error 199 | form .clearfix.error { 200 | .formFieldState(#b94a48, #ee5f5b, lighten(#ee5f5b, 30%)); 201 | } 202 | // Warning 203 | form .clearfix.warning { 204 | .formFieldState(#c09853, #ccae64, lighten(#CCAE64, 5%)); 205 | } 206 | // Success 207 | form .clearfix.success { 208 | .formFieldState(#468847, #57a957, lighten(#57a957, 30%)); 209 | } 210 | 211 | 212 | // Form element sizes 213 | // TODO v2: remove duplication here and just stick to .input-[size] in light of adding .spanN sizes 214 | .input-mini, 215 | input.mini, 216 | textarea.mini, 217 | select.mini { 218 | width: 60px; 219 | } 220 | .input-small, 221 | input.small, 222 | textarea.small, 223 | select.small { 224 | width: 90px; 225 | } 226 | .input-medium, 227 | input.medium, 228 | textarea.medium, 229 | select.medium { 230 | width: 150px; 231 | } 232 | .input-large, 233 | input.large, 234 | textarea.large, 235 | select.large { 236 | width: 210px; 237 | } 238 | .input-xlarge, 239 | input.xlarge, 240 | textarea.xlarge, 241 | select.xlarge { 242 | width: 270px; 243 | } 244 | .input-xxlarge, 245 | input.xxlarge, 246 | textarea.xxlarge, 247 | select.xxlarge { 248 | width: 530px; 249 | } 250 | textarea.xxlarge { 251 | overflow-y: auto; 252 | } 253 | 254 | // Grid style input sizes 255 | // This is a duplication of the main grid .columns() mixin, but subtracts 10px to account for input padding and border 256 | .formColumns(@columnSpan: 1) { 257 | display: inline-block; 258 | float: none; 259 | width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 10; 260 | margin-left: 0; 261 | } 262 | input, 263 | textarea { 264 | // Default columns 265 | &.span1 { .formColumns(1); } 266 | &.span2 { .formColumns(2); } 267 | &.span3 { .formColumns(3); } 268 | &.span4 { .formColumns(4); } 269 | &.span5 { .formColumns(5); } 270 | &.span6 { .formColumns(6); } 271 | &.span7 { .formColumns(7); } 272 | &.span8 { .formColumns(8); } 273 | &.span9 { .formColumns(9); } 274 | &.span10 { .formColumns(10); } 275 | &.span11 { .formColumns(11); } 276 | &.span12 { .formColumns(12); } 277 | &.span13 { .formColumns(13); } 278 | &.span14 { .formColumns(14); } 279 | &.span15 { .formColumns(15); } 280 | &.span16 { .formColumns(16); } 281 | } 282 | 283 | // Disabled and read-only inputs 284 | input[disabled], 285 | select[disabled], 286 | textarea[disabled], 287 | input[readonly], 288 | select[readonly], 289 | textarea[readonly] { 290 | background-color: #f5f5f5; 291 | border-color: #ddd; 292 | cursor: not-allowed; 293 | } 294 | 295 | // Actions (the buttons) 296 | .actions { 297 | background: #f5f5f5; 298 | margin-top: @baseline; 299 | margin-bottom: @baseline; 300 | padding: (@baseline - 1) 20px @baseline 150px; 301 | border-top: 1px solid #ddd; 302 | .border-radius(0 0 3px 3px); 303 | .secondary-action { 304 | float: right; 305 | a { 306 | line-height: 30px; 307 | &:hover { 308 | text-decoration: underline; 309 | } 310 | } 311 | } 312 | } 313 | 314 | // Help Text 315 | // TODO: Do we need to set basefont and baseline here? 316 | .help-inline, 317 | .help-block { 318 | font-size: @basefont; 319 | line-height: @baseline; 320 | color: @grayLight; 321 | } 322 | .help-inline { 323 | padding-left: 5px; 324 | *position: relative; /* IE6-7 */ 325 | *top: -5px; /* IE6-7 */ 326 | } 327 | 328 | // Big blocks of help text 329 | .help-block { 330 | display: block; 331 | max-width: 600px; 332 | } 333 | 334 | // Inline Fields (input fields that appear as inline objects 335 | .inline-inputs { 336 | color: @gray; 337 | span { 338 | padding: 0 2px 0 1px; 339 | } 340 | } 341 | 342 | // Allow us to put symbols and text within the input field for a cleaner look 343 | .input-prepend, 344 | .input-append { 345 | input { 346 | .border-radius(0 3px 3px 0); 347 | } 348 | .add-on { 349 | position: relative; 350 | background: #f5f5f5; 351 | border: 1px solid #ccc; 352 | z-index: 2; 353 | float: left; 354 | display: block; 355 | width: auto; 356 | min-width: 16px; 357 | height: 18px; 358 | padding: 4px 4px 4px 5px; 359 | margin-right: -1px; 360 | font-weight: normal; 361 | line-height: 18px; 362 | color: @grayLight; 363 | text-align: center; 364 | text-shadow: 0 1px 0 @white; 365 | .border-radius(3px 0 0 3px); 366 | } 367 | .active { 368 | background: lighten(@green, 30); 369 | border-color: @green; 370 | } 371 | } 372 | .input-prepend { 373 | .add-on { 374 | *margin-top: 1px; /* IE6-7 */ 375 | } 376 | } 377 | .input-append { 378 | input { 379 | float: left; 380 | .border-radius(3px 0 0 3px); 381 | } 382 | .add-on { 383 | .border-radius(0 3px 3px 0); 384 | margin-right: 0; 385 | margin-left: -1px; 386 | } 387 | } 388 | 389 | // Stacked options for forms (radio buttons or checkboxes) 390 | .inputs-list { 391 | margin: 0 0 5px; 392 | width: 100%; 393 | li { 394 | display: block; 395 | padding: 0; 396 | width: 100%; 397 | } 398 | label { 399 | display: block; 400 | float: none; 401 | width: auto; 402 | padding: 0; 403 | margin-left: 20px; 404 | line-height: @baseline; 405 | text-align: left; 406 | white-space: normal; 407 | strong { 408 | color: @gray; 409 | } 410 | small { 411 | font-size: @basefont - 2; 412 | font-weight: normal; 413 | } 414 | } 415 | .inputs-list { 416 | margin-left: 25px; 417 | margin-bottom: 10px; 418 | padding-top: 0; 419 | } 420 | &:first-child { 421 | padding-top: 6px; 422 | } 423 | li + li { 424 | padding-top: 2px; 425 | } 426 | input[type=radio], 427 | input[type=checkbox] { 428 | margin-bottom: 0; 429 | margin-left: -20px; 430 | float: left; 431 | } 432 | } 433 | 434 | // Stacked forms 435 | .form-stacked { 436 | padding-left: 20px; 437 | fieldset { 438 | padding-top: @baseline / 2; 439 | } 440 | legend { 441 | padding-left: 0; 442 | } 443 | label { 444 | display: block; 445 | float: none; 446 | width: auto; 447 | font-weight: bold; 448 | text-align: left; 449 | line-height: 20px; 450 | padding-top: 0; 451 | } 452 | .clearfix { 453 | margin-bottom: @baseline / 2; 454 | div.input { 455 | margin-left: 0; 456 | } 457 | } 458 | .inputs-list { 459 | margin-bottom: 0; 460 | li { 461 | padding-top: 0; 462 | label { 463 | font-weight: normal; 464 | padding-top: 0; 465 | } 466 | } 467 | } 468 | div.clearfix.error { 469 | padding-top: 10px; 470 | padding-bottom: 10px; 471 | padding-left: 10px; 472 | margin-top: 0; 473 | margin-left: -10px; 474 | } 475 | .actions { 476 | margin-left: -20px; 477 | padding-left: 20px; 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /library/Rain/Tpl.php: -------------------------------------------------------------------------------- 1 | array(), 53 | 'charset' => 'UTF-8', 54 | 'debug' => false, 55 | 'tpl_dir' => 'templates/', 56 | 'cache_dir' => 'cache/', 57 | 'tpl_ext' => 'html', 58 | 'base_url' => '', 59 | 'php_enabled' => false, 60 | 'auto_escape' => true, 61 | 'sandbox' => true, 62 | 'remove_comments' => false, 63 | 'registered_tags' => array(), 64 | ); 65 | 66 | // tags registered by the developers 67 | protected static $registered_tags = array(); 68 | 69 | 70 | /** 71 | * Draw the template 72 | * 73 | * @param string $templateFilePath: name of the template file 74 | * @param bool $toString: if the method should return a string 75 | * or echo the output 76 | * 77 | * @return void, string: depending of the $toString 78 | */ 79 | public function draw($templateFilePath, $toString = FALSE) { 80 | extract($this->var); 81 | // Merge local and static configurations 82 | $this->config = $this->objectConf + static::$conf; 83 | 84 | ob_start(); 85 | require $this->checkTemplate($templateFilePath); 86 | $html = ob_get_clean(); 87 | 88 | // Execute plugins, before_parse 89 | $context = $this->getPlugins()->createContext(array( 90 | 'code' => $html, 91 | 'conf' => $this->config, 92 | )); 93 | $this->getPlugins()->run('afterDraw', $context); 94 | $html = $context->code; 95 | 96 | if ($toString) 97 | return $html; 98 | else 99 | echo $html; 100 | } 101 | 102 | /** 103 | * Draw a string 104 | * 105 | * @param string $string: string in RainTpl format 106 | * @param bool $toString: if the param 107 | * 108 | * @return void, string: depending of the $toString 109 | */ 110 | public function drawString($string, $toString = false) { 111 | extract($this->var); 112 | // Merge local and static configurations 113 | $this->config = $this->objectConf + static::$conf; 114 | ob_start(); 115 | require $this->checkString($string); 116 | $html = ob_get_clean(); 117 | 118 | // Execute plugins, before_parse 119 | $context = $this->getPlugins()->createContext(array( 120 | 'code' => $html, 121 | 'conf' => $this->config, 122 | )); 123 | $this->getPlugins()->run('afterDraw', $context); 124 | $html = $context->code; 125 | 126 | if ($toString) 127 | return $html; 128 | else 129 | echo $html; 130 | } 131 | 132 | /** 133 | * Object specific configuration 134 | * 135 | * @param string, array $setting: name of the setting to configure 136 | * or associative array type 'setting' => 'value' 137 | * @param mixed $value: value of the setting to configure 138 | * @return \Rain\Tpl $this 139 | */ 140 | public function objectConfigure($setting, $value = null) { 141 | if (is_array($setting)) 142 | foreach ($setting as $key => $value) 143 | $this->objectConfigure($key, $value); 144 | else if (isset(static::$conf[$setting])) { 145 | 146 | // add ending slash if missing 147 | if ($setting == "tpl_dir" || $setting == "cache_dir") { 148 | $value = self::addTrailingSlash($value); 149 | } 150 | $this->objectConf[$setting] = $value; 151 | } 152 | 153 | return $this; 154 | } 155 | 156 | /** 157 | * Global configurations 158 | * 159 | * @param string, array $setting: name of the setting to configure 160 | * or associative array type 'setting' => 'value' 161 | * @param mixed $value: value of the setting to configure 162 | */ 163 | public static function configure($setting, $value = null) { 164 | if (is_array($setting)) 165 | foreach ($setting as $key => $value) 166 | static::configure($key, $value); 167 | else if (isset(static::$conf[$setting])) { 168 | 169 | // add ending slash if missing 170 | if ($setting == "tpl_dir" || $setting == "cache_dir") { 171 | $value = self::addTrailingSlash($value); 172 | } 173 | 174 | static::$conf[$setting] = $value; 175 | static::$conf['checksum'][$setting] = $value; // take trace of all config 176 | } 177 | } 178 | 179 | /** 180 | * Assign variable 181 | * eg. $t->assign('name','mickey'); 182 | * 183 | * @param mixed $variable Name of template variable or associative array name/value 184 | * @param mixed $value value assigned to this variable. Not set if variable_name is an associative array 185 | * 186 | * @return \Rain\Tpl $this 187 | */ 188 | public function assign($variable, $value = null) { 189 | if (is_array($variable)) 190 | $this->var = $variable + $this->var; 191 | else 192 | $this->var[$variable] = $value; 193 | 194 | return $this; 195 | } 196 | 197 | /** 198 | * Clean the expired files from cache 199 | * @param type $expireTime Set the expiration time 200 | */ 201 | public static function clean($expireTime = 2592000) { 202 | $files = glob(static::$conf['cache_dir'] . "*.rtpl.php"); 203 | $time = time() - $expireTime; 204 | foreach ($files as $file) 205 | if ($time > filemtime($file) ) 206 | unlink($file); 207 | } 208 | 209 | /** 210 | * Allows the developer to register a tag. 211 | * 212 | * @param string $tag nombre del tag 213 | * @param regexp $parse regular expression to parse the tag 214 | * @param anonymous function $function: action to do when the tag is parsed 215 | */ 216 | public static function registerTag($tag, $parse, $function) { 217 | static::$registered_tags[$tag] = array("parse" => $parse, "function" => $function); 218 | } 219 | 220 | /** 221 | * Registers a plugin globally. 222 | * 223 | * @param \Rain\Tpl\IPlugin $plugin 224 | * @param string $name name can be used to distinguish plugins of same class. 225 | */ 226 | public static function registerPlugin(Tpl\IPlugin $plugin, $name = '') { 227 | $name = (string)$name ?: \get_class($plugin); 228 | 229 | static::getPlugins()->addPlugin($name, $plugin); 230 | } 231 | 232 | /** 233 | * Removes registered plugin from stack. 234 | * 235 | * @param string $name 236 | */ 237 | public static function removePlugin($name) { 238 | static::getPlugins()->removePlugin($name); 239 | } 240 | 241 | /** 242 | * Returns plugin container. 243 | * 244 | * @return \Rain\Tpl\PluginContainer 245 | */ 246 | protected static function getPlugins() { 247 | return static::$plugins 248 | ?: static::$plugins = new Tpl\PluginContainer(); 249 | } 250 | 251 | /** 252 | * Check if the template exist and compile it if necessary 253 | * 254 | * @param string $template: name of the file of the template 255 | * 256 | * @throw \Rain\Tpl\NotFoundException the file doesn't exists 257 | * @return string: full filepath that php must use to include 258 | */ 259 | protected function checkTemplate($template) { 260 | 261 | // set filename 262 | $templateName = basename($template); 263 | $templateBasedir = strpos($template, '/') !== false ? dirname($template) . '/' : null; 264 | $templateDirectory = null; 265 | $templateFilepath = null; 266 | $parsedTemplateFilepath = null; 267 | 268 | // Make directories to array for multiple template directory 269 | $templateDirectories = $this->config['tpl_dir']; 270 | if (!is_array($templateDirectories)) { 271 | $templateDirectories = array($templateDirectories); 272 | } 273 | 274 | $isFileNotExist = true; 275 | 276 | // absolute path 277 | if ($template[0] == '/') { 278 | $templateDirectory = $templateBasedir; 279 | $templateFilepath = $templateDirectory . $templateName . '.' . $this->config['tpl_ext']; 280 | $parsedTemplateFilepath = $this->config['cache_dir'] . $templateName . "." . md5($templateDirectory . serialize($this->config['checksum'])) . '.rtpl.php'; 281 | // For check templates are exists 282 | if (file_exists($templateFilepath)) { 283 | $isFileNotExist = false; 284 | } 285 | } else { 286 | foreach($templateDirectories as $templateDirectory) { 287 | $templateDirectory .= $templateBasedir; 288 | $templateFilepath = $templateDirectory . $templateName . '.' . $this->config['tpl_ext']; 289 | $parsedTemplateFilepath = $this->config['cache_dir'] . $templateName . "." . md5($templateDirectory . serialize($this->config['checksum'])) . '.rtpl.php'; 290 | 291 | // For check templates are exists 292 | if (file_exists($templateFilepath)) { 293 | $isFileNotExist = false; 294 | break; 295 | } 296 | } 297 | } 298 | 299 | // if the template doesn't exsist throw an error 300 | if ($isFileNotExist === true) { 301 | $e = new Tpl\NotFoundException('Template ' . $templateName . ' not found!'); 302 | throw $e->templateFile($templateFilepath); 303 | } 304 | 305 | // Compile the template if the original has been updated 306 | if ($this->config['debug'] || !file_exists($parsedTemplateFilepath) || ( filemtime($parsedTemplateFilepath) < filemtime($templateFilepath) )) { 307 | $parser = new Tpl\Parser($this->config, static::$plugins, static::$registered_tags); 308 | $parser->compileFile($templateName, $templateBasedir, $templateDirectory, $templateFilepath, $parsedTemplateFilepath); 309 | } 310 | return $parsedTemplateFilepath; 311 | } 312 | 313 | /** 314 | * Compile a string if necessary 315 | * 316 | * @param string $string: RainTpl template string to compile 317 | * 318 | * @return string: full filepath that php must use to include 319 | */ 320 | protected function checkString($string) { 321 | 322 | // set filename 323 | $templateName = md5($string . implode($this->config['checksum'])); 324 | $parsedTemplateFilepath = $this->config['cache_dir'] . $templateName . '.s.rtpl.php'; 325 | $templateFilepath = ''; 326 | $templateBasedir = ''; 327 | 328 | 329 | // Compile the template if the original has been updated 330 | if ($this->config['debug'] || !file_exists($parsedTemplateFilepath)) { 331 | $parser = new Tpl\Parser($this->config, static::$plugins, static::$registered_tags); 332 | $parser->compileString($templateName, $templateBasedir, $templateFilepath, $parsedTemplateFilepath, $string); 333 | } 334 | 335 | return $parsedTemplateFilepath; 336 | } 337 | 338 | private static function addTrailingSlash($folder) { 339 | 340 | if (is_array($folder)) { 341 | foreach($folder as &$f) { 342 | $f = self::addTrailingSlash($f); 343 | } 344 | } elseif ( strlen($folder) > 0 && $folder[0] != '/' ) { 345 | $folder = $folder . "/"; 346 | } 347 | return $folder; 348 | 349 | } 350 | 351 | } 352 | --------------------------------------------------------------------------------