diff --git a/mod/hovercard.php b/mod/hovercard.php deleted file mode 100644 index d5951dbe00..0000000000 --- a/mod/hovercard.php +++ /dev/null @@ -1,151 +0,0 @@ - - * License: GNU AFFERO GENERAL PUBLIC LICENSE (Version 3) - */ - -use Friendica\App; -use Friendica\Core\Config; -use Friendica\Core\Renderer; -use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\Model\Contact; -use Friendica\Model\GContact; -use Friendica\Util\Proxy as ProxyUtils; -use Friendica\Util\Strings; - -function hovercard_init(App $a) -{ - // Just for testing purposes - $_GET['mode'] = 'minimal'; -} - -function hovercard_content() -{ - $profileurl = $_REQUEST['profileurl'] ?? ''; - $datatype = ($_REQUEST['datatype'] ?? '') ?: 'json'; - - // Get out if the system doesn't have public access allowed - if (intval(Config::get('system', 'block_public'))) { - throw new \Friendica\Network\HTTPException\ForbiddenException(); - } - - // Return the raw content of the template. We use this to make templates usable for js functions. - // Look at hovercard.js (function getHoverCardTemplate()). - // This part should be moved in its own module. Maybe we could make more templates accessible. - // (We need to discuss possible security leaks before doing this) - if ($datatype == 'tpl') { - $templatecontent = get_template_content('hovercard.tpl'); - echo $templatecontent; - exit(); - } - - // If a contact is connected the url is internally changed to 'redir/CID'. We need the pure url to search for - // the contact. So we strip out the contact id from the internal url and look in the contact table for - // the real url (nurl) - if (strpos($profileurl, 'redir/') === 0) { - $cid = intval(substr($profileurl, 6)); - $remote_contact = DBA::selectFirst('contact', ['nurl'], ['id' => $cid]); - $profileurl = $remote_contact['nurl'] ?? ''; - } - - $contact = []; - // if it's the url containing https it should be converted to http - $nurl = Strings::normaliseLink(GContact::cleanContactUrl($profileurl)); - if (!$nurl) { - return; - } - - // Search for contact data - // Look if the local user has got the contact - if (local_user()) { - $contact = Contact::getDetailsByURL($nurl, local_user()); - } - - // If not then check the global user - if (!count($contact)) { - $contact = Contact::getDetailsByURL($nurl); - } - - // Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url - if (!count($contact) && local_user()) { - $nurl = Strings::normaliseLink($profileurl); - $contact = Contact::getDetailsByURL($nurl, local_user()); - } - - if (!count($contact)) { - $nurl = Strings::normaliseLink($profileurl); - $contact = Contact::getDetailsByURL($nurl); - } - - if (!count($contact)) { - return; - } - - // Get the photo_menu - the menu if possible contact actions - if (local_user()) { - $actions = Contact::photoMenu($contact); - } else { - $actions = []; - } - - // Move the contact data to the profile array so we can deliver it to - $profile = [ - 'name' => $contact['name'], - 'nick' => $contact['nick'], - 'addr' => ($contact['addr'] ?? '') ?: $contact['url'], - 'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB), - 'url' => Contact::magicLink($contact['url']), - 'nurl' => $contact['nurl'], // We additionally store the nurl as identifier - 'location' => $contact['location'], - 'gender' => $contact['gender'], - 'about' => $contact['about'], - 'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']), - 'tags' => $contact['keywords'], - 'bd' => $contact['birthday'] <= DBA::NULL_DATE ? '' : $contact['birthday'], - 'account_type' => Contact::getAccountType($contact), - 'actions' => $actions, - ]; - if ($datatype == 'html') { - $tpl = Renderer::getMarkupTemplate('hovercard.tpl'); - $o = Renderer::replaceMacros($tpl, [ - '$profile' => $profile, - ]); - - return $o; - } else { - System::jsonExit($profile); - } -} - -/** - * @brief Get the raw content of a template file - * - * @param string $template The name of the template - * @param string $root Directory of the template - * - * @return string|bool Output the raw content if existent, otherwise false - * @throws Exception - */ -function get_template_content($template, $root = '') -{ - // We load the whole template system to get the filename. - // Maybe we can do it a little bit smarter if I get time. - $templateEngine = Renderer::getTemplateEngine(); - $template = $templateEngine->getTemplateFile($template, $root); - - $filename = $template->filename; - - // Get the content of the template file - if (file_exists($filename)) { - $content = file_get_contents($filename); - - return $content; - } - - return false; -} diff --git a/src/Model/Contact.php b/src/Model/Contact.php index a57c7b96ef..2742e49dfa 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -983,41 +983,43 @@ class Contact extends BaseObject $ssl_url = str_replace('http://', 'https://', $url); + $nurl = Strings::normaliseLink($url); + // Fetch contact data from the contact table for the given user $s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self` - FROM `contact` WHERE `nurl` = ? AND `uid` = ?", Strings::normaliseLink($url), $uid); + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending` + FROM `contact` WHERE `nurl` = ? AND `uid` = ?", $nurl, $uid); $r = DBA::toArray($s); // Fetch contact data from the contact table for the given user, checking with the alias if (!DBA::isResult($r)) { $s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self` - FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", Strings::normaliseLink($url), $url, $ssl_url, $uid); + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending` + FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", $nurl, $url, $ssl_url, $uid); $r = DBA::toArray($s); } // Fetch the data from the contact table with "uid=0" (which is filled automatically) if (!DBA::isResult($r)) { $s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self` - FROM `contact` WHERE `nurl` = ? AND `uid` = 0", Strings::normaliseLink($url)); + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending` + FROM `contact` WHERE `nurl` = ? AND `uid` = 0", $nurl); $r = DBA::toArray($s); } // Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias if (!DBA::isResult($r)) { $s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self` - FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", Strings::normaliseLink($url), $url, $ssl_url); + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending` + FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", $nurl, $url, $ssl_url); $r = DBA::toArray($s); } // Fetch the data from the gcontact table if (!DBA::isResult($r)) { $s = DBA::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`, - `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self` - FROM `gcontact` WHERE `nurl` = ?", Strings::normaliseLink($url)); + `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending` + FROM `gcontact` WHERE `nurl` = ?", $nurl); $r = DBA::toArray($s); } @@ -1121,7 +1123,7 @@ class Contact extends BaseObject // Fetch contact data from the contact table for the given user $r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self` + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending` FROM `contact` WHERE `addr` = '%s' AND `uid` = %d AND NOT `deleted`", DBA::escape($addr), intval($uid) @@ -1129,7 +1131,7 @@ class Contact extends BaseObject // Fetch the data from the contact table with "uid=0" (which is filled automatically) if (!DBA::isResult($r)) { $r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`, - `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self` + `keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending` FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 AND NOT `deleted`", DBA::escape($addr) ); @@ -1138,7 +1140,7 @@ class Contact extends BaseObject // Fetch the data from the gcontact table if (!DBA::isResult($r)) { $r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`, - `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self` + `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending` FROM `gcontact` WHERE `addr` = '%s'", DBA::escape($addr) ); @@ -1225,28 +1227,40 @@ class Contact extends BaseObject $contact_drop_link = System::baseUrl() . '/contact/' . $contact['id'] . '/drop?confirm=1'; } + $follow_link = ''; + $unfollow_link = ''; + if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) { + if ($contact['uid'] && in_array($contact['rel'], [self::SHARING, self::FRIEND])) { + $unfollow_link = 'unfollow?url=' . urlencode($contact['url']); + } elseif(!$contact['pending']) { + $follow_link = 'follow?url=' . urlencode($contact['url']); + } + } + /** * Menu array: * "name" => [ "Label", "link", (bool)Should the link opened in a new tab? ] */ if (empty($contact['uid'])) { - $connlnk = 'follow/?url=' . $contact['url']; $menu = [ - 'profile' => [L10n::t('View Profile'), $profile_link, true], - 'network' => [L10n::t('Network Posts'), $posts_link, false], - 'edit' => [L10n::t('View Contact'), $contact_url, false], - 'follow' => [L10n::t('Connect/Follow'), $connlnk, true], + 'profile' => [L10n::t('View Profile') , $profile_link , true], + 'network' => [L10n::t('Network Posts') , $posts_link , false], + 'edit' => [L10n::t('View Contact') , $contact_url , false], + 'follow' => [L10n::t('Connect/Follow'), $follow_link , true], + 'unfollow'=> [L10n::t('UnFollow') , $unfollow_link, true], ]; } else { $menu = [ - 'status' => [L10n::t('View Status'), $status_link, true], - 'profile' => [L10n::t('View Profile'), $profile_link, true], - 'photos' => [L10n::t('View Photos'), $photos_link, true], - 'network' => [L10n::t('Network Posts'), $posts_link, false], - 'edit' => [L10n::t('View Contact'), $contact_url, false], - 'drop' => [L10n::t('Drop Contact'), $contact_drop_link, false], - 'pm' => [L10n::t('Send PM'), $pm_url, false], - 'poke' => [L10n::t('Poke'), $poke_link, false], + 'status' => [L10n::t('View Status') , $status_link , true], + 'profile' => [L10n::t('View Profile') , $profile_link , true], + 'photos' => [L10n::t('View Photos') , $photos_link , true], + 'network' => [L10n::t('Network Posts') , $posts_link , false], + 'edit' => [L10n::t('View Contact') , $contact_url , false], + 'drop' => [L10n::t('Drop Contact') , $contact_drop_link, false], + 'pm' => [L10n::t('Send PM') , $pm_url , false], + 'poke' => [L10n::t('Poke') , $poke_link , false], + 'follow' => [L10n::t('Connect/Follow'), $follow_link , true], + 'unfollow'=> [L10n::t('UnFollow') , $unfollow_link , true], ]; if (!empty($contact['pending'])) { diff --git a/src/Module/Contact/Hovercard.php b/src/Module/Contact/Hovercard.php new file mode 100644 index 0000000000..20290e0aca --- /dev/null +++ b/src/Module/Contact/Hovercard.php @@ -0,0 +1,104 @@ + $cid]); + $contact_url = $remote_contact['nurl'] ?? ''; + } + + $contact = []; + + // if it's the url containing https it should be converted to http + $contact_nurl = Strings::normaliseLink(GContact::cleanContactUrl($contact_url)); + if (!$contact_nurl) { + throw new HTTPException\BadRequestException(); + } + + // Search for contact data + // Look if the local user has got the contact + if (Session::isAuthenticated()) { + $contact = Contact::getDetailsByURL($contact_nurl, local_user()); + } + + // If not then check the global user + if (!count($contact)) { + $contact = Contact::getDetailsByURL($contact_nurl); + } + + // Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url + if (!count($contact) && Session::isAuthenticated()) { + $contact_nurl = Strings::normaliseLink($contact_url); + $contact = Contact::getDetailsByURL($contact_nurl, local_user()); + } + + if (!count($contact)) { + $contact_nurl = Strings::normaliseLink($contact_url); + $contact = Contact::getDetailsByURL($contact_nurl); + } + + if (!count($contact)) { + throw new HTTPException\NotFoundException(); + } + + // Get the photo_menu - the menu if possible contact actions + if (Session::isAuthenticated()) { + $actions = Contact::photoMenu($contact); + } else { + $actions = []; + } + + // Move the contact data to the profile array so we can deliver it to + $tpl = Renderer::getMarkupTemplate('hovercard.tpl'); + $o = Renderer::replaceMacros($tpl, [ + '$profile' => [ + 'name' => $contact['name'], + 'nick' => $contact['nick'], + 'addr' => $contact['addr'] ?: $contact['url'], + 'thumb' => Proxy::proxifyUrl($contact['thumb'], false, Proxy::SIZE_THUMB), + 'url' => Contact::magicLink($contact['url']), + 'nurl' => $contact['nurl'], + 'location' => $contact['location'], + 'gender' => $contact['gender'], + 'about' => $contact['about'], + 'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']), + 'tags' => $contact['keywords'], + 'bd' => $contact['birthday'] <= DBA::NULL_DATE ? '' : $contact['birthday'], + 'account_type' => Contact::getAccountType($contact), + 'actions' => $actions, + ], + ]); + + echo $o; + exit(); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index ee0669118b..1f2fe0ad1b 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -74,23 +74,25 @@ return [ '/compose[/{type}]' => [Module\Item\Compose::class, [R::GET, R::POST]], '/contact' => [ - '[/]' => [Module\Contact::class, [R::GET]], - '/{id:\d+}[/]' => [Module\Contact::class, [R::GET, R::POST]], - '/{id:\d+}/archive' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/block' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/conversations' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/drop' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/ignore' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/posts' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/update' => [Module\Contact::class, [R::GET]], - '/{id:\d+}/updateprofile' => [Module\Contact::class, [R::GET]], - '/archived' => [Module\Contact::class, [R::GET]], - '/batch' => [Module\Contact::class, [R::GET, R::POST]], - '/pending' => [Module\Contact::class, [R::GET]], - '/blocked' => [Module\Contact::class, [R::GET]], - '/hidden' => [Module\Contact::class, [R::GET]], - '/ignored' => [Module\Contact::class, [R::GET]], + '[/]' => [Module\Contact::class, [R::GET]], + '/{id:\d+}[/]' => [Module\Contact::class, [R::GET, R::POST]], + '/{id:\d+}/archive' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/block' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/conversations' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/drop' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/ignore' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/posts' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/update' => [Module\Contact::class, [R::GET]], + '/{id:\d+}/updateprofile' => [Module\Contact::class, [R::GET]], + '/archived' => [Module\Contact::class, [R::GET]], + '/batch' => [Module\Contact::class, [R::GET, R::POST]], + '/pending' => [Module\Contact::class, [R::GET]], + '/blocked' => [Module\Contact::class, [R::GET]], + '/hidden' => [Module\Contact::class, [R::GET]], + '/ignored' => [Module\Contact::class, [R::GET]], + '/hovercard' => [Module\Contact\Hovercard::class, [R::GET]], ], + '/credits' => [Module\Credits::class, [R::GET]], '/delegation'=> [Module\Delegation::class, [R::GET, R::POST]], '/dirfind' => [Module\Search\Directory::class, [R::GET]], diff --git a/view/templates/hovercard.tpl b/view/templates/hovercard.tpl index 017e096afc..4f41c204ee 100644 --- a/view/templates/hovercard.tpl +++ b/view/templates/hovercard.tpl @@ -28,6 +28,7 @@ {{if $profile.actions.network}}{{/if}} {{if $profile.actions.edit}}{{/if}} {{if $profile.actions.follow}}{{/if}} + {{if $profile.actions.unfollow}}{{/if}} diff --git a/view/theme/frio/css/style.css b/view/theme/frio/css/style.css index f1c78abc77..b0458d513c 100644 --- a/view/theme/frio/css/style.css +++ b/view/theme/frio/css/style.css @@ -1800,6 +1800,28 @@ aside .panel-body { font-size: 14px; } +/* Contact avatar click card */ +.userinfo.click-card { + position: relative; +} + +.userinfo.click-card > *:hover:after { + content: '⌄'; + color: #bebebe; + font-size: 1em; + font-weight: bold; + background-color: #ffffff; + text-align: center; + line-height: 40%; + position: absolute; + top: 0; + left: 0; + width: 33%; + height: 33%; + opacity: .8; + border-radius: 0 0 40% 0; +} + /* The lock symbol popup */ #panel { position: absolute; diff --git a/view/theme/frio/frameworks/jsmart/jsmart.custom.js b/view/theme/frio/frameworks/jsmart/jsmart.custom.js deleted file mode 100644 index 7a611a20e7..0000000000 --- a/view/theme/frio/frameworks/jsmart/jsmart.custom.js +++ /dev/null @@ -1,3456 +0,0 @@ -/*! - * jSmart Javascript template engine - * https://github.com/umakantp/jsmart - * - * Copyright 2011-2015, Max Miroshnikov - * Umakant Patil - * jSmart is licensed under the GNU Lesser General Public License - * http://opensource.org/licenses/LGPL-3.0 - */ - - -(function() { - - /** - merges two or more objects into one - shallow copy for objects - */ - function obMerge(ob1, ob2 /*, ...*/) - { - for (var i=1; i= 0 && s.substr(i-1,1).match(/\s/)) - { - continue; - } - if (!--openCount) - { - var sTag = s.slice(ldelim.length,i).replace(/[\r\n]/g, ' '); - var found = sTag.match(reTag); - if (found) - { - found.index = offset; - found[0] = s.slice(0,i+rdelim.length); - return found; - } - } - if (openCount < 0) //ignore any number of unmatched right delimiters - { - openCount = 0; - } - } - } - return null; - } - - function findCloseTag(reClose,reOpen,s) - { - var sInner = ''; - var closeTag = null; - var openTag = null; - var findIndex = 0; - - do - { - if (closeTag) - { - findIndex += closeTag[0].length; - } - closeTag = findTag(reClose,s); - if (!closeTag) - { - throw new Error('Unclosed {'+reOpen+'}'); - } - sInner += s.slice(0,closeTag.index); - findIndex += closeTag.index; - s = s.slice(closeTag.index+closeTag[0].length); - - openTag = findTag(reOpen,sInner); - if (openTag) - { - sInner = sInner.slice(openTag.index+openTag[0].length); - } - } - while (openTag); - - closeTag.index = findIndex; - return closeTag; - } - - function findElseTag(reOpen, reClose, reElse, s) - { - var offset = 0; - for (var elseTag=findTag(reElse,s); elseTag; elseTag=findTag(reElse,s)) - { - var openTag = findTag(reOpen,s); - if (!openTag || openTag.index > elseTag.index) - { - elseTag.index += offset; - return elseTag; - } - else - { - s = s.slice(openTag.index+openTag[0].length); - offset += openTag.index+openTag[0].length; - var closeTag = findCloseTag(reClose,reOpen,s); - s = s.slice(closeTag.index + closeTag[0].length); - offset += closeTag.index + closeTag[0].length; - } - } - return null; - } - - function execute(code, data) - { - if (typeof(code) == 'string') - { - with ({'__code':code}) - { - with (modifiers) - { - with (data) - { - try { - return eval(__code); - } - catch(e) - { - throw new Error(e.message + ' in \n' + code); - } - } - } - } - } - return code; - } - - /** - * Execute function when we have a object. - * - * @param object obj Object of the function to be called. - * @param array args Arguments to pass to a function. - * - * @return - * @throws Error If function obj does not exists. - */ - function executeByFuncObject(obj, args) { - try { - return obj.apply(this, args); - } catch (e) { - throw new Error(e.message); - } - } - - function assignVar(nm, val, data) - { - if (nm.match(/\[\]$/)) //ar[] = - { - data[ nm.replace(/\[\]$/,'') ].push(val); - } - else - { - data[nm] = val; - } - } - - var buildInFunctions = - { - expression: - { - parse: function(s, tree) - { - var e = parseExpression(s); - - tree.push({ - type: 'build-in', - name: 'expression', - expression: e.tree, - params: parseParams(s.slice(e.value.length).replace(/^\s+|\s+$/g,'')) - }); - - return e.tree; - - }, - process: function(node, data) - { - var params = getActualParamValues(node.params, data); - var res = process([node.expression],data); - - if (findInArray(params, 'nofilter') < 0) - { - for (var i=0; i': return arg1>arg2; - case '>=': return arg1>=arg2; - case '===': return arg1===arg2; - case '!==': return arg1!==arg2; - } - } - else if (node.op == '!') - { - return !arg1; - } - else - { - var isVar = node.params.__parsed[0].type == 'var'; - if (isVar) - { - arg1 = getVarValue(node.params.__parsed[0], data); - } - var v = arg1; - if (node.optype == 'pre-unary') - { - switch (node.op) - { - case '-': v=-arg1; break; - case '++': v=++arg1; break; - case '--': v=--arg1; break; - } - if (isVar) - { - getVarValue(node.params.__parsed[0], data, arg1); - } - } - else - { - switch (node.op) - { - case '++': arg1++; break; - case '--': arg1--; break; - } - getVarValue(node.params.__parsed[0], data, arg1); - } - return v; - } - } - }, - - section: - { - type: 'block', - parse: function(params, tree, content) - { - var subTree = []; - var subTreeElse = []; - tree.push({ - type: 'build-in', - name: 'section', - params: params, - subTree: subTree, - subTreeElse: subTreeElse - }); - - var findElse = findElseTag('section [^}]+', '\/section', 'sectionelse', content); - if (findElse) - { - parse(content.slice(0,findElse.index),subTree); - parse(content.slice(findElse.index+findElse[0].length).replace(/^[\r\n]/,''), subTreeElse); - } - else - { - parse(content, subTree); - } - }, - - process: function(node, data) - { - var params = getActualParamValues(node.params, data); - - var props = {}; - data.smarty.section[params.__get('name',null,0)] = props; - - var show = params.__get('show',true); - props.show = show; - if (!show) - { - return process(node.subTreeElse, data); - } - - var from = parseInt(params.__get('start',0)); - var to = (params.loop instanceof Object) ? countProperties(params.loop) : isNaN(params.loop) ? 0 : parseInt(params.loop); - var step = parseInt(params.__get('step',1)); - var max = parseInt(params.__get('max')); - if (isNaN(max)) - { - max = Number.MAX_VALUE; - } - - if (from < 0) - { - from += to; - if (from < 0) - { - from = 0; - } - } - else if (from >= to) - { - from = to ? to-1 : 0; - } - - var count = 0; - var loop = 0; - var i = from; - for (; i>=0 && i=0 && i=to); - props.index = i; - props.index_prev = i-step; - props.index_next = i+step; - props.iteration = props.rownum = count+1; - - s += process(node.subTree, data); - data.smarty['continue'] = false; - } - data.smarty['break'] = false; - - if (count) - { - return s; - } - return process(node.subTreeElse, data); - } - }, - - setfilter: - { - type: 'block', - parseParams: function(paramStr) - { - return [parseExpression('__t()|' + paramStr).tree]; - }, - - parse: function(params, tree, content) - { - tree.push({ - type: 'build-in', - name: 'setfilter', - params: params, - subTree: parse(content,[]) - }); - }, - - process: function(node, data) - { - tpl_modifiers = node.params; - var s = process(node.subTree, data); - tpl_modifiers = []; - return s; - } - }, - - 'for': - { - type: 'block', - parseParams: function(paramStr) - { - var res = paramStr.match(/^\s*\$(\w+)\s*=\s*([^\s]+)\s*to\s*([^\s]+)\s*(?:step\s*([^\s]+))?\s*(.*)$/); - if (!res) - { - throw new Error('Invalid {for} parameters: '+paramStr); - } - return parseParams("varName='"+res[1]+"' from="+res[2]+" to="+res[3]+" step="+(res[4]?res[4]:'1')+" "+res[5]); - }, - - parse: function(params, tree, content) - { - var subTree = []; - var subTreeElse = []; - tree.push({ - type: 'build-in', - name: 'for', - params: params, - subTree: subTree, - subTreeElse: subTreeElse - }); - - var findElse = findElseTag('for\\s[^}]+', '\/for', 'forelse', content); - if (findElse) - { - parse(content.slice(0,findElse.index),subTree); - parse(content.slice(findElse.index+findElse[0].length), subTreeElse); - } - else - { - parse(content, subTree); - } - }, - - process: function(node, data) - { - var params = getActualParamValues(node.params, data); - var from = parseInt(params.__get('from')); - var to = parseInt(params.__get('to')); - var step = parseInt(params.__get('step')); - if (isNaN(step)) - { - step = 1; - } - var max = parseInt(params.__get('max')); - if (isNaN(max)) - { - max = Number.MAX_VALUE; - } - - var count = 0; - var s = ''; - var total = Math.min( Math.ceil( ((step > 0 ? to-from : from-to)+1) / Math.abs(step) ), max); - - for (var i=parseInt(params.from); count\s*[$](\w+))?\s*$/i); - if (res) //Smarty 3.x syntax => Smarty 2.x syntax - { - paramStr = 'from='+res[1] + ' item='+(res[4]||res[2]); - if (res[4]) - { - paramStr += ' key='+res[2]; - } - } - return parseParams(paramStr); - }, - - parse: function(params, tree, content) - { - var subTree = []; - var subTreeElse = []; - tree.push({ - type: 'build-in', - name: 'foreach', - params: params, - subTree: subTree, - subTreeElse: subTreeElse - }); - - var findElse = findElseTag('foreach\\s[^}]+', '\/foreach', 'foreachelse', content); - if (findElse) - { - parse(content.slice(0,findElse.index),subTree); - parse(content.slice(findElse.index+findElse[0].length).replace(/^[\r\n]/,''), subTreeElse); - } - else - { - parse(content, subTree); - } - }, - - process: function(node, data) - { - var params = getActualParamValues(node.params, data); - var a = params.from; - if (typeof a == 'undefined') - { - a = []; - } - if (typeof a != 'object') - { - a = [a]; - } - - var total = countProperties(a); - - data[params.item+'__total'] = total; - if ('name' in params) - { - data.smarty.foreach[params.name] = {}; - data.smarty.foreach[params.name].total = total; - } - - var s = ''; - var i=0; - for (var key in a) - { - if (!a.hasOwnProperty(key)) - { - continue; - } - - if (data.smarty['break']) - { - break; - } - - data[params.item+'__key'] = isNaN(key) ? key : parseInt(key); - if ('key' in params) - { - data[params.key] = data[params.item+'__key']; - } - data[params.item] = a[key]; - data[params.item+'__index'] = parseInt(i); - data[params.item+'__iteration'] = parseInt(i+1); - data[params.item+'__first'] = (i===0); - data[params.item+'__last'] = (i==total-1); - - if ('name' in params) - { - data.smarty.foreach[params.name].index = parseInt(i); - data.smarty.foreach[params.name].iteration = parseInt(i+1); - data.smarty.foreach[params.name].first = (i===0) ? 1 : ''; - data.smarty.foreach[params.name].last = (i==total-1) ? 1 : ''; - } - - ++i; - - s += process(node.subTree, data); - data.smarty['continue'] = false; - } - data.smarty['break'] = false; - - data[params.item+'__show'] = (i>0); - if (params.name) - { - data.smarty.foreach[params.name].show = (i>0) ? 1 : ''; - } - if (i>0) - { - return s; - } - return process(node.subTreeElse, data); - } - }, - - 'function': - { - type: 'block', - parse: function(params, tree, content) - { - var subTree = []; - plugins[trimQuotes(params.name?params.name:params[0])] = - { - type: 'function', - subTree: subTree, - defautParams: params, - process: function(params, data) - { - var defaults = getActualParamValues(this.defautParams,data); - delete defaults.name; - return process(this.subTree, obMerge({},data,defaults,params)); - } - }; - parse(content, subTree); - } - }, - - php: - { - type: 'block', - parse: function(params, tree, content) {} - }, - - 'extends': - { - type: 'function', - parse: function(params, tree) - { - tree.splice(0,tree.length); - getTemplate(trimQuotes(params.file?params.file:params[0]),tree); - } - }, - - block: - { - type: 'block', - parse: function(params, tree, content) - { - tree.push({ - type: 'build-in', - name: 'block', - params: params - }); - params.append = findInArray(params,'append') >= 0; - params.prepend = findInArray(params,'prepend') >= 0; - params.hide = findInArray(params,'hide') >= 0; - params.hasChild = params.hasParent = false; - - onParseVar = function(nm) - { - if (nm.match(/^\s*[$]smarty.block.child\s*$/)) - { - params.hasChild = true; - } - if (nm.match(/^\s*[$]smarty.block.parent\s*$/)) - { - params.hasParent = true; - } - } - var tree = parse(content, []); - onParseVar = function(nm) {} - - var blockName = trimQuotes(params.name?params.name:params[0]); - if (!(blockName in blocks)) - { - blocks[blockName] = []; - } - blocks[blockName].push({tree:tree, params:params}); - }, - - process: function(node, data) - { - data.smarty.block.parent = data.smarty.block.child = ''; - var blockName = trimQuotes(node.params.name?node.params.name:node.params[0]); - this.processBlocks(blocks[blockName], blocks[blockName].length-1, data); - return data.smarty.block.child; - }, - - processBlocks: function(blockAncestry, i, data) - { - if (!i && blockAncestry[i].params.hide) { - data.smarty.block.child = ''; - return; - } - var append = true; - var prepend = false; - for (; i>=0; --i) - { - if (blockAncestry[i].params.hasParent) - { - var tmpChild = data.smarty.block.child; - data.smarty.block.child = ''; - this.processBlocks(blockAncestry, i-1, data); - data.smarty.block.parent = data.smarty.block.child; - data.smarty.block.child = tmpChild; - } - - var tmpChild = data.smarty.block.child; - var s = process(blockAncestry[i].tree, data); - data.smarty.block.child = tmpChild; - - if (blockAncestry[i].params.hasChild) - { - data.smarty.block.child = s; - } - else if (append) - { - data.smarty.block.child = s + data.smarty.block.child; - } - else if (prepend) - { - data.smarty.block.child += s; - } - append = blockAncestry[i].params.append; - prepend = blockAncestry[i].params.prepend; - } - } - }, - - strip: - { - type: 'block', - parse: function(params, tree, content) - { - parse(content.replace(/[ \t]*[\r\n]+[ \t]*/g, ''), tree); - } - }, - - literal: - { - type: 'block', - parse: function(params, tree, content) - { - parseText(content, tree); - } - }, - - ldelim: - { - type: 'function', - parse: function(params, tree) - { - parseText(jSmart.prototype.left_delimiter, tree); - } - }, - - rdelim: - { - type: 'function', - parse: function(params, tree) - { - parseText(jSmart.prototype.right_delimiter, tree); - } - }, - - 'while': - { - type: 'block', - parse: function(params, tree, content) - { - tree.push({ - type: 'build-in', - name: 'while', - params: params, - subTree: parse(content, []) - }); - }, - - process: function(node, data) - { - var s = ''; - while (getActualParamValues(node.params,data)[0]) - { - if (data.smarty['break']) - { - break; - } - s += process(node.subTree, data); - data.smarty['continue'] = false; - } - data.smarty['break'] = false; - return s; - } - } - }; - - var plugins = {}; - var modifiers = {}; - var files = {}; - var blocks = null; - var scripts = null; - var tpl_modifiers = []; - - function parse(s, tree) - { - for (var openTag=findTag('',s); openTag; openTag=findTag('',s)) - { - if (openTag.index) - { - parseText(s.slice(0,openTag.index),tree); - } - s = s.slice(openTag.index + openTag[0].length); - - var res = openTag[1].match(/^\s*(\w+)(.*)$/); - if (res) //function - { - var nm = res[1]; - var paramStr = (res.length>2) ? res[2].replace(/^\s+|\s+$/g,'') : ''; - - if (nm in buildInFunctions) - { - var buildIn = buildInFunctions[nm]; - var params = ('parseParams' in buildIn ? buildIn.parseParams : parseParams)(paramStr); - if (buildIn.type == 'block') - { - s = s.replace(/^\n/,''); //remove new line after block open tag (like in Smarty) - var closeTag = findCloseTag('\/'+nm, nm+' +[^}]*', s); - buildIn.parse(params, tree, s.slice(0,closeTag.index)); - s = s.slice(closeTag.index+closeTag[0].length); - } - else - { - buildIn.parse(params, tree); - if (nm == 'extends') - { - tree = []; //throw away further parsing except for {block} - } - } - s = s.replace(/^\n/,''); - } - else if (nm in plugins) - { - var plugin = plugins[nm]; - if (plugin.type == 'block') - { - var closeTag = findCloseTag('\/'+nm, nm+' +[^}]*', s); - parsePluginBlock(nm, parseParams(paramStr), tree, s.slice(0,closeTag.index)); - s = s.slice(closeTag.index+closeTag[0].length); - } - else if (plugin.type == 'function') - { - parsePluginFunc(nm, parseParams(paramStr), tree); - } - if (nm=='append' || nm=='assign' || nm=='capture' || nm=='eval' || nm=='include') - { - s = s.replace(/^\n/,''); - } - } - else //variable - { - buildInFunctions.expression.parse(openTag[1],tree); - } - } - else //variable - { - var node = buildInFunctions.expression.parse(openTag[1],tree); - if (node.type=='build-in' && node.name=='operator' && node.op == '=') - { - s = s.replace(/^\n/,''); - } - } - } - if (s) - { - parseText(s, tree); - } - return tree; - } - - function parseText(text, tree) - { - if (parseText.parseEmbeddedVars) - { - var re = /([$][\w@]+)|`([^`]*)`/; - for (var found=re.exec(text); found; found=re.exec(text)) - { - tree.push({type: 'text', data: text.slice(0,found.index)}); - tree.push( parseExpression(found[1] ? found[1] : found[2]).tree ); - text = text.slice(found.index + found[0].length); - } - } - tree.push({type: 'text', data: text}); - return tree; - } - - function parseFunc(name, params, tree) - { - params.__parsed.name = parseText(name,[])[0]; - tree.push({ - type: 'plugin', - name: '__func', - params: params - }); - return tree; - } - - function parseOperator(op, type, precedence, tree) - { - tree.push({ - type: 'build-in', - name: 'operator', - op: op, - optype: type, - precedence: precedence, - params: {} - }); - } - - function parseVar(s, e, nm) - { - var rootName = e.token; - var parts = [{type:'text', data:nm.replace(/^(\w+)@(key|index|iteration|first|last|show|total)/gi, "$1__$2")}]; - - var re = /^(?:\.|\s*->\s*|\[\s*)/; - for (var op=s.match(re); op; op=s.match(re)) - { - e.token += op[0]; - s = s.slice(op[0].length); - - var eProp = {value:'', tree:[]}; - if (op[0].match(/\[/)) - { - eProp = parseExpression(s); - if (eProp) - { - e.token += eProp.value; - parts.push( eProp.tree ); - s = s.slice(eProp.value.length); - } - - var closeOp = s.match(/\s*\]/); - if (closeOp) - { - e.token += closeOp[0]; - s = s.slice(closeOp[0].length); - } - } - else - { - var parseMod = parseModifiers.stop; - parseModifiers.stop = true; - if (lookUp(s,eProp)) - { - e.token += eProp.value; - var part = eProp.tree[0]; - if (part.type == 'plugin' && part.name == '__func') - { - part.hasOwner = true; - } - parts.push( part ); - s = s.slice(eProp.value.length); - } - else - { - eProp = false; - } - parseModifiers.stop = parseMod; - } - - if (!eProp) - { - parts.push({type:'text', data:''}); - } - } - - e.tree.push({type: 'var', parts: parts}); - - e.value += e.token.substr(rootName.length); - - onParseVar(e.token); - - return s; - } - - function onParseVar(nm) {} - - - var tokens = - [ - { - re: /^\$([\w@]+)/, //var - parse: function(e, s) - { - parseModifiers(parseVar(s, e, RegExp.$1), e); - } - }, - { - re: /^(true|false)/i, //bool - parse: function(e, s) - { - parseText(e.token.match(/true/i) ? '1' : '', e.tree); - } - }, - { - re: /^'([^'\\]*(?:\\.[^'\\]*)*)'/, //single quotes - parse: function(e, s) - { - parseText(evalString(RegExp.$1), e.tree); - parseModifiers(s, e); - } - }, - { - re: /^"([^"\\]*(?:\\.[^"\\]*)*)"/, //double quotes - parse: function(e, s) - { - var v = evalString(RegExp.$1); - var isVar = v.match(tokens[0].re); - if (isVar) - { - var eVar = {token:isVar[0], tree:[]}; - parseVar(v, eVar, isVar[1]); - if (eVar.token.length == v.length) - { - e.tree.push( eVar.tree[0] ); - return; - } - } - parseText.parseEmbeddedVars = true; - e.tree.push({ - type: 'plugin', - name: '__quoted', - params: {__parsed: parse(v,[])} - }); - parseText.parseEmbeddedVars = false; - parseModifiers(s, e); - } - }, - { - re: /^(\w+)\s*[(]([)]?)/, //func() - parse: function(e, s) - { - var fnm = RegExp.$1; - var noArgs = RegExp.$2; - var params = parseParams(noArgs?'':s,/^\s*,\s*/); - parseFunc(fnm, params, e.tree); - e.value += params.toString(); - parseModifiers(s.slice(params.toString().length), e); - } - }, - { - re: /^\s*\(\s*/, //expression in parentheses - parse: function(e, s) - { - var parens = []; - e.tree.push(parens); - parens.parent = e.tree; - e.tree = parens; - } - }, - { - re: /^\s*\)\s*/, - parse: function(e, s) - { - if (e.tree.parent) //it may be the end of func() or (expr) - { - e.tree = e.tree.parent; - } - } - }, - { - re: /^\s*(\+\+|--)\s*/, - parse: function(e, s) - { - if (e.tree.length && e.tree[e.tree.length-1].type == 'var') - { - parseOperator(RegExp.$1, 'post-unary', 1, e.tree); - } - else - { - parseOperator(RegExp.$1, 'pre-unary', 1, e.tree); - } - } - }, - { - re: /^\s*(===|!==|==|!=)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1, 'binary', 6, e.tree); - } - }, - { - re: /^\s+(eq|ne|neq)\s+/i, - parse: function(e, s) - { - var op = RegExp.$1.replace(/ne(q)?/,'!=').replace(/eq/,'=='); - parseOperator(op, 'binary', 6, e.tree); - } - }, - { - re: /^\s*!\s*/, - parse: function(e, s) - { - parseOperator('!', 'pre-unary', 2, e.tree); - } - }, - { - re: /^\s+not\s+/i, - parse: function(e, s) - { - parseOperator('!', 'pre-unary', 2, e.tree); - } - }, - { - re: /^\s*(=|\+=|-=|\*=|\/=|%=)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1, 'binary', 10, e.tree); - } - }, - { - re: /^\s*(\*|\/|%)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1, 'binary', 3, e.tree); - } - }, - { - re: /^\s+mod\s+/i, - parse: function(e, s) - { - parseOperator('%', 'binary', 3, e.tree); - } - }, - { - re: /^\s*(\+|-)\s*/, - parse: function(e, s) - { - if (!e.tree.length || e.tree[e.tree.length-1].name == 'operator') - { - parseOperator(RegExp.$1, 'pre-unary', 4, e.tree); - } - else - { - parseOperator(RegExp.$1, 'binary', 4, e.tree); - } - } - }, - { - re: /^\s*(<=|>=|<>|<|>)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1.replace(/<>/,'!='), 'binary', 5, e.tree); - } - }, - { - re: /^\s+(lt|lte|le|gt|gte|ge)\s+/i, - parse: function(e, s) - { - var op = RegExp.$1.replace(/lt/,'<').replace(/l(t)?e/,'<=').replace(/gt/,'>').replace(/g(t)?e/,'>='); - parseOperator(op, 'binary', 5, e.tree); - } - }, - { - re: /^\s+(is\s+(not\s+)?div\s+by)\s+/i, - parse: function(e, s) - { - parseOperator(RegExp.$2?'div_not':'div', 'binary', 7, e.tree); - } - }, - { - re: /^\s+is\s+(not\s+)?(even|odd)(\s+by\s+)?\s*/i, - parse: function(e, s) - { - var op = RegExp.$1 ? ((RegExp.$2=='odd')?'even':'even_not') : ((RegExp.$2=='odd')?'even_not':'even'); - parseOperator(op, 'binary', 7, e.tree); - if (!RegExp.$3) - { - parseText('1', e.tree); - } - } - }, - { - re: /^\s*(&&)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1, 'binary', 8, e.tree); - } - }, - { - re: /^\s*(\|\|)\s*/, - parse: function(e, s) - { - parseOperator(RegExp.$1, 'binary', 9, e.tree); - } - }, - { - re: /^\s+and\s+/i, - parse: function(e, s) - { - parseOperator('&&', 'binary', 11, e.tree); - } - }, - { - re: /^\s+xor\s+/i, - parse: function(e, s) - { - parseOperator('xor', 'binary', 12, e.tree); - } - }, - { - re: /^\s+or\s+/i, - parse: function(e, s) - { - parseOperator('||', 'binary', 13, e.tree); - } - }, - { - re: /^#(\w+)#/, //config variable - parse: function(e, s) - { - var eVar = {token:'$smarty',tree:[]}; - parseVar('.config.'+RegExp.$1, eVar, 'smarty'); - e.tree.push( eVar.tree[0] ); - parseModifiers(s, e); - } - }, - { - re: /^\s*\[\s*/, //array - parse: function(e, s) - { - var params = parseParams(s, /^\s*,\s*/, /^('[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"|\w+)\s*=>\s*/); - parsePluginFunc('__array',params,e.tree); - e.value += params.toString(); - var paren = s.slice(params.toString().length).match(/\s*\]/); - if (paren) - { - e.value += paren[0]; - } - } - }, - { - re: /^[\d.]+/, //number - parse: function(e, s) - { - if (e.token.indexOf('.') > -1) { - e.token = parseFloat(e.token); - } else { - e.token = parseInt(e.token, 10); - } - parseText(e.token, e.tree); - parseModifiers(s, e); - } - }, - { - re: /^\w+/, //static - parse: function(e, s) - { - parseText(e.token, e.tree); - parseModifiers(s, e); - } - } - ]; - - function parseModifiers(s, e) - { - if (parseModifiers.stop) - { - return; - } - - var modifier = s.match(/^\|(\w+)/); - if (!modifier) - { - return; - } - - e.value += modifier[0]; - - var fnm = modifier[1]=='default' ? 'defaultValue' : modifier[1]; - s = s.slice(modifier[0].length).replace(/^\s+/,''); - - parseModifiers.stop = true; - var params = []; - for (var colon=s.match(/^\s*:\s*/); colon; colon=s.match(/^\s*:\s*/)) - { - e.value += s.slice(0,colon[0].length); - s = s.slice(colon[0].length); - - var param = {value:'', tree:[]}; - if (lookUp(s, param)) - { - e.value += param.value; - params.push(param.tree[0]); - s = s.slice(param.value.length); - } - else - { - parseText('',params); - } - } - parseModifiers.stop = false; - - params.unshift(e.tree.pop()); //modifiers have the highest priority - e.tree.push(parseFunc(fnm,{__parsed:params},[])[0]); - - parseModifiers(s, e); //modifiers can be combined - } - - function lookUp(s,e) - { - if (!s) - { - return false; - } - - if (s.substr(0,jSmart.prototype.left_delimiter.length)==jSmart.prototype.left_delimiter) - { - var tag = findTag('',s); - if (tag) - { - e.token = tag[0]; - e.value += tag[0]; - parse(tag[0], e.tree); - parseModifiers(s.slice(e.value.length), e); - return true; - } - } - - for (var i=0; i0; --i) - { - i -= bundleOp(i-1, tree, precedence); - } - } - else - { - for (i=0; i= data.smarty.cycle[name].arr.length || reset) - { - data.smarty.cycle[name].index = 0; - } - - if (params.__get('assign',false)) - { - assignVar(params.assign, data.smarty.cycle[name].arr[ data.smarty.cycle[name].index ], data); - return ''; - } - - if (params.__get('print',true)) - { - return data.smarty.cycle[name].arr[ data.smarty.cycle[name].index ]; - } - - return ''; - } - ); - - jSmart.prototype.print_r = function(v,indent) - { - if (v instanceof Object) - { - var s = ((v instanceof Array) ? 'Array['+v.length+']' : 'Object') + '
'; - for (var nm in v) - { - if (v.hasOwnProperty(nm)) - { - s += indent + '  ' + nm + ' : ' + jSmart.prototype.print_r(v[nm],indent+'   ') + '
'; - } - } - return s; - } - return v; - } - - jSmart.prototype.registerPlugin( - 'function', - 'debug', - function(params, data) - { - if (typeof dbgWnd != 'undefined') - { - dbgWnd.close(); - } - dbgWnd = window.open('','','width=680,height=600,resizable,scrollbars=yes'); - var sVars = ''; - var i=0; - for (var nm in data) - { - sVars += '' + nm + '' + jSmart.prototype.print_r(data[nm],'') + ''; - } - dbgWnd.document.write(" \ - \ - \ - jSmart Debug Console \ - \ - \ - \ -

jSmart Debug Console

\ -

assigned template variables

\ - " + sVars + "
\ - \ - \ - "); - return ''; - } - ); - - jSmart.prototype.registerPlugin( - 'function', - 'eval', - function(params, data) - { - var tree = []; - parse(params.__get('var','',0), tree); - var s = process(tree, data); - if ('assign' in params) - { - assignVar(params.assign, s, data); - return ''; - } - return s; - } - ); - - jSmart.prototype.registerPlugin( - 'function', - 'fetch', - function(params, data) - { - var s = jSmart.prototype.getFile(params.__get('file',null,0)); - if ('assign' in params) - { - assignVar(params.assign, s, data); - return ''; - } - return s; - } - ); - - jSmart.prototype.registerPlugin( - 'function', - 'html_checkboxes', - function (params, data) { - var type = params.__get('type','checkbox'), - name = params.__get('name',type), - realName = params.__get('name',type), - values = params.__get('values',params.options), - output = params.__get('options',[]), - useName = ('options' in params), - selected = params.__get('selected',false), - separator = params.__get('separator',''), - labels = Boolean(params.__get('labels',true)), - label_ids = Boolean(params.__get('label_ids',false)), - p, - res = [], - i = 0, - s = '', - value, - id; - - if (type == 'checkbox') { - name += '[]'; - } - if (!useName) { - for (p in params.output) { - output.push(params.output[p]); - } - } - - for (p in values) { - if (values.hasOwnProperty(p)) { - value = (useName ? p : values[p]); - id = realName + '_' + value; - s = (labels ? ( label_ids ? '