00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 class JavascriptHelper extends AppHelper {
00034
00035
00036
00037
00038
00039
00040 var $useNative = false;
00041
00042
00043
00044
00045
00046
00047
00048 var $enabled = true;
00049
00050
00051
00052
00053
00054
00055 var $safe = false;
00056
00057
00058
00059
00060
00061
00062 var $tags = array(
00063 'javascriptstart' => '<script type="text/javascript">',
00064 'javascriptend' => '</script>',
00065 'javascriptblock' => '<script type="text/javascript">%s</script>',
00066 'javascriptlink' => '<script type="text/javascript" src="%s"></script>'
00067 );
00068
00069
00070
00071
00072
00073
00074
00075 var $_blockOptions = array();
00076
00077
00078
00079
00080
00081
00082
00083 var $_cachedEvents = array();
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093 var $_cacheEvents = false;
00094
00095
00096
00097
00098
00099
00100
00101
00102 var $_cacheToFile = false;
00103
00104
00105
00106
00107
00108
00109
00110
00111 var $_cacheAll = false;
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 var $_rules = array();
00122
00123
00124
00125
00126 var $__scriptBuffer = null;
00127
00128
00129
00130
00131
00132 function __construct($options = array()) {
00133 if (!empty($options)) {
00134 foreach ($options as $key => $val) {
00135 if (is_numeric($key)) {
00136 $key = $val;
00137 $val = true;
00138 }
00139 switch ($key) {
00140 case 'cache':
00141
00142 break;
00143 case 'safe':
00144 $this->safe = $val;
00145 break;
00146 }
00147 }
00148 }
00149 $this->useNative = function_exists('json_encode');
00150 return parent::__construct($options);
00151 }
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 function codeBlock($script = null, $options = array()) {
00170 if (!empty($options) && !is_array($options)) {
00171 $options = array('allowCache' => $options);
00172 } elseif (empty($options)) {
00173 $options = array();
00174 }
00175 $defaultOptions = array('allowCache' => true, 'safe' => true, 'inline' => true);
00176 $options = array_merge($defaultOptions, $options);
00177
00178 if (empty($script)) {
00179 $this->__scriptBuffer = @ob_get_contents();
00180 $this->_blockOptions = $options;
00181 $this->inBlock = true;
00182 @ob_end_clean();
00183 ob_start();
00184 return null;
00185 }
00186 if ($this->_cacheEvents && $this->_cacheAll && $options['allowCache']) {
00187 $this->_cachedEvents[] = $script;
00188 return null;
00189 }
00190 if ($options['safe'] || $this->safe) {
00191 $script = "\n" . '
00192 }
00193 if ($options['inline']) {
00194 return sprintf($this->tags['javascriptblock'], $script);
00195 } else {
00196 $view =& ClassRegistry::getObject('view');
00197 $view->addScript(sprintf($this->tags['javascriptblock'], $script));
00198 }
00199 }
00200
00201
00202
00203
00204
00205 function blockEnd() {
00206 if (!isset($this->inBlock) || !$this->inBlock) {
00207 return;
00208 }
00209 $script = @ob_get_contents();
00210 @ob_end_clean();
00211 ob_start();
00212 echo $this->__scriptBuffer;
00213 $this->__scriptBuffer = null;
00214 $options = $this->_blockOptions;
00215 $this->_blockOptions = array();
00216 $this->inBlock = false;
00217
00218 if (empty($script)) {
00219 return null;
00220 }
00221
00222 return $this->codeBlock($script, $options);
00223 }
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235 function link($url, $inline = true) {
00236 if (is_array($url)) {
00237 $out = '';
00238 foreach ($url as $i) {
00239 $out .= "\n\t" . $this->link($i, $inline);
00240 }
00241 if ($inline) {
00242 return $out . "\n";
00243 }
00244 return;
00245 }
00246
00247 if (strpos($url, ':
00248 if ($url[0] !== '/') {
00249 $url = JS_URL . $url;
00250 }
00251 if (strpos($url, '?') === false) {
00252 if (strpos($url, '.js') === false) {
00253 $url .= '.js';
00254 }
00255 }
00256
00257 $url = $this->webroot($url);
00258 $timestampEnabled = (
00259 (Configure::read('Asset.timestamp') === true && Configure::read() > 0) ||
00260 Configure::read('Asset.timestamp') === 'force'
00261 );
00262
00263 if (strpos($url, '?') === false && $timestampEnabled) {
00264 $url .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $url));
00265 }
00266
00267 if (Configure::read('Asset.filter.js')) {
00268 $url = str_replace(JS_URL, 'cjs/', $url);
00269 }
00270 }
00271 $out = $this->output(sprintf($this->tags['javascriptlink'], $url));
00272
00273 if ($inline) {
00274 return $out;
00275 } else {
00276 $view =& ClassRegistry::getObject('view');
00277 $view->addScript($out);
00278 }
00279 }
00280
00281
00282
00283
00284
00285
00286 function escapeScript($script) {
00287 $script = str_replace(array("\r\n", "\n", "\r"), '\n', $script);
00288 $script = str_replace(array('"', "'"), array('\"', "\\'"), $script);
00289 return $script;
00290 }
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 function escapeString($string) {
00305 App::import('Core', 'Multibyte');
00306 $escape = array("\r\n" => "\n", "\r" => "\n");
00307 $string = str_replace(array_keys($escape), array_values($escape), $string);
00308 return $this->_utf8ToHex($string);
00309 }
00310
00311
00312
00313
00314
00315 function _utf8ToHex($string) {
00316 $length = strlen($string);
00317 $return = '';
00318 for ($i = 0; $i < $length; ++$i) {
00319 $ord = ord($string{$i});
00320 switch (true) {
00321 case $ord == 0x08:
00322 $return .= '\b';
00323 break;
00324 case $ord == 0x09:
00325 $return .= '\t';
00326 break;
00327 case $ord == 0x0A:
00328 $return .= '\n';
00329 break;
00330 case $ord == 0x0C:
00331 $return .= '\f';
00332 break;
00333 case $ord == 0x0D:
00334 $return .= '\r';
00335 break;
00336 case $ord == 0x22:
00337 case $ord == 0x2F:
00338 case $ord == 0x5C:
00339 case $ord == 0x27:
00340 $return .= '\\' . $string{$i};
00341 break;
00342 case (($ord >= 0x20) && ($ord <= 0x7F)):
00343 $return .= $string{$i};
00344 break;
00345 case (($ord & 0xE0) == 0xC0):
00346 if ($i + 1 >= $length) {
00347 $i += 1;
00348 $return .= '?';
00349 break;
00350 }
00351 $charbits = $string{$i} . $string{$i + 1};
00352 $char = Multibyte::utf8($charbits);
00353 $return .= sprintf('\u%04s', dechex($char[0]));
00354 $i += 1;
00355 break;
00356 case (($ord & 0xF0) == 0xE0):
00357 if ($i + 2 >= $length) {
00358 $i += 2;
00359 $return .= '?';
00360 break;
00361 }
00362 $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2};
00363 $char = Multibyte::utf8($charbits);
00364 $return .= sprintf('\u%04s', dechex($char[0]));
00365 $i += 2;
00366 break;
00367 case (($ord & 0xF8) == 0xF0):
00368 if ($i + 3 >= $length) {
00369 $i += 3;
00370 $return .= '?';
00371 break;
00372 }
00373 $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3};
00374 $char = Multibyte::utf8($charbits);
00375 $return .= sprintf('\u%04s', dechex($char[0]));
00376 $i += 3;
00377 break;
00378 case (($ord & 0xFC) == 0xF8):
00379 if ($i + 4 >= $length) {
00380 $i += 4;
00381 $return .= '?';
00382 break;
00383 }
00384 $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4};
00385 $char = Multibyte::utf8($charbits);
00386 $return .= sprintf('\u%04s', dechex($char[0]));
00387 $i += 4;
00388 break;
00389 case (($ord & 0xFE) == 0xFC):
00390 if ($i + 5 >= $length) {
00391 $i += 5;
00392 $return .= '?';
00393 break;
00394 }
00395 $charbits = $string{$i} . $string{$i + 1} . $string{$i + 2} . $string{$i + 3} . $string{$i + 4} . $string{$i + 5};
00396 $char = Multibyte::utf8($charbits);
00397 $return .= sprintf('\u%04s', dechex($char[0]));
00398 $i += 5;
00399 break;
00400 }
00401 }
00402 return $return;
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413 function event($object, $event, $observer = null, $options = array()) {
00414 if (!empty($options) && !is_array($options)) {
00415 $options = array('useCapture' => $options);
00416 } else if (empty($options)) {
00417 $options = array();
00418 }
00419
00420 $defaultOptions = array('useCapture' => false);
00421 $options = array_merge($defaultOptions, $options);
00422
00423 if ($options['useCapture'] == true) {
00424 $options['useCapture'] = 'true';
00425 } else {
00426 $options['useCapture'] = 'false';
00427 }
00428 $isObject = (
00429 strpos($object, 'window') !== false || strpos($object, 'document') !== false ||
00430 strpos($object, '$(') !== false || strpos($object, '"') !== false ||
00431 strpos($object, '\'') !== false
00432 );
00433
00434 if ($isObject) {
00435 $b = "Event.observe({$object}, '{$event}', function(event) { {$observer} }, ";
00436 $b .= "{$options['useCapture']});";
00437 } elseif ($object[0] == '\'') {
00438 $b = "Event.observe(" . substr($object, 1) . ", '{$event}', function(event) { ";
00439 $b .= "{$observer} }, {$options['useCapture']});";
00440 } else {
00441 $chars = array('#', ' ', ', ', '.', ':');
00442 $found = false;
00443 foreach ($chars as $char) {
00444 if (strpos($object, $char) !== false) {
00445 $found = true;
00446 break;
00447 }
00448 }
00449 if ($found) {
00450 $this->_rules[$object] = $event;
00451 } else {
00452 $b = "Event.observe(\$('{$object}'), '{$event}', function(event) { ";
00453 $b .= "{$observer} }, {$options['useCapture']});";
00454 }
00455 }
00456
00457 if (isset($b) && !empty($b)) {
00458 if ($this->_cacheEvents === true) {
00459 $this->_cachedEvents[] = $b;
00460 return;
00461 } else {
00462 return $this->codeBlock($b, array_diff_key($options, $defaultOptions));
00463 }
00464 }
00465 }
00466
00467
00468
00469
00470
00471
00472
00473 function cacheEvents($file = false, $all = false) {
00474 $this->_cacheEvents = true;
00475 $this->_cacheToFile = $file;
00476 $this->_cacheAll = $all;
00477 }
00478
00479
00480
00481
00482
00483
00484 function getCache($clear = true) {
00485 $out = '';
00486 $rules = array();
00487
00488 if (!empty($this->_rules)) {
00489 foreach ($this->_rules as $sel => $event) {
00490 $rules[] = "\t'{$sel}': function(element, event) {\n\t\t{$event}\n\t}";
00491 }
00492 }
00493 $data = implode("\n", $this->_cachedEvents);
00494
00495 if (!empty($rules)) {
00496 $data .= "\nvar Rules = {\n" . implode(",\n\n", $rules) . "\n}";
00497 $data .= "\nEventSelectors.start(Rules);\n";
00498 }
00499 if ($clear) {
00500 $this->_rules = array();
00501 $this->_cacheEvents = false;
00502 $this->_cachedEvents = array();
00503 }
00504 return $data;
00505 }
00506
00507
00508
00509
00510
00511
00512
00513
00514 function writeEvents($inline = true, $options = array()) {
00515 $out = '';
00516 $rules = array();
00517
00518 if (!$this->_cacheEvents) {
00519 return;
00520 }
00521 $data = $this->getCache();
00522
00523 if (empty($data)) {
00524 return;
00525 }
00526
00527 if ($this->_cacheToFile) {
00528 $filename = md5($data);
00529 if (!file_exists(JS . $filename . '.js')) {
00530 cache(str_replace(WWW_ROOT, '', JS) . $filename . '.js', $data, '+999 days', 'public');
00531 }
00532 $out = $this->link($filename);
00533 } else {
00534 $out = $this->codeBlock("\n" . $data . "\n", $options);
00535 }
00536
00537 if ($inline) {
00538 return $out;
00539 } else {
00540 $view =& ClassRegistry::getObject('view');
00541 $view->addScript($out);
00542 }
00543 }
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556 function includeScript($script = "", $options = array()) {
00557 if ($script == "") {
00558 $files = scandir(JS);
00559 $javascript = '';
00560
00561 foreach ($files as $file) {
00562 if (substr($file, -3) == '.js') {
00563 $javascript .= file_get_contents(JS . "{$file}") . "\n\n";
00564 }
00565 }
00566 } else {
00567 $javascript = file_get_contents(JS . "$script.js") . "\n\n";
00568 }
00569 return $this->codeBlock("\n\n" . $javascript, $options);
00570 }
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593 function object($data = array(), $options = array(), $prefix = null, $postfix = null, $stringKeys = null, $quoteKeys = null, $q = null) {
00594 if (!empty($options) && !is_array($options)) {
00595 $options = array('block' => $options);
00596 } else if (empty($options)) {
00597 $options = array();
00598 }
00599
00600 $defaultOptions = array(
00601 'block' => false, 'prefix' => '', 'postfix' => '',
00602 'stringKeys' => array(), 'quoteKeys' => true, 'q' => '"'
00603 );
00604 $options = array_merge($defaultOptions, $options, array_filter(compact(array_keys($defaultOptions))));
00605
00606 if (is_object($data)) {
00607 $data = get_object_vars($data);
00608 }
00609
00610 $out = $keys = array();
00611 $numeric = true;
00612
00613 if ($this->useNative) {
00614 $rt = json_encode($data);
00615 } else {
00616 if (is_null($data)) {
00617 return 'null';
00618 }
00619 if (is_bool($data)) {
00620 return $data ? 'true' : 'false';
00621 }
00622
00623 if (is_array($data)) {
00624 $keys = array_keys($data);
00625 }
00626
00627 if (!empty($keys)) {
00628 $numeric = (array_values($keys) === array_keys(array_values($keys)));
00629 }
00630
00631 foreach ($data as $key => $val) {
00632 if (is_array($val) || is_object($val)) {
00633 $val = $this->object($val, array_merge($options, array('block' => false)));
00634 } else {
00635 $quoteStrings = (
00636 !count($options['stringKeys']) ||
00637 ($options['quoteKeys'] && in_array($key, $options['stringKeys'], true)) ||
00638 (!$options['quoteKeys'] && !in_array($key, $options['stringKeys'], true))
00639 );
00640 $val = $this->value($val, $quoteStrings);
00641 }
00642 if (!$numeric) {
00643 $val = $options['q'] . $this->value($key, false) . $options['q'] . ':' . $val;
00644 }
00645 $out[] = $val;
00646 }
00647
00648 if (!$numeric) {
00649 $rt = '{' . join(',', $out) . '}';
00650 } else {
00651 $rt = '[' . join(',', $out) . ']';
00652 }
00653 }
00654 $rt = $options['prefix'] . $rt . $options['postfix'];
00655
00656 if ($options['block']) {
00657 $rt = $this->codeBlock($rt, array_diff_key($options, $defaultOptions));
00658 }
00659
00660 return $rt;
00661 }
00662
00663
00664
00665
00666
00667
00668
00669 function value($val, $quoteStrings = true) {
00670 switch (true) {
00671 case (is_array($val) || is_object($val)):
00672 $val = $this->object($val);
00673 break;
00674 case ($val === null):
00675 $val = 'null';
00676 break;
00677 case (is_bool($val)):
00678 $val = ife($val, 'true', 'false');
00679 break;
00680 case (is_int($val)):
00681 $val = $val;
00682 break;
00683 case (is_float($val)):
00684 $val = sprintf("%.11f", $val);
00685 break;
00686 default:
00687 $val = $this->escapeString($val);
00688 if ($quoteStrings) {
00689 $val = '"' . $val . '"';
00690 }
00691 break;
00692 }
00693 return $val;
00694 }
00695
00696
00697
00698
00699
00700 function afterRender() {
00701 if (!$this->enabled) {
00702 return;
00703 }
00704 echo $this->writeEvents(true);
00705 }
00706 }
00707
00708 ?>