--- /dev/null
+<?php
+// vim: tabstop=2:shiftwidth=2
+
+/**
+ * NP_Clap ($Revision: 1.1 $)
+ * by hsur ( http://blog.cles.jp/np_cles )
+ *
+ * $Id: NP_Clap.php,v 1.1 2008-05-17 19:11:11 hsur Exp $
+*/
+
+/*
+ * Copyright (C) 2006-2008 CLES. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
+ * permission to link the code of this program with those files in the PEAR
+ * library that are licensed under the PHP License (or with modified versions
+ * of those files that use the same license as those files), and distribute
+ * linked combinations including the two. You must obey the GNU General Public
+ * License in all respects for all of the code used other than those files in
+ * the PEAR library that are licensed under the PHP License. If you modify
+ * this file, you may extend this exception to your version of the file,
+ * but you are not obligated to do so. If you do not wish to do so, delete
+ * this exception statement from your version.
+*/
+
+// load class
+require_once(dirname(__FILE__).'/sharedlibs/sharedlibs.php');
+require_once('cles/Template.php');
+require_once('Jsphon.php');
+
+define('NP_CLAP_GLOBALKEY', 'global');
+
+class NP_Clap extends NucleusPlugin {
+
+ // name of plugin
+ function getName() {
+ return 'Clap';
+ }
+
+ // author of plugin
+ function getAuthor() {
+ return 'hsur';
+ }
+
+ // an URL to the plugin website
+ // can also be of the form mailto:foo@bar.com
+ function getURL() {
+ return 'http://blog.cles.jp/np_cles/category/31/subcatid/13';
+ }
+
+ // version of the plugin
+ function getVersion() {
+ return '1.6.0';
+ }
+
+ function install() {
+ $this->createOption('mailaddr', NP_CLAP_mailaddr, 'text', '');
+ $this->createOption('commentedOnly', NP_CLAP_commentedOnly, 'yesno', 'yes');
+
+ $this->createOption('antispam_limit', NP_CLAP_antispam_limit, 'text', '10/86400');
+ $this->createOption('antispam_check', NP_CLAP_antispam_check, 'yesno', 'yes');
+
+ $this->createOption('deleteData', NP_CLAP_deleteData, 'yesno', 'no');
+
+ /* Create tables */
+ sql_query("
+ CREATE TABLE IF NOT EXISTS
+ ".sql_table('plugin_clap').'
+ (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `itemkey` CHAR(11),
+ `timestamp` DATETIME,
+ `ipaddr` CHAR(15),
+ PRIMARY KEY (`id`),
+ INDEX itemkey_timestamp (`itemkey`, `timestamp`),
+ INDEX ipaddr_timestamp (`ipaddr`, `timestamp`)
+ )
+ ');
+ sql_query("
+ CREATE TABLE IF NOT EXISTS
+ ".sql_table('plugin_clap_comment').'
+ (
+ `id` INT(11) NOT NULL,
+ `user` VARCHAR(255),
+ `mail_or_url` VARCHAR(255),
+ `comment` TEXT,
+ PRIMARY KEY (`id`)
+ )
+ ');
+ sql_query("
+ CREATE TABLE IF NOT EXISTS
+ ".sql_table('plugin_clap_thanks').'
+ (
+ `id` INT(11) NOT NULL AUTO_INCREMENT,
+ `image` TEXT,
+ `comment` TEXT,
+ PRIMARY KEY (`id`)
+ )
+ ');
+
+ sql_query("
+ CREATE TABLE IF NOT EXISTS
+ ".sql_table('plugin_clap_thanks_category').'
+ (
+ `category` VARCHAR(255),
+ `thanksid` INT(11),
+ INDEX category_idx (`category`),
+ INDEX thanksid_idx (`thanksid`)
+ )
+ ');
+ }
+
+ function uninstall() {
+ if($this->getOption('deleteData') == "yes") {
+ sql_query("DROP TABLE ".sql_table('plugin_clap'));
+ sql_query("DROP TABLE ".sql_table('plugin_clap_comment'));
+ sql_query("DROP TABLE ".sql_table('plugin_clap_thanks'));
+ sql_query("DROP TABLE ".sql_table('plugin_clap_thanks_category'));
+ }
+ }
+
+ function getTableList() {
+ return array(
+ sql_table('plugin_clap'),
+ sql_table('plugin_clap_comment'),
+ sql_table('plugin_clap_thanks'),
+ sql_table('plugin_clap_thanks_category'),
+ );
+ }
+
+ function getEventList() {
+ return array('QuickMenu');
+ }
+
+ function getMinNucleusVersion() { return 320; }
+ function getMinNucleusPatchLevel() { return 0; }
+
+ // a description to be shown on the installed plugins listing
+ function getDescription() {
+ return '[$Revision: 1.1 $]<br />'.NP_CLAP_description ;
+ }
+
+ function supportsFeature($what) {
+ switch($what){
+ case 'SqlTablePrefix':
+ case 'HelpPage':
+ return 1;
+ default:
+ return 0;
+ }
+ }
+
+ function hasAdminArea() { return 1; }
+
+ function event_QuickMenu(&$data) {
+ global $member, $nucleus, $blogid;
+
+ // only show to admins
+ if (!$member->isLoggedIn() || !$member->isAdmin()) return;
+
+ array_push(
+ $data['options'],
+ array(
+ 'title' => 'Clap',
+ 'url' => $this->getAdminURL(),
+ 'tooltip' => 'Manage your clap'
+ )
+ );
+ }
+
+ function init(){
+ // include language file for this plugin
+ $language = ereg_replace( '[\\|/]', '', getLanguageName());
+ if (file_exists($this->getDirectory().'language/'.$language.'.php'))
+ @ include_once($this->getDirectory().'language/'.$language.'.php');
+ }
+
+ function doAction($type) {
+ global $itemid, $member, $manager;
+ $aActionsNotToCheck = array(
+ '',
+ 'clap',
+ 'preview',
+ );
+ if (!in_array($type, $aActionsNotToCheck)) {
+ if (!$manager->checkTicket()) return _ERROR_BADTICKET;
+ }
+
+ $key = requestVar('key') ? requestVar('key') : false;
+ if(!$key){
+ # $key = $itemid ? $itemid : NP_CLAP_GLOBALKEY;
+ }
+ switch ($type) {
+ case 'preview':
+ if( $member->isLoggedIn() ){
+ $id = requestVar('id');
+ if( ! is_numeric($id) ){
+ $id = null;
+ }
+ $this->showPreview($id);
+ } else {
+ return _ERROR_DISALLOWED;
+ }
+ break;
+ break;
+ case 'clap':
+ $this->doClap($key);
+ break;
+
+ case 'chart':
+ $year = intRequestVar('year');
+ $month = intRequestVar('month');
+ $this->echoChartJs($year, $month);
+ break;
+
+ // other actions result in an error
+ case '':
+ default:
+ return 'Unexisting action: ' . $type;
+ }
+ return '';
+ }
+
+ function doTemplateVar(&$item, $what = '', $key = '') {
+ $key = $key ? $key : $item->itemid;
+ $this->doSkinVar('template', $what, $key);
+ }
+
+ function doSkinVar($skinType, $type = '') {
+ global $itemid;
+ if( $type != 'list'){
+ list(,,$key) = func_get_args();
+ if( ! $key ){
+ $key = $itemid ? $itemid : NP_CLAP_GLOBALKEY;
+ }
+ } else {
+ list(,,$count,$blog,$order,$numOfDays) = func_get_args();
+ $count = $count ? $count : 10;
+ if( $blog ){
+ $blog = strtr($blog, '/', ',');
+ }
+ $order = $order ? $order : 'DESC';
+ $numOfDays = is_numeric($numOfDays) ? $numOfDays : false;
+ }
+
+ switch ($type) {
+ case 'button':
+ case '':
+ $this->showButton($key);
+ break;
+ case 'count':
+ echo $this->getCount($key);
+ break;
+ case 'list':
+ $this->showList($blog,$count,$order,$numOfDays);
+ break;
+ case 'actionurl':
+ echo $this->getActionUrl($key);
+ break;
+ default:
+ return 'Unexisting type: ' . $type;
+ }
+ }
+
+ function echoChartJs($year = null, $month = null){
+ $year = intval($year) ? intval($year) : date('Y');
+ $month = intval($month) ? intval($month) : date('m');
+
+ $fromDate = date("Y-m-d H:i:s", mktime( 0, 0, 0, $month, 1 , $year));
+ $toDate = date("Y-m-d H:i:s", mktime( 23, 59, 59, $month+1, 0 , $year));
+
+ $param = array();
+
+ // keys
+ $param['key'] = array();
+ $query = sprintf('SELECT itemkey, count(*) as count, count(DISTINCT ipaddr) as ipcount, count(c.id) as msgcount FROM ' . sql_table('plugin_clap')
+ . " LEFT OUTER JOIN " . sql_table('plugin_clap_comment') . " c USING( id ) "
+ . " WHERE `timestamp` between '%s' and '%s' "
+ . " GROUP BY itemkey ORDER BY count DESC LIMIT 20"
+ , mysql_real_escape_string( $fromDate )
+ , mysql_real_escape_string( $toDate )
+ );
+ $res = sql_query($query);
+
+ while( $row = mysql_fetch_array($res) ){
+ $param['key']['label'][] = $row['itemkey'];
+ $param['key']['count'][] = $row['count'];
+ $param['key']['ipcount'][] = $row['ipcount'];
+ $param['key']['msgcount'][] = $row['msgcount'];
+ if(!$param['key']['max'])
+ $param['key']['max'] = $row['count'];
+ }
+ $param['key']['length'] = count($param['key']['label']);
+ if(!$param['key']['length']){
+ $param['key']['label'][] = "";
+ $param['key']['count'][] = 0;
+ $param['key']['ipcount'][] = 0;
+ $param['key']['msgcount'][] = 0;
+ $param['key']['max'] = 10;
+ }
+
+ // dayOfMonth
+ $param['dayOfMonth'] = array();
+ $query = sprintf('SELECT DAYOFMONTH(`timestamp`) as day_of_month, count(*) as count, count(DISTINCT ipaddr) as ipcount, count(c.id) as msgcount FROM ' . sql_table('plugin_clap')
+ . " LEFT OUTER JOIN " . sql_table('plugin_clap_comment') . " c USING( id ) "
+ . " WHERE `timestamp` between '%s' and '%s' "
+ . " GROUP BY day_of_month ORDER BY count"
+ , mysql_real_escape_string( $fromDate )
+ , mysql_real_escape_string( $toDate )
+ );
+ $res = sql_query($query);
+
+ $lastDay = date("d", mktime( 23, 59, 59, $month+1, 0 , $year));
+ $param['dayOfMonth']['label'] = range(1, $lastDay );
+ $param['dayOfMonth']['count'] = array_fill(0, $lastDay, 0);
+ $param['dayOfMonth']['ipcount'] = array_fill(0, $lastDay, 0);
+ $param['dayOfMonth']['msgcount'] = array_fill(0, $lastDay, 0);
+ $param['dayOfMonth']['length'] = $lastDay;
+ while( $row = mysql_fetch_array($res) ){
+ $idx = $row['day_of_month'] - 1;
+ $param['dayOfMonth']['count'][$idx] = $row['count'];
+ $param['dayOfMonth']['ipcount'][$idx] = $row['ipcount'];
+ $param['dayOfMonth']['msgcount'][$idx] = $row['msgcount'];
+ $param['dayOfMonth']['max'] = $row['count'];
+ }
+
+ // dayOfWeek
+ $param['dayOfWeek'] = array();
+ $query = sprintf('SELECT DAYOFWEEK(`timestamp`) as day_of_week, count(*) as count, count(DISTINCT ipaddr) as ipcount, count(c.id) as msgcount FROM ' . sql_table('plugin_clap')
+ . " LEFT OUTER JOIN " . sql_table('plugin_clap_comment') . " c USING( id ) "
+ . " WHERE `timestamp` between '%s' and '%s' "
+ . " GROUP BY day_of_week ORDER BY count"
+ , mysql_real_escape_string( $fromDate )
+ , mysql_real_escape_string( $toDate )
+ );
+ $res = sql_query($query);
+
+ $param['dayOfWeek']['label'] = explode(',', NP_CLAP_DAYOFWEEK);
+ $param['dayOfWeek']['count'] = array_fill(0, 7, 0);
+ $param['dayOfWeek']['ipcount'] = array_fill(0, 7, 0);
+ $param['dayOfWeek']['msgcount'] = array_fill(0, 7, 0);
+ $param['dayOfWeek']['length'] = 7;
+ while( $row = mysql_fetch_array($res) ){
+ $idx = $row['day_of_week'] - 1;
+ $param['dayOfWeek']['count'][$idx] = $row['count'];
+ $param['dayOfWeek']['ipcount'][$idx] = $row['ipcount'];
+ $param['dayOfWeek']['msgcount'][$idx] = $row['msgcount'];
+ $param['dayOfWeek']['max'] = $row['count'];
+ }
+
+ // hours
+ $param['hours'] = array();
+ $query = sprintf('SELECT HOUR(`timestamp`) as hour, count(*) as count, count(DISTINCT ipaddr) as ipcount, count(c.id) as msgcount FROM ' . sql_table('plugin_clap')
+ . " LEFT OUTER JOIN " . sql_table('plugin_clap_comment') . " c USING( id ) "
+ . " WHERE `timestamp` between '%s' and '%s' "
+ . " GROUP BY hour ORDER BY count"
+ , mysql_real_escape_string( $fromDate )
+ , mysql_real_escape_string( $toDate )
+ );
+ $res = sql_query($query);
+
+ $param['hours']['label'] = range(0, 24);
+ $param['hours']['count'] = array_fill(0, 24, 0);
+ $param['hours']['ipcount'] = array_fill(0, 24, 0);
+ $param['hours']['msgcount'] = array_fill(0, 24, 0);
+ $param['hours']['length'] = 24;
+ while( $row = mysql_fetch_array($res) ){
+ $idx = $row['hour'];
+ $param['hours']['count'][$idx] = $row['count'];
+ $param['hours']['ipcount'][$idx] = $row['ipcount'];
+ $param['hours']['msgcount'][$idx] = $row['msgcount'];
+ $param['hours']['max'] = $row['count'];
+ }
+
+ // convert
+ mb_convert_variables('UTF-8', _CHARSET, $param);
+ echo Jsphon::encode($param);
+ }
+
+ function doClap($key){
+ global $member;
+
+ // spam check
+ $limit = !$key || $this->_spamcheck( requestVar('comment') . "\n" . requestVar('mail_or_url'). "\n" . requestVar('user') );
+ if($limit){
+ $this->showThanks($key, true);
+ return;
+ }
+
+ list($maxcount, $lifetime) = explode('/', $this->getOption('antispam_limit'), 2);
+ if( !(is_numeric($maxcount) && is_numeric($lifetime)) ){
+ $maxcount = 10;
+ $lifetime = 86400;
+ }
+
+ // count
+ $query = sprintf('SELECT count(id) as result FROM ' . sql_table('plugin_clap')
+ . " where ipaddr = '%s' and timestamp > DATE_SUB(NOW(),INTERVAL %s SECOND);"
+ , mysql_real_escape_string( serverVar('REMOTE_ADDR') )
+ , mysql_real_escape_string( intval($lifetime) )
+ );
+ $count = quickQuery($query);
+
+ if( (!$member->isLoggedIn()) && $count < $maxcount ){
+ $query = sprintf('INSERT INTO ' . sql_table('plugin_clap')
+ . '( itemkey, timestamp, ipaddr ) '
+ . "values('%s', NOW(), '%s')"
+ , mysql_real_escape_string( $key )
+ , mysql_real_escape_string( serverVar('REMOTE_ADDR') )
+ );
+ sql_query($query);
+
+ if( trim(requestVar('comment')) ){
+ $query = sprintf('INSERT INTO ' . sql_table('plugin_clap_comment')
+ . '( id, user, mail_or_url, comment ) '
+ . "values(LAST_INSERT_ID(), '%s', '%s', '%s')"
+ , mysql_real_escape_string( trim(requestVar('user')))
+ , mysql_real_escape_string( trim(requestVar('mail_or_url')))
+ , mysql_real_escape_string( trim(requestVar('comment')))
+ );
+ sql_query($query);
+ }
+ }
+ if( $count >= $maxcount ){
+ $limit = true;
+ }
+
+ // thanks page
+ $this->showThanks($key, $limit);
+
+ // send mail
+ if( ($this->getOption('commentedOnly') == 'yes') && (!trim(requestVar('comment'))) ){
+ return;
+ }
+
+ if(!$limit){
+ $vars = array(
+ 'user' => requestVar('user'),
+ 'comment' => requestVar('comment'),
+ 'mail_or_url' => requestVar('mail_or_url'),
+ 'key' => $key,
+ 'ipaddr' => serverVar('REMOTE_ADDR'),
+ 'hostname' => gethostbyaddr(serverVar('REMOTE_ADDR')),
+ 'useragent' => serverVar('HTTP_USER_AGENT'),
+ 'referer' => serverVar('HTTP_REFERER'),
+ );
+ $this->_notify($vars);
+ }
+ }
+
+ function showThanks($key, $limit, $contentid = null){
+ global $blog, $CONF, $manager;
+ $cat = $this->getCatnameByKey($key);
+ $content = null;
+ $thanksids = array();
+
+ if( !$contentid ){
+ // random fetch
+ $query = sprintf('SELECT thanksid FROM ' . sql_table('plugin_clap_thanks_category')
+ . " WHERE category = '%s'"
+ , mysql_real_escape_string( $cat )
+ );
+ $res = sql_query($query);
+ while( $row = mysql_fetch_array($res) ){
+ $thanksids[] = $row['thanksid'];
+ }
+ if($thanksids){
+ shuffle($thanksids);
+ $contentid = array_pop($thanksids);
+ }
+ }
+
+ $content = $this->getThanksMsg($contentid);
+ if( !$content ){
+ while( $contentid = array_pop($thanksids) ){
+ $content = $this->getThanksMsg($contentid);
+ if( $content ){
+ $this->_correctBrokenThanksCategory();
+ break;
+ }
+ }
+
+ if( !$content ){
+ $content['comment'] = NP_CLAP_NOCONTENT;
+ }
+ }
+
+ $returnurl = requestVar('returnurl') ? trim(requestVar('returnurl')) : serverVar('HTTP_REFERER');
+ if( ! $returnurl ){
+ if ($blog) {
+ $b =& $blog;
+ } else {
+ $b =& $manager->getBlog($CONF['DefaultBlog']);
+ }
+ $returnurl = $b->getURL();
+ }
+
+ $vars = array(
+ 'charset' => _CHARSET,
+ 'img' => ($content['image']) ? $content['image'] : '',
+ 'text' => $content['comment'],
+ 'returnurl' => htmlspecialchars($returnurl, ENT_QUOTES),
+ 'actionurl' => $this->getActionUrl($key, false),
+ 'key' => htmlspecialchars($key, ENT_QUOTES),
+ 'type' => ($key == 'preview') ? 'preview' : 'clap',
+ 'contentid' => $content['id'],
+ );
+
+ $tpl = $this->_getTemplateEngine();
+ if(!$limit)
+ $page = $tpl->fetch('thanks', 'clap_'.$cat)
+ or $page = $tpl->fetch('thanks', strtolower(__CLASS__));
+ else
+ $page = $tpl->fetch('forbidden', 'clap_'.$cat)
+ or $page = $tpl->fetch('forbidden', strtolower(__CLASS__));
+
+ echo $tpl->fill($page, $vars, false);
+ }
+
+ function getCatnameByKey($key){
+ $dir = '';
+ if( is_numeric($key) ){
+ $dir .= $this->getCategoryIDFromItemID($key);
+ } else {
+ $dir .= preg_replace('/[^A-Za-z0-9_.-]+/','', $key);
+ }
+ return $dir;
+ }
+
+ function getCategoryIDFromItemID($itemid) {
+ return quickQuery('SELECT icat as result FROM ' . sql_table('item') . ' WHERE inumber=' . intval($itemid) );
+ }
+
+ function showPreview($id){
+ $this->showThanks('preview', false, $id);
+ }
+
+ function showButton($key){
+ $tpl = $this->_getTemplateEngine();
+ $vars = array(
+ 'actionurl' => $this->getActionUrl($key),
+ 'actionurl_wo_key' => $this->getActionUrl($key,true,false),
+ 'key' => htmlspecialchars($key, ENT_QUOTES),
+ 'count' => $this->getCount($key),
+ );
+ $button = $tpl->fetch('button', strtolower(__CLASS__));
+ echo $tpl->fill($button, $vars, false);
+ }
+
+ function getCount($key){
+ $query = sprintf('SELECT count(id) as result FROM ' . sql_table('plugin_clap')
+ . " where itemkey = '%s'"
+ , mysql_real_escape_string( $key )
+ );
+ return quickQuery($query);
+ }
+
+ function getBloglist(){
+ $query = 'SELECT bnumber, bname FROM ' . sql_table('blog');
+ $res = sql_query($query);
+ $list = array();
+ while( $row = mysql_fetch_array($res) ){
+ list($blogid, $blogname) = $row;
+ $list[$blogid] = $blogname;
+ }
+ return $list;
+ }
+
+ function getOverview($blog = null, $offset = '0', $itemperpage = '20'){
+ if(! $blog ){
+ $query = sprintf('SELECT itemkey as `key`, ititle as title, inumber as itemid, iblog as blog, count(id) as count FROM ' . sql_table('plugin_clap') . ' left outer join ' . sql_table('item')
+ . " on itemkey = inumber group by `key`, title, itemid order by count DESC limit %s,%s"
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ } elseif( $blog == 'global' ){
+ $query = sprintf('SELECT itemkey as `key`, ititle as title, inumber as itemid, iblog as blog, count(id) as count FROM ' . sql_table('plugin_clap') . ' left outer join ' . sql_table('item')
+ . " on itemkey = inumber where inumber is null group by `key`, title, itemid order by blog, count DESC limit %s,%s"
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ } else {
+ $query = sprintf('SELECT itemkey as `key`, ititle as title, inumber as itemid, iblog as blog, count(id) as count FROM ' . sql_table('plugin_clap') . ' left outer join ' . sql_table('item')
+ . " on itemkey = inumber where iblog = %s group by `key`, title, itemid order by blog, count DESC limit %s,%s"
+ , mysql_real_escape_string( intval($blog) )
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ }
+ return sql_query($query);
+ }
+
+ function getDetail($key, $offset = '0', $itemperpage = '20'){
+ $query = sprintf('SELECT c.id, `timestamp`, ipaddr, user, mail_or_url, comment FROM ' . sql_table('plugin_clap') . ' as c left outer join ' . sql_table('plugin_clap_comment')
+ . " as m on c.id = m.id where itemkey = '%s' order by `timestamp` DESC limit %s,%s"
+ , mysql_real_escape_string( $key )
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ return sql_query($query);
+ }
+
+ function deleteClap($id){
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap')
+ . " where id = %s limit 1"
+ , mysql_real_escape_string( intval($id) )
+ );
+ sql_query($query);
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap_comment')
+ . " where id = %s limit 1"
+ , mysql_real_escape_string( intval($id) )
+ );
+ sql_query($query);
+ }
+
+ function getMessagelist($offset = '0', $itemperpage = '20'){
+ $query = sprintf('SELECT itemkey as `key`,`timestamp`, ipaddr, user, mail_or_url, comment FROM ' . sql_table('plugin_clap') . ' as c, ' . sql_table('plugin_clap_comment')
+ . " as m where c.id = m.id order by `timestamp` DESC limit %s,%s"
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ return sql_query($query);
+ }
+ \r function getThanksMsgList($offset = '0', $itemperpage = '20'){
+ $query = sprintf('SELECT * FROM ' . sql_table('plugin_clap_thanks')
+ . " order by id DESC limit %s,%s"
+ , mysql_real_escape_string( intval($offset) )
+ , mysql_real_escape_string( intval($itemperpage) )
+ );
+ return sql_query($query);
+ }
+
+ function getAssociatedCategoriesByThanksId($thanksid){
+ $query = sprintf('SELECT category FROM ' . sql_table('plugin_clap_thanks_category')
+ . " WHERE thanksid = %s"
+ , mysql_real_escape_string( intval($thanksid) )
+ );
+ $res = sql_query($query);
+
+ $list = array();
+ while( $row = mysql_fetch_assoc($res) ){
+ $list[] = $row['category'];
+ }
+ return $list;
+ }
+ \r function getThanksMsg($id){
+ $query = sprintf('SELECT * FROM ' . sql_table('plugin_clap_thanks')
+ . " where id = %s limit 1"
+ , mysql_real_escape_string( intval($id) )
+ );
+ $res = sql_query($query);
+ return mysql_fetch_assoc($res);
+ }
+
+ function deleteThanksMsg($id){
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap_thanks')
+ . " where id = %s limit 1"
+ , mysql_real_escape_string( intval($id) )
+ );
+ sql_query($query);
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap_thanks_category')
+ . " where thanksid = %s"
+ , mysql_real_escape_string( intval($id) )
+ );
+ sql_query($query);
+ }
+
+ function setThanksMsg($msg){
+ if( $msg['id'] == 'new'){ \r $query = sprintf('INSERT INTO ' . sql_table('plugin_clap_thanks')
+ . ' ( image, comment ) '
+ . " values('%s', '%s')"
+ , mysql_real_escape_string( $msg['image'] )
+ , mysql_real_escape_string( $msg['comment'] )
+ );
+ } else {
+ $query = sprintf('UPDATE ' . sql_table('plugin_clap_thanks')
+ . " SET image = '%s', comment = '%s' where id = %s limit 1"
+ , mysql_real_escape_string( $msg['image'] )
+ , mysql_real_escape_string( $msg['comment'] )
+ , mysql_real_escape_string( intval($msg['id']) )
+ );
+ }
+ sql_query($query);
+ }
+
+ function setAssociatedCategories($thanksid, $assoc, $assoc_etc = ''){
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap_thanks_category')
+ . " where thanksid = %s"
+ , mysql_real_escape_string( intval($thanksid) )
+ );
+ sql_query($query);
+
+ $etc = explode(',', $assoc_etc);
+ if($etc) $assoc = array_merge($assoc, $etc);
+
+ $assoc = array_map("trim", $assoc);
+ $assoc = array_filter($assoc);
+
+ foreach ($assoc as $c) {
+ $query = sprintf('INSERT INTO ' . sql_table('plugin_clap_thanks_category')
+ . ' ( category, thanksid ) '
+ . " values('%s', %s)"
+ , mysql_real_escape_string( $c )
+ , mysql_real_escape_string( intval($thanksid) )
+ );
+ sql_query($query);
+ }
+ }
+
+ function showList($bloglist = null, $count = '10', $order = 'DESC', $numOfDays = false){
+
+ $where = '';
+ if( $numOfDays){
+ $where .= sprintf(' and `timestamp` > DATE_ADD(now(), INTERVAL -%s DAY)'
+ , mysql_real_escape_string( intval($numOfDays) )
+ );
+ }
+ if( $bloglist ){
+ $where .= sprintf(" and iblog in (%s) and itemkey <> '%s'"
+ , mysql_real_escape_string( $bloglist )
+ , mysql_real_escape_string( NP_CLAP_GLOBALKEY )
+ );
+ } else {
+ $where .= sprintf(" and itemkey <> '%s'"
+ , mysql_real_escape_string( NP_CLAP_GLOBALKEY )
+ );
+ }
+
+ $query = sprintf('SELECT itemkey as `key`, ititle as title, inumber as itemid, count(id) as count FROM ' . sql_table('plugin_clap') . ', ' . sql_table('item')
+ . " where itemkey = inumber %s group by `key`, title, itemid order by count %s limit %s"
+ , $where
+ , mysql_real_escape_string( $order )
+ , mysql_real_escape_string( intval($count) )
+ );
+ $res = sql_query($query);
+
+ $tpl = $this->_getTemplateEngine();
+ echo $tpl->fetch('list_header', strtolower(__CLASS__));
+
+ $item = $tpl->fetch('list_item', strtolower(__CLASS__));
+ while( $row = mysql_fetch_assoc($res) ){
+ $row['title'] = shorten(strip_tags($row['title']),30,'...');
+ $row['itemlink'] = createItemLink($row['itemid'], '');
+ echo $tpl->fill($item, $row, false);
+ }
+
+ echo $tpl->fetch('list_footer', strtolower(__CLASS__));
+ }
+
+ function getActionUrl($key, $withQuery = true, $withKey = true){
+ global $CONF;
+ $url = $CONF['ActionURL'];
+ if($withQuery){
+ $url .= '?action=plugin&name=Clap&type=clap';
+ if($withKey){
+ $url .= '&key='.htmlspecialchars($key, ENT_QUOTES);
+ }
+ }
+ return $url;
+ }
+
+ function _getTemplateEngine(){
+ if( ! $this->templateEngine )
+ $this->templateEngine =& new cles_Template(dirname(__FILE__).'/clap/template');
+ return $this->templateEngine;
+ }
+
+ function _notify($vars){
+ global $CONF, $DIR_LIBS, $member;
+ $destAddress = trim($this->getOption('mailaddr'));
+ if ( ! $destAddress ) return;
+
+ if (!class_exists('notification'))
+ include($DIR_LIBS . 'NOTIFICATION.php');
+
+ $tpl = $this->_getTemplateEngine();
+
+ $msg = $tpl->fetch('mail_body', strtolower(__CLASS__), 'txt');
+ $msg = $tpl->fill($msg, $vars, null);
+
+ $subject = $tpl->fetch('mail_subject', strtolower(__CLASS__), 'txt');
+ $subject = $tpl->fill($subject, $vars, false);
+
+ $notify =& new NOTIFICATION($destAddress);
+ $notify->notify($subject, $msg , $CONF['AdminEmail']);
+ }
+
+ function _spamcheck($text = ''){
+ if($this->getOption('antispam_check') == "yes") {
+ global $itemid, $manager;
+
+ $spamcheck = array (
+ 'type' => 'clap',
+ 'body' => $text,
+ 'author' => '',
+ 'url' => '',
+ 'id' => $itemid,
+ 'return' => true,
+ 'live' => true,
+
+ /* Backwards compatibility with SpamCheck API 1*/
+ 'data' => $text,
+ 'ipblock' => true,
+ );
+
+ $manager->notify('SpamCheck', array ('spamcheck' => & $spamcheck));
+ if (isset($spamcheck['result']) && $spamcheck['result'] == true){
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function _correctBrokenThanksCategory(){
+ $query = 'SELECT tc.* FROM ' . sql_table('plugin_clap_thanks_category') . ' tc'
+ . ' LEFT OUTER JOIN ' . sql_table('plugin_clap_thanks') . ' t ON tc.thanksid = t.id'
+ . ' WHERE id IS NULL';
+ $res = sql_query($query);
+
+ $count = 0;
+ while( $row = mysql_fetch_assoc($res) ){
+ $query = sprintf('DELETE FROM ' . sql_table('plugin_clap_thanks_category')
+ . " WHERE category = %s and thanksid = %s limit 1"
+ , mysql_real_escape_string( intval($row['category']) )
+ , mysql_real_escape_string( intval($row['thanksid']) )
+ );
+ sql_query($query);
+ $count++;
+ }
+
+ $msg = sprintf(NP_CLAP_corrected, $count);
+ $this->_warn($msg);
+ return $msg;
+ }
+
+ function _info($msg) {
+ if ($this->getOption('debug') == 'yes') {
+ ACTIONLOG :: add(INFO, 'Clap: '.$msg);
+ }
+ }
+
+ function _warn($msg) {
+ ACTIONLOG :: add(WARNING, 'Clap: '.$msg);
+ }
+}
+
--- /dev/null
+.chart {
+ font: menu;
+ background: white;
+ color: black;
+ overflow: hidden;
+ position: relative;
+ line-height: normal;
+ font-size: x-small;
+}
+
+.chart .legend {
+ float: left;
+ border: 1px solid black;
+ background: white;
+ padding: 5px 10px 5px 10px;
+ line-height: normal;
+}
+
+.chart .legend ul {
+ margin: 0px;
+ padding: 0px 0px 0px 20px;
+ line-height: normal;
+}
+
+.chart .legend ul li {
+ list-style: square;
+ line-height: normal;
+}
+
+.chart .legend ul li span {
+ color: black;
+ line-height: normal;
+}
--- /dev/null
+/*----------------------------------------------------------------------------\
+| Chart 1.0 |
+| Canvas Chart Painter |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+|-----------------------------------------------------------------------------|
+| Canvas implementation of the chart painter API. A canvas element is used to |
+| draw the chart, html elements are used for the legend and axis labels as, |
+| at the time being, there is no text support in canvas. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2006 Emil A Eklund |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| 2006-01-03 | Work started. |
+| 2006-01-05 | Added legend and axis labels. Changed the painter api slightly |
+| | to allow two-stage initialization (required for ie/canvas) and |
+| | added legend/axis related methods. Also updated bar chart type |
+| | and added a few options, mostly related to bar charts. |
+| 2006-01-07 | Updated chart size calculations to take legend and axis labels |
+| | into consideration. Split painter implementations to separate |
+| | files. |
+| 2006-04-16 | Updated to use the ExplorerCanvas ie emulation layer instead |
+| | of the, now deprecated, IECanvas one. |
+|-----------------------------------------------------------------------------|
+| Created 2006-01-03 | All changes are in the log above. | Updated 2006-04-16 |
+\----------------------------------------------------------------------------*/
+
+function CanvasChartPainterFactory() {
+ return new CanvasChartPainter();
+}
+
+
+function CanvasChartPainter() {
+ this.base = AbstractChartPainter;
+};
+
+
+CanvasChartPainter.prototype = new AbstractChartPainter;
+
+
+CanvasChartPainter.prototype.create = function(el) {
+ while (el.firstChild) { el.removeChild(el.lastChild); }
+
+ this.el = el;
+ this.w = el.clientWidth;
+ this.h = el.clientHeight;
+
+ this.canvas = document.createElement('canvas');
+ this.canvas.width = this.w;
+ this.canvas.height = this.h;
+ this.canvas.style.width = this.w + 'px';
+ this.canvas.style.height = this.h + 'px';
+
+ el.appendChild(this.canvas);
+
+ /* Init explorercanvas emulation for IE */
+ if ((!this.canvas.getContext) && (typeof G_vmlCanvasManager != "undefined")) {
+ this.canvas = G_vmlCanvasManager.initElement(this.canvas);
+ }
+};
+
+
+CanvasChartPainter.prototype.init = function(xlen, ymin, ymax, xgd, ygd, bLegendLabels) {
+ this.ctx = this.canvas.getContext('2d');
+
+ this.chartx = 0;
+ this.chartw = this.w;
+ this.charth = this.h;
+ this.charty = 0;
+
+ this.xlen = xlen;
+ this.ymin = ymin;
+ this.ymax = ymax;
+ this.xgd = xgd;
+ this.ygd = ygd;
+
+ this.calc(this.chartw, this.charth, xlen, ymin, ymax, xgd, ygd);
+};
+
+
+CanvasChartPainter.prototype.drawLegend = function(series) {
+ var legend, list, item, label;
+
+ legend = document.createElement('div');
+ legend.className = 'legend';
+ legend.style.position = 'absolute';
+ list = document.createElement('ul');
+
+ for (i = 0; i < series.length; i++) {
+ item = document.createElement('li');
+ item.style.color = series[i].color;
+ label = document.createElement('span');
+ label.appendChild(document.createTextNode(series[i].label));
+ label.style.color = 'black';
+ item.appendChild(label);
+ list.appendChild(item);
+ }
+ legend.appendChild(list);
+ this.el.appendChild(legend);
+ legend.style.right = '0px';
+ legend.style.top = this.charty + (this.charth / 2) - (legend.offsetHeight / 2) + 'px';
+ this.legend = legend;
+
+ /* Recalculate chart width and position based on labels and legend */
+ this.chartw = this.w - (this.legend.offsetWidth + 5);
+
+ this.calc(this.chartw, this.charth, this.xlen, this.ymin, this.ymax, this.xgd, this.ygd);
+};
+
+
+CanvasChartPainter.prototype.drawVerticalAxis = function(ygd, precision) {
+ var axis, item, step, y, ty, n, yoffset, value, multiplier, w, items, pos;
+
+ /* Calculate step size and rounding precision */
+ multiplier = Math.pow(10, precision);
+ step = this.range / (ygd - 1);
+
+ /* Create container */
+ axis = document.createElement('div');
+ axis.style.position = 'absolute';
+ axis.style.left = '0px';
+ axis.style.top = '0px';
+ axis.style.textAlign = 'right';
+ this.el.appendChild(axis);
+
+ /* Draw labels and points */
+ this.ctx.fillStyle = 'black';
+ w = 0;
+ items = new Array();
+ for (n = 0, i = this.ymax; (i > this.ymin) && (n < ygd - 1); i -= step, n++) {
+ item = document.createElement('span');
+ value = parseInt(i * multiplier) / multiplier;
+ item.appendChild(document.createTextNode(value));
+ axis.appendChild(item);
+ items.push([i, item]);
+ if (item.offsetWidth > w) { w = item.offsetWidth; }
+ }
+
+ /* Draw last label and point (lower left corner of chart) */
+ item = document.createElement('span');
+ item.appendChild(document.createTextNode(this.ymin));
+ axis.appendChild(item);
+ items.push([this.ymin, item]);
+ if (item.offsetWidth > w) { w = item.offsetWidth; }
+
+ /* Set width of container to width of widest label */
+ axis.style.width = w + 'px';
+
+ /* Recalculate chart width and position based on labels and legend */
+ this.chartx = w + 5;
+ this.charty = item.offsetHeight / 2;
+ this.charth = this.h - ((item.offsetHeight * 1.5) + 5);
+ this.chartw = this.w - (((this.legend)?this.legend.offsetWidth:0) + w + 10);
+ this.calc(this.chartw, this.charth, this.xlen, this.ymin, this.ymax, this.xgd, this.ygd);
+
+ /* Position labels on the axis */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+ for (i = 0; i < items.length; i++) {
+ item = items[i][1];
+ pos = items[i][0];
+ if (pos == this.ymin) { y = this.charty + this.charth - 1; }
+ else { y = this.charty + (this.charth - (pos / n) + yoffset); }
+ this.ctx.fillRect(this.chartx - 5, y, 5, 1);
+ ty = y - (item.offsetHeight/2);
+ item.style.position = 'absolute';
+ item.style.right = '0px';
+ item.style.top = ty + 'px';
+} };
+
+
+CanvasChartPainter.prototype.drawHorizontalAxis = function(xlen, labels, xgd, precision) {
+ var axis, item, step, x, tx, n, multiplier;
+
+ /* Calculate offset, step size and rounding precision */
+ multiplier = Math.pow(10, precision);
+ n = this.chartw / (xgd - 1);
+
+ /* Create container */
+ axis = document.createElement('div');
+ axis.style.position = 'absolute';
+ axis.style.left = '0px';
+ axis.style.top = (this.charty + this.charth + 5) + 'px';
+ axis.style.width = this.w + 'px';
+ this.el.appendChild(axis);
+
+ /* Draw labels and points */
+ this.ctx.fillStyle = 'black';
+ for (i = 0; i < xgd; i++) {
+ item = document.createElement('span');
+ item.appendChild(document.createTextNode(labels[i]));
+ axis.appendChild(item);
+ x = this.chartx + (n * i);
+ tx = x - (item.offsetWidth/2)
+ item.style.position = 'absolute';
+ item.style.left = tx + 'px';
+ item.style.top = '0px';
+ this.ctx.fillRect(x, this.charty + this.charth, 1, 5);
+} };
+
+
+CanvasChartPainter.prototype.drawAxis = function() {
+ this.ctx.fillStyle = 'black';
+ this.ctx.fillRect(this.chartx, this.charty, 1, this.charth-1);
+ this.ctx.fillRect(this.chartx, this.charty + this.charth - 1, this.chartw+1, 1);
+};
+
+
+CanvasChartPainter.prototype.drawBackground = function() {
+ this.ctx.fillStyle = 'white';
+ this.ctx.fillRect(0, 0, this.w, this.h);
+};
+
+
+CanvasChartPainter.prototype.drawChart = function() {
+ this.ctx.fillStyle = 'silver';
+ if (this.xgrid) {
+ for (i = this.xgrid; i < this.chartw; i += this.xgrid) {
+ this.ctx.fillRect(this.chartx + i, this.charty, 1, this.charth-1);
+ } }
+ if (this.ygrid) {
+ for (i = this.charth - this.ygrid; i > 0; i -= this.ygrid) {
+ this.ctx.fillRect(this.chartx + 1, this.charty + i, this.chartw, 1);
+} } };
+
+
+CanvasChartPainter.prototype.drawArea = function(color, values) {
+ var i, len, x, y, n, yoffset;
+
+ /* Determine distance between points and offset */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+ this.ctx.fillStyle = color;
+
+ /* Begin line in lower left corner */
+ x = this.chartx + 1;
+ this.ctx.beginPath();
+ this.ctx.moveTo(x, this.charty + this.charth - 1);
+
+ /* Determine position of first point and draw it */
+ y = this.charty + this.charth - (values[0] / n) + yoffset;
+ this.ctx.lineTo(x, y);
+
+ /* Draw lines to succeeding points */
+ for (i = 1; i < len; i++) {
+ y = this.charty + this.charth - (values[i] / n) + yoffset;
+ x += this.xstep;
+ this.ctx.lineTo(x, y);
+ }
+
+ /* Close path and fill it */
+ this.ctx.lineTo(x, this.charty + this.charth - 1);
+ this.ctx.closePath();
+ this.ctx.fill();
+} };
+
+
+CanvasChartPainter.prototype.drawLine = function(color, values) {
+ var i, len, x, y, n, yoffset;
+
+ /* Determine distance between points and offset */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+ this.ctx.lineWidth = 1;
+ this.ctx.strokeStyle = color;
+
+ /* Determine position of first point and draw it */
+ x = this.chartx + 1;
+ y = this.charty + this.charth - (values[0] / n) + yoffset;
+ this.ctx.beginPath();
+ this.ctx.moveTo(x, y);
+
+ /* Draw lines to succeeding points */
+ for (i = 1; i < len; i++) {
+ y = this.charty + this.charth - (values[i] / n) + yoffset;
+ x += this.xstep;
+ this.ctx.lineTo(x, y);
+ }
+
+ /* Stroke path */
+ this.ctx.stroke();
+} };
+
+
+CanvasChartPainter.prototype.drawBars = function(color, values, xlen, xoffset, width) {
+ var i, len, x, y, n, yoffset;
+
+ /* Determine distance between points and offset */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len > xlen) { len = xlen; }
+ if (len) {
+ this.ctx.fillStyle = color;
+
+ /* Determine position of each bar and draw it */
+ x = this.chartx + xoffset + 1;
+ for (i = 0; i < len; i++) {
+ y = this.charty + this.charth - (values[i] / n) + yoffset;
+
+ this.ctx.beginPath();
+ this.ctx.moveTo(x, this.charty + this.charth-1);
+ this.ctx.lineTo(x, y );
+ this.ctx.lineTo(x+width, y);
+ this.ctx.lineTo(x+width, this.charty + this.charth-1);
+ this.ctx.closePath();
+ this.ctx.fill();
+
+ x += this.xstep;
+} } };
--- /dev/null
+/*----------------------------------------------------------------------------\
+| Chart 1.0 |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+|-----------------------------------------------------------------------------|
+| Client side chart painter, supports line, area and bar charts and stacking, |
+| uses Canvas (mozilla, safari, opera) or SVG (mozilla, opera) for drawing. |
+| Can be used with IECanvas to allow the canvas painter to be used in IE. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2006 Emil A Eklund |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| Dependencies: canvaschartpainter.js - Canvas chart painter implementation. |
+| canvaschart.css - Canvas chart painter styles. |
+| or: svgchartpainter.js - SVG chart painter implementation. |
+|-----------------------------------------------------------------------------|
+| 2006-01-03 | Work started. |
+| 2006-01-05 | Added legend and axis labels. Changed the painter api slightly |
+| | to allow two-stage initialization (required for ie/canvas) and |
+| | added legend/axis related methods. Also updated bar chart type |
+| | and added a few options, mostly related to bar charts. |
+| 2006-01-07 | Updated chart size calculations to take legend and axis labels |
+| | into consideration. Split painter implementations to separate |
+| | files. |
+| 2006-01-10 | Fixed bug in automatic range calculation. Also added explicit |
+| | cast to float for stacked series. |
+| 2006-04-16 | Updated constructor to set painter factory based on available |
+| | and supported implementations. |
+|-----------------------------------------------------------------------------|
+| Created 2006-01-03 | All changes are in the log above. | Updated 2006-04-16 |
+\----------------------------------------------------------------------------*/
+
+var CHART_LINE = 1;
+var CHART_AREA = 2;
+var CHART_BAR = 3;
+var CHART_STACKED = 4;
+
+/*----------------------------------------------------------------------------\
+| Chart |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Chart class, control class that's used to represent a chart. Uses a painter |
+| class for the actual drawing. This is the only class that should be used |
+| directly, the other ones are internal. |
+\----------------------------------------------------------------------------*/
+
+function Chart(el) {
+ this._cont = el;
+ this._yMin = null;
+ this._yMax = null;
+ this._xGridDensity = 0;
+ this._yGridDensity = 0;
+ this._flags = 0;
+ this._series = new Array();
+ this._labelPrecision = 0;
+ this._horizontalLabels = new Array();
+ this._barWidth = 10;
+ this._barDistance = 2;
+ this._bars = 0;
+ this._showLegend = true;
+
+ /*
+ * Determine painter implementation to use based on what's available and
+ * supported. CanvasChartPainter is the prefered one, JsGraphicsChartPainter
+ * the fallback one as it works in pretty much any browser. The
+ * SVGChartPainter implementation one will only be used if set explicitly as
+ * it's not up to pair with the other ones.
+ */
+ if ((typeof CanvasChartPainterFactory != 'undefined') && (window.CanvasRenderingContext2D)) {
+ this._painterFactory = CanvasChartPainterFactory;
+ }
+ else if (typeof JsGraphicsChartPainterFactory != 'undefined') {
+ this._painterFactory = JsGraphicsChartPainterFactory;
+ }
+ else { this._painterFactory = null; }
+}
+
+
+Chart.prototype.setPainterFactory = function(f) {
+ this._painterFactory = f;
+};
+
+
+Chart.prototype.setVerticalRange = function(min, max) {
+ this._yMin = min;
+ this._yMax = max;
+};
+
+
+Chart.prototype.setLabelPrecision = function(precision) {
+ this._labelPrecision = precision;
+};
+
+
+Chart.prototype.setShowLegend = function(b) {
+ this._showLegend = b;
+};
+
+
+Chart.prototype.setGridDensity = function(horizontal, vertical) {
+ this._xGridDensity = horizontal;
+ this._yGridDensity = vertical;
+};
+
+
+Chart.prototype.setHorizontalLabels = function(labels) {
+ this._horizontalLabels = labels;
+};
+
+
+Chart.prototype.setDefaultType = function(flags) {
+ this._flags = flags;
+};
+
+
+Chart.prototype.setBarWidth = function(width) {
+ this._barWidth = width;
+};
+
+
+Chart.prototype.setBarDistance = function(distance) {
+ this._barDistance = distance;
+};
+
+
+Chart.prototype.add = function(label, color, values, flags) {
+ var o, offset;
+
+ if (!flags) { flags = this._flags; }
+ if ((flags & CHART_BAR) == CHART_BAR) { offset = this._barDistance + this._bars * (this._barWidth + this._barDistance); this._bars++; }
+ else { offset = 0; }
+ o = new ChartSeries(label, color, values, flags, offset);
+
+ this._series.push(o);
+};
+
+
+Chart.prototype.draw = function() {
+ var painter, i, o, o2, len, xlen, ymin, ymax, series, type, self, bLabels;
+
+ if (!this._painterFactory) { return; }
+
+ /* Initialize */
+ series = new Array();
+ stackedSeries = new Array();
+ xlen = 0;
+ ymin = this._yMin;
+ ymax = this._yMax;
+
+ /* Separate stacked series (as they need processing). */
+ for (i = 0; i < this._series.length; i++) {
+ o = this._series[i]
+ if ((o.flags & CHART_STACKED) == CHART_STACKED) { series.push(o); }
+ }
+
+ /* Calculate values for stacked series */
+ for (i = series.length - 2; i >= 0; i--) {
+ o = series[i].values;
+ o2 = series[i+1].values;
+ len = (o2.length > o.length)?o2.length:o.length;
+ for (j = 0; j < len; j++) {
+ if ((o[j]) && (!o2[j])) { continue; }
+ if ((!o[j]) && (o2[j])) { o[j] = o2[j]; }
+ else { o[j] = parseInt(o[j]) + parseFloat(o2[j]); }
+ } }
+
+ /* Append non-stacked series to list */
+ for (i = 0; i < this._series.length; i++) {
+ o = this._series[i]
+ if ((o.flags & CHART_STACKED) != CHART_STACKED) { series.push(o); }
+ }
+
+ /* Determine maximum number of values, ymin and ymax */
+ for (i = 0; i < series.length; i++) {
+ o = series[i]
+ if (o.values.length > xlen) { xlen = o.values.length; }
+ for (j = 0; j < o.values.length; j++) {
+ if ((o.values[j] < ymin) || (ymin == null)) { ymin = o.values[j]; }
+ if (o.values[j] > ymax) { ymax = o.values[j]; }
+ } }
+
+ /*
+ * For bar only charts the number of charts is the same as the length of the
+ * longest series, for others combinations it's one less. Compensate for that
+ * for bar only charts.
+ */
+ if (this._series.length == this._bars) {
+ xlen++;
+ this._xGridDensity++;
+ }
+
+ /*
+ * Determine whatever or not to show the legend and axis labels
+ * Requires density and labels to be set.
+ */
+ bLabels = ((this._xGridDensity) && (this._yGridDensity) && (this._horizontalLabels.length >= this._xGridDensity));
+
+ /* Create painter object */
+ painter = this._painterFactory();
+ painter.create(this._cont);
+
+ /* Initialize painter object */
+ painter.init(xlen, ymin, ymax, this._xGridDensity, this._yGridDensity, bLabels);
+
+ /* Draw chart */
+ painter.drawBackground();
+
+ /*
+ * If labels and grid density where specified, draw legend and labels.
+ * It's drawn prior to the chart as the size of the legend and labels
+ * affects the size of the chart area.
+ */
+ if (this._showLegend) { painter.drawLegend(series); }
+ if (bLabels) {
+ painter.drawVerticalAxis(this._yGridDensity, this._labelPrecision);
+ painter.drawHorizontalAxis(xlen, this._horizontalLabels, this._xGridDensity, this._labelPrecision);
+ }
+
+ /* Draw chart */
+ painter.drawChart();
+
+ /* Draw series */
+ for (i = 0; i < series.length; i++) {
+ type = series[i].flags & ~CHART_STACKED;
+ switch (type) {
+ case CHART_LINE: painter.drawLine(series[i].color, series[i].values); break;
+ case CHART_AREA: painter.drawArea(series[i].color, series[i].values); break;
+ case CHART_BAR: painter.drawBars(series[i].color, series[i].values, xlen-1, series[i].offset, this._barWidth); break;
+ default: ;
+ };
+ }
+
+ /*
+ * Draw axis (after the series since the anti aliasing of the lines may
+ * otherwise be drawn on top of the axis)
+ */
+ painter.drawAxis();
+
+};
+
+
+/*----------------------------------------------------------------------------\
+| ChartSeries |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Internal class for representing a series. |
+\----------------------------------------------------------------------------*/
+
+function ChartSeries(label, color, values, flags, offset) {
+ this.label = label;
+ this.color = color;
+ this.values = values;
+ this.flags = flags;
+ this.offset = offset;
+}
+
+
+/*----------------------------------------------------------------------------\
+| AbstractChartPainter |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Abstract base class defining the painter API. Can not be used directly. |
+\----------------------------------------------------------------------------*/
+
+function AbstractChartPainter() {
+
+};
+
+
+AbstractChartPainter.prototype.calc = function(w, h, xlen, ymin, ymax, xgd, ygd) {
+ this.range = ymax - ymin;
+ this.xstep = w / (xlen - 1);
+ this.xgrid = (xgd)?w / (xgd - 1):0;
+ this.ygrid = (ygd)?h / (ygd - 1):0;
+ this.ymin = ymin;
+ this.ymax = ymax;
+};
+
+
+AbstractChartPainter.prototype.create = function(el) {};
+AbstractChartPainter.prototype.init = function(xlen, ymin, ymax, xgd, ygd, bLabels) {};
+AbstractChartPainter.prototype.drawLegend = function(series) {};
+AbstractChartPainter.prototype.drawVerticalAxis = function(ygd, precision) {};
+AbstractChartPainter.prototype.drawHorizontalAxis = function(xlen, labels, xgd, precision) {};
+AbstractChartPainter.prototype.drawAxis = function() {};
+AbstractChartPainter.prototype.drawBackground = function() {};
+AbstractChartPainter.prototype.drawChart = function() {};
+AbstractChartPainter.prototype.drawArea = function(color, values) {};
+AbstractChartPainter.prototype.drawLine = function(color, values) {};
+AbstractChartPainter.prototype.drawBars = function(color, values, xlen, xoffset, width) {};
--- /dev/null
+function demo() {
+ demo1();
+ demo2();
+ demo3();
+ demo4();
+}
+
+
+function demo1(painterType) {
+ var c = new Chart(document.getElementById('chart'));
+ if (painterType == 'svg') { c.setPainterFactory(SVGChartPainterFactory); }
+ c.setDefaultType(CHART_AREA | CHART_STACKED);
+ c.setGridDensity(10, 10);
+ c.setVerticalRange(-100, 300);
+ c.setHorizontalLabels(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'mon', 'tue', 'wed']);
+ c.setBarWidth(10);
+ c.add('Spam', '#4040FF', [ 5, 10, 20, 10, 40, 52, 68, 70, 70, 60]);
+ c.add('Innocent', '#8080FF', [ 8, 7, 12, 20, 24, 16, 36, 28, 28, 45]);
+ c.add('Missed Spam', '#A5A5FF', [ 8, 7, 12, 20, 24, 16, 36, 36, 18, 5]);
+ c.add('False Positives', '#DEDEFF', [ 1, -20, 3, 2, 1, 4, -80, 12, 8, -10]);
+ c.add('SMTP Rejects', 'red', [45, 54, 65, 150, 280, 120, 86, 65, 74, 12], CHART_LINE);
+ c.add('System Load', '#008800', [-8, -7, -12, -20, -24, -16, -36, -36, -18], CHART_BAR);
+ c.add('Memory Usage', '#009900', [-1, -20, -3, -2, -1, -4, -80, -12, -8], CHART_BAR);
+ c.draw();
+}
+
+function demo2() {
+ var c = new Chart(document.getElementById('chart2'));
+ c.setDefaultType(CHART_AREA | CHART_STACKED);
+ c.setGridDensity(5, 5);
+ c.setVerticalRange(-100, 300);
+ c.setHorizontalLabels(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'mon', 'tue', 'wed']);
+ c.add('Spam', '#4040FF', [ 5, 10, 20, 10, 40, 52, 68, 70, 70, 60]);
+ c.add('Innocent', '#8080FF', [ 8, 7, 12, 20, 24, 16, 36, 28, 28, 45]);
+ c.add('Missed Spam', '#A5A5FF', [ 8, 7, 12, 20, 24, 16, 36, 36, 18, 5]);
+ c.add('False Positives', '#DEDEFF', [ 1, -20, 3, 2, 1, 4, -80, 12, 8, -10]);
+ c.add('SMTP Rejects', 'red', [45, 54, 65, 150, 280, 120, 86, 65, 74, 12], CHART_LINE);
+ c.draw();
+}
+
+function demo3() {
+ var c = new Chart(document.getElementById('chart3'));
+ c.setDefaultType(CHART_LINE);
+ c.setGridDensity(10, 10);
+ c.setHorizontalLabels(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'mon', 'tue', 'wed']);
+ c.setShowLegend(false);
+ c.add('', '#4040FF', [ 5, 10, 20, 10, 40, 52, 68, 70, 70, 60]);
+ c.add('', '#8080FF', [ 8, 7, 12, 20, 24, 16, 36, 28, 28, 45]);
+ c.add('', '#FF8080', [ 3, 12, 5, 18, 20, 13, 28, 36, 18, 5]);
+ c.add('', '#FF00FF', [ 1, 20, 3, 2, 1, 4, 10, 12, 8, 10]);
+ c.draw();
+}
+
+function demo4() {
+ var c = new Chart(document.getElementById('chart4'));
+ c.setDefaultType(CHART_BAR);
+ c.setGridDensity(0, 0);
+ c.setBarWidth(10);
+ c.setBarDistance(1);
+ c.setShowLegend(false);
+ c.add('', 'blue', [6, 3, 9, 6, 1, 3]);
+ c.add('', 'red', [7, 1, 2, 9, 3, 6]);
+ c.add('', 'green', [2, 7, 1, 3, 7, 2]);
+ c.draw();
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// TODO: Patterns
+// TODO: Radial gradient
+// TODO: Clipping paths
+// TODO: Coordsize (still need to support stretching)
+// TODO: Painting mode
+// TODO: Optimize
+// TODO: canvas width/height sets content size in moz, border size in ie
+
+// only add this code if we do not already have a canvas implementation
+if (!window.CanvasRenderingContext2D) {
+
+(function () {
+
+ // alias some functions to make (compiled) code shorter
+ var m = Math;
+ var mr = m.round;
+ var ms = m.sin;
+ var mc = m.cos;
+
+ var G_vmlCanvasManager_ = {
+ init: function (opt_doc) {
+ var doc = opt_doc || document;
+ if (/MSIE/.test(navigator.userAgent) && !window.opera) {
+ var self = this;
+ doc.attachEvent("onreadystatechange", function () {
+ self.init_(doc);
+ });
+ }
+ },
+
+ init_: function (doc, e) {
+ if (doc.readyState == "complete") {
+ // create xmlns
+ if (!doc.namespaces["g_vml_"]) {
+ doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
+ }
+
+ // setup default css
+ var ss = doc.createStyleSheet();
+ ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
+ "text-align:left;}" +
+ "g_vml_\\:*{behavior:url(#default#VML)}";
+
+ // find all canvas elements
+ var els = doc.getElementsByTagName("canvas");
+ for (var i = 0; i < els.length; i++) {
+ if (!els[i].getContext) {
+ this.initElement(els[i]);
+ }
+ }
+ }
+ },
+
+ fixElement_: function (el) {
+ // in IE before version 5.5 we would need to add HTML: to the tag name
+ // but we do not care about IE before version 6
+ var outerHTML = el.outerHTML;
+ var newEl = document.createElement(outerHTML);
+ // if the tag is still open IE has created the children as siblings and
+ // it has also created a tag with the name "/FOO"
+ if (outerHTML.slice(-2) != "/>") {
+ var tagName = "/" + el.tagName;
+ var ns;
+ // remove content
+ while ((ns = el.nextSibling) && ns.tagName != tagName) {
+ ns.removeNode();
+ }
+ // remove the incorrect closing tag
+ if (ns) {
+ ns.removeNode();
+ }
+ }
+ el.parentNode.replaceChild(newEl, el);
+ return newEl;
+ },
+
+ /**
+ * Public initializes a canvas element so that it can be used as canvas
+ * element from now on. This is called automatically before the page is
+ * loaded but if you are creating elements using createElement you need to
+ * make sure this is called on the element.
+ * @param {HTMLElement} el The canvas element to initialize.
+ * @return {HTMLElement} the element that was created.
+ */
+ initElement: function (el) {
+ el = this.fixElement_(el);
+ el.getContext = function () {
+ if (this.context_) {
+ return this.context_;
+ }
+ return this.context_ = new CanvasRenderingContext2D_(this);
+ };
+
+ // do not use inline function because that will leak memory
+ // el.attachEvent('onpropertychange', onPropertyChange)
+ el.attachEvent('onresize', onResize);
+
+ var attrs = el.attributes;
+ if (attrs.width && attrs.width.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setWidth_(attrs.width.nodeValue);
+ el.style.width = attrs.width.nodeValue + "px";
+ }
+ if (attrs.height && attrs.height.specified) {
+ // TODO: use runtimeStyle and coordsize
+ // el.getContext().setHeight_(attrs.height.nodeValue);
+ el.style.height = attrs.height.nodeValue + "px";
+ }
+ //el.getContext().setCoordsize_()
+ return el;
+ }
+ };
+
+ function onPropertyChange(e) {
+ // we need to watch changes to width and height
+ switch (e.propertyName) {
+ case 'width':
+ case 'height':
+ // TODO: coordsize and size
+ break;
+ }
+ }
+
+ function onResize(e) {
+ var el = e.srcElement;
+ if (el.firstChild) {
+ el.firstChild.style.width = el.clientWidth + 'px';
+ el.firstChild.style.height = el.clientHeight + 'px';
+ }
+ }
+
+ G_vmlCanvasManager_.init();
+
+ // precompute "00" to "FF"
+ var dec2hex = [];
+ for (var i = 0; i < 16; i++) {
+ for (var j = 0; j < 16; j++) {
+ dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
+ }
+ }
+
+ function createMatrixIdentity() {
+ return [
+ [1, 0, 0],
+ [0, 1, 0],
+ [0, 0, 1]
+ ];
+ }
+
+ function matrixMultiply(m1, m2) {
+ var result = createMatrixIdentity();
+
+ for (var x = 0; x < 3; x++) {
+ for (var y = 0; y < 3; y++) {
+ var sum = 0;
+
+ for (var z = 0; z < 3; z++) {
+ sum += m1[x][z] * m2[z][y];
+ }
+
+ result[x][y] = sum;
+ }
+ }
+ return result;
+ }
+
+ function copyState(o1, o2) {
+ o2.fillStyle = o1.fillStyle;
+ o2.lineCap = o1.lineCap;
+ o2.lineJoin = o1.lineJoin;
+ o2.lineWidth = o1.lineWidth;
+ o2.miterLimit = o1.miterLimit;
+ o2.shadowBlur = o1.shadowBlur;
+ o2.shadowColor = o1.shadowColor;
+ o2.shadowOffsetX = o1.shadowOffsetX;
+ o2.shadowOffsetY = o1.shadowOffsetY;
+ o2.strokeStyle = o1.strokeStyle;
+ }
+
+ function processStyle(styleString) {
+ var str, alpha = 1;
+
+ styleString = String(styleString);
+ if (styleString.substring(0, 3) == "rgb") {
+ var start = styleString.indexOf("(", 3);
+ var end = styleString.indexOf(")", start + 1);
+ var guts = styleString.substring(start + 1, end).split(",");
+
+ str = "#";
+ for (var i = 0; i < 3; i++) {
+ str += dec2hex[parseInt(guts[i])];
+ }
+
+ if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
+ alpha = guts[3];
+ }
+ } else {
+ str = styleString;
+ }
+
+ return [str, alpha];
+ }
+
+ function processLineCap(lineCap) {
+ switch (lineCap) {
+ case "butt":
+ return "flat";
+ case "round":
+ return "round";
+ case "square":
+ default:
+ return "square";
+ }
+ }
+
+ /**
+ * This class implements CanvasRenderingContext2D interface as described by
+ * the WHATWG.
+ * @param {HTMLElement} surfaceElement The element that the 2D context should
+ * be associated with
+ */
+ function CanvasRenderingContext2D_(surfaceElement) {
+ this.m_ = createMatrixIdentity();
+
+ this.mStack_ = [];
+ this.aStack_ = [];
+ this.currentPath_ = [];
+
+ // Canvas context properties
+ this.strokeStyle = "#000";
+ this.fillStyle = "#ccc";
+
+ this.lineWidth = 1;
+ this.lineJoin = "miter";
+ this.lineCap = "butt";
+ this.miterLimit = 10;
+ this.globalAlpha = 1;
+
+ var el = document.createElement('div');
+ el.style.width = surfaceElement.clientWidth + 'px';
+ el.style.height = surfaceElement.clientHeight + 'px';
+ el.style.overflow = 'hidden';
+ el.style.position = 'absolute';
+ surfaceElement.appendChild(el);
+
+ this.element_ = el;
+ this.arcScaleX_ = 1;
+ this.arcScaleY_ = 1;
+ };
+
+ var contextPrototype = CanvasRenderingContext2D_.prototype;
+ contextPrototype.clearRect = function() {
+ this.element_.innerHTML = "";
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.beginPath = function() {
+ // TODO: Branch current matrix so that save/restore has no effect
+ // as per safari docs.
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.moveTo = function(aX, aY) {
+ this.currentPath_.push({type: "moveTo", x: aX, y: aY});
+ };
+
+ contextPrototype.lineTo = function(aX, aY) {
+ this.currentPath_.push({type: "lineTo", x: aX, y: aY});
+ };
+
+ contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
+ aCP2x, aCP2y,
+ aX, aY) {
+ this.currentPath_.push({type: "bezierCurveTo",
+ cp1x: aCP1x,
+ cp1y: aCP1y,
+ cp2x: aCP2x,
+ cp2y: aCP2y,
+ x: aX,
+ y: aY});
+ };
+
+ contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
+ // VML's qb produces different output to Firefox's
+ // FF's behaviour seems to have changed in 1.5.0.1, check this
+ this.bezierCurveTo(aCPx, aCPy, aCPx, aCPy, aX, aY);
+ };
+
+ contextPrototype.arc = function(aX, aY, aRadius,
+ aStartAngle, aEndAngle, aClockwise) {
+ aRadius *= 10;
+ var arcType = aClockwise ? "at" : "wa";
+
+ var xStart = aX + (mc(aStartAngle) * aRadius) - 5;
+ var yStart = aY + (ms(aStartAngle) * aRadius) - 5;
+
+ var xEnd = aX + (mc(aEndAngle) * aRadius) - 5;
+ var yEnd = aY + (ms(aEndAngle) * aRadius) - 5;
+
+ this.currentPath_.push({type: arcType,
+ x: aX,
+ y: aY,
+ radius: aRadius,
+ xStart: xStart,
+ yStart: yStart,
+ xEnd: xEnd,
+ yEnd: yEnd});
+
+ };
+
+ contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ };
+
+ contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.stroke();
+ };
+
+ contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
+ // Will destroy any existing path (same as FF behaviour)
+ this.beginPath();
+ this.moveTo(aX, aY);
+ this.lineTo(aX + aWidth, aY);
+ this.lineTo(aX + aWidth, aY + aHeight);
+ this.lineTo(aX, aY + aHeight);
+ this.closePath();
+ this.fill();
+ };
+
+ contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
+ var gradient = new CanvasGradient_("gradient");
+ return gradient;
+ };
+
+ contextPrototype.createRadialGradient = function(aX0, aY0,
+ aR0, aX1,
+ aY1, aR1) {
+ var gradient = new CanvasGradient_("gradientradial");
+ gradient.radius1_ = aR0;
+ gradient.radius2_ = aR1;
+ gradient.focus_.x = aX0;
+ gradient.focus_.y = aY0;
+ return gradient;
+ };
+
+ contextPrototype.drawImage = function (image, var_args) {
+ var dx, dy, dw, dh, sx, sy, sw, sh;
+ var w = image.width;
+ var h = image.height;
+
+ if (arguments.length == 3) {
+ dx = arguments[1];
+ dy = arguments[2];
+ sx = sy = 0;
+ sw = dw = w;
+ sh = dh = h;
+ } else if (arguments.length == 5) {
+ dx = arguments[1];
+ dy = arguments[2];
+ dw = arguments[3];
+ dh = arguments[4];
+ sx = sy = 0;
+ sw = w;
+ sh = h;
+ } else if (arguments.length == 9) {
+ sx = arguments[1];
+ sy = arguments[2];
+ sw = arguments[3];
+ sh = arguments[4];
+ dx = arguments[5];
+ dy = arguments[6];
+ dw = arguments[7];
+ dh = arguments[8];
+ } else {
+ throw "Invalid number of arguments";
+ }
+
+ var d = this.getCoords_(dx, dy);
+
+ var w2 = (sw / 2);
+ var h2 = (sh / 2);
+
+ var vmlStr = [];
+
+ // For some reason that I've now forgotten, using divs didn't work
+ vmlStr.push(' <g_vml_:group',
+ ' coordsize="1000,1000"',
+ ' coordorigin="0, 0"' ,
+ ' style="width:100px;height:100px;position:absolute;');
+
+ // If filters are necessary (rotation exists), create them
+ // filters are bog-slow, so only create them if abbsolutely necessary
+ // The following check doesn't account for skews (which don't exist
+ // in the canvas spec (yet) anyway.
+
+ if (this.m_[0][0] != 1 || this.m_[0][1]) {
+ var filter = [];
+
+ // Note the 12/21 reversal
+ filter.push("M11='", this.m_[0][0], "',",
+ "M12='", this.m_[1][0], "',",
+ "M21='", this.m_[0][1], "',",
+ "M22='", this.m_[1][1], "',",
+ "Dx='", d.x, "',",
+ "Dy='", d.y, "'");
+
+ // Bounding box calculation (need to minimize displayed area so that
+ // filters don't waste time on unused pixels.
+ var max = d;
+ var c2 = this.getCoords_(dx+dw, dy);
+ var c3 = this.getCoords_(dx, dy+dh);
+ var c4 = this.getCoords_(dx+dw, dy+dh);
+
+ max.x = Math.max(max.x, c2.x, c3.x, c4.x);
+ max.y = Math.max(max.y, c2.y, c3.y, c4.y);
+
+ vmlStr.push(" padding:0 ", mr(max.x), "px ", mr(max.y),
+ "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
+ filter.join(""), ", sizingmethod='clip');")
+ } else {
+ vmlStr.push(" top:", d.y, "px;left:", d.x, "px;")
+ }
+
+ vmlStr.push(' ">' ,
+ '<g_vml_:image src="', image.src, '"',
+ ' style="width:', dw, ';',
+ ' height:', dh, ';"',
+ ' cropleft="', sx / w, '"',
+ ' croptop="', sy / h, '"',
+ ' cropright="', (w - sx - sw) / w, '"',
+ ' cropbottom="', (h - sy - sh) / h, '"',
+ ' />',
+ '</g_vml_:group>');
+
+ this.element_.insertAdjacentHTML("BeforeEnd",
+ vmlStr.join(""));
+ };
+
+ contextPrototype.stroke = function(aFill) {
+ var lineStr = [];
+ var lineOpen = false;
+ var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
+ var color = a[0];
+ var opacity = a[1] * this.globalAlpha;
+
+ lineStr.push('<g_vml_:shape',
+ ' fillcolor="', color, '"',
+ ' filled="', Boolean(aFill), '"',
+ ' style="position:absolute;width:10;height:10;"',
+ ' coordorigin="0 0" coordsize="100 100"',
+ ' stroked="', !aFill, '"',
+ ' strokeweight="', this.lineWidth, '"',
+ ' strokecolor="', color, '"',
+ ' path="');
+
+ var newSeq = false;
+ var min = {x: null, y: null};
+ var max = {x: null, y: null};
+
+ for (var i = 0; i < this.currentPath_.length; i++) {
+ var p = this.currentPath_[i];
+
+ if (p.type == "moveTo") {
+ lineStr.push(" m ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "lineTo") {
+ lineStr.push(" l ");
+ var c = this.getCoords_(p.x, p.y);
+ lineStr.push(mr(c.x), ",", mr(c.y));
+ } else if (p.type == "close") {
+ lineStr.push(" x ");
+ } else if (p.type == "bezierCurveTo") {
+ lineStr.push(" c ");
+ var c = this.getCoords_(p.x, p.y);
+ var c1 = this.getCoords_(p.cp1x, p.cp1y);
+ var c2 = this.getCoords_(p.cp2x, p.cp2y);
+ lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
+ mr(c2.x), ",", mr(c2.y), ",",
+ mr(c.x), ",", mr(c.y));
+ } else if (p.type == "at" || p.type == "wa") {
+ lineStr.push(" ", p.type, " ");
+ var c = this.getCoords_(p.x, p.y);
+ var cStart = this.getCoords_(p.xStart, p.yStart);
+ var cEnd = this.getCoords_(p.xEnd, p.yEnd);
+
+ lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
+ mr(c.y - this.arcScaleY_ * p.radius), " ",
+ mr(c.x + this.arcScaleX_ * p.radius), ",",
+ mr(c.y + this.arcScaleY_ * p.radius), " ",
+ mr(cStart.x), ",", mr(cStart.y), " ",
+ mr(cEnd.x), ",", mr(cEnd.y));
+ }
+
+
+ // TODO: Following is broken for curves due to
+ // move to proper paths.
+
+ // Figure out dimensions so we can do gradient fills
+ // properly
+ if(c) {
+ if (min.x == null || c.x < min.x) {
+ min.x = c.x;
+ }
+ if (max.x == null || c.x > max.x) {
+ max.x = c.x;
+ }
+ if (min.y == null || c.y < min.y) {
+ min.y = c.y;
+ }
+ if (max.y == null || c.y > max.y) {
+ max.y = c.y;
+ }
+ }
+ }
+ lineStr.push(' ">');
+
+ if (typeof this.fillStyle == "object") {
+ var focus = {x: "50%", y: "50%"};
+ var width = (max.x - min.x);
+ var height = (max.y - min.y);
+ var dimension = (width > height) ? width : height;
+
+ focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
+ focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
+
+ var colors = [];
+
+ // inside radius (%)
+ if (this.fillStyle.type_ == "gradientradial") {
+ var inside = (this.fillStyle.radius1_ / dimension * 100);
+
+ // percentage that outside radius exceeds inside radius
+ var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
+ } else {
+ var inside = 0;
+ var expansion = 100;
+ }
+
+ var insidecolor = {offset: null, color: null};
+ var outsidecolor = {offset: null, color: null};
+
+ // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
+ // won't interpret it correctly
+ this.fillStyle.colors_.sort(function (cs1, cs2) {
+ return cs1.offset - cs2.offset;
+ });
+
+ for (var i = 0; i < this.fillStyle.colors_.length; i++) {
+ var fs = this.fillStyle.colors_[i];
+
+ colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
+
+ if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
+ insidecolor.offset = fs.offset;
+ insidecolor.color = fs.color;
+ }
+
+ if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
+ outsidecolor.offset = fs.offset;
+ outsidecolor.color = fs.color;
+ }
+ }
+ colors.pop();
+
+ lineStr.push('<g_vml_:fill',
+ ' color="', outsidecolor.color, '"',
+ ' color2="', insidecolor.color, '"',
+ ' type="', this.fillStyle.type_, '"',
+ ' focusposition="', focus.x, ', ', focus.y, '"',
+ ' colors="', colors.join(""), '"',
+ ' opacity="', opacity, '" />');
+ } else if (aFill) {
+ lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
+ } else {
+ lineStr.push(
+ '<g_vml_:stroke',
+ ' opacity="', opacity,'"',
+ ' joinstyle="', this.lineJoin, '"',
+ ' miterlimit="', this.miterLimit, '"',
+ ' endcap="', processLineCap(this.lineCap) ,'"',
+ ' weight="', this.lineWidth, 'px"',
+ ' color="', color,'" />'
+ );
+ }
+
+ lineStr.push("</g_vml_:shape>");
+
+ this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
+
+ this.currentPath_ = [];
+ };
+
+ contextPrototype.fill = function() {
+ this.stroke(true);
+ }
+
+ contextPrototype.closePath = function() {
+ this.currentPath_.push({type: "close"});
+ };
+
+ /**
+ * @private
+ */
+ contextPrototype.getCoords_ = function(aX, aY) {
+ return {
+ x: 10 * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - 5,
+ y: 10 * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - 5
+ }
+ };
+
+ contextPrototype.save = function() {
+ var o = {};
+ copyState(this, o);
+ this.aStack_.push(o);
+ this.mStack_.push(this.m_);
+ this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
+ };
+
+ contextPrototype.restore = function() {
+ copyState(this.aStack_.pop(), this);
+ this.m_ = this.mStack_.pop();
+ };
+
+ contextPrototype.translate = function(aX, aY) {
+ var m1 = [
+ [1, 0, 0],
+ [0, 1, 0],
+ [aX, aY, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.rotate = function(aRot) {
+ var c = mc(aRot);
+ var s = ms(aRot);
+
+ var m1 = [
+ [c, s, 0],
+ [-s, c, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ contextPrototype.scale = function(aX, aY) {
+ this.arcScaleX_ *= aX;
+ this.arcScaleY_ *= aY;
+ var m1 = [
+ [aX, 0, 0],
+ [0, aY, 0],
+ [0, 0, 1]
+ ];
+
+ this.m_ = matrixMultiply(m1, this.m_);
+ };
+
+ /******** STUBS ********/
+ contextPrototype.clip = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.arcTo = function() {
+ // TODO: Implement
+ };
+
+ contextPrototype.createPattern = function() {
+ return new CanvasPattern_;
+ };
+
+ // Gradient / Pattern Stubs
+ function CanvasGradient_(aType) {
+ this.type_ = aType;
+ this.radius1_ = 0;
+ this.radius2_ = 0;
+ this.colors_ = [];
+ this.focus_ = {x: 0, y: 0};
+ }
+
+ CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
+ aColor = processStyle(aColor);
+ this.colors_.push({offset: 1-aOffset, color: aColor});
+ };
+
+ function CanvasPattern_() {}
+
+ // set up externs
+ G_vmlCanvasManager = G_vmlCanvasManager_;
+ CanvasRenderingContext2D = CanvasRenderingContext2D_;
+ CanvasGradient = CanvasGradient_;
+ CanvasPattern = CanvasPattern_;
+
+})();
+
+} // if
--- /dev/null
+/*----------------------------------------------------------------------------\
+| IE Canvas 1.0 |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+|-----------------------------------------------------------------------------|
+| Implementation of the canvas API for Internet Explorer. Uses VML. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2005 Emil A Eklund |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| Dependencies: canvas.js - For initialization of canvas elements |
+|-----------------------------------------------------------------------------|
+| 2005-12-27 | Work started. |
+| 2005-12-29 | First version posted. |
+| 2006-01-03 | Fixed bug in moveTo and lineTo, arguments where not converted |
+| | to int which could cause IE to enter an endless loop. Disabled |
+| | antalias for fillRect to better comply with the Mozilla, Opera |
+| | and possibly Safari implementations where using fillRect is |
+| | about the only way to raw non antialiased lines. |
+|-----------------------------------------------------------------------------|
+| Created 2005-12-27 | All changes are in the log above. | Updated 2006-01-03 |
+\----------------------------------------------------------------------------*/
+
+<public:component>
+ <public:method name="getContext" />
+ <public:attach event="oncontentready" onevent="initCanvas()"/>
+</public:component>
+
+<script language="JScript">
+
+ function getContext() {
+ return element.context;
+ }
+
+ function initCanvas() {
+ element.context = new IECanvasContext();
+ element.style.position = 'relative';
+ element.style.display = 'block';
+ element.style.overflow = 'hidden';
+ }
+
+
+
+ function IECanvasContext() {
+ this.fillStyle = 'black';
+ this.globalAlpha = 1.0;
+ this.globalCompositeOperation = '';
+ this.lineCap = '';
+ this.lineJoin = '';
+ this.lineWidth = '0';
+ this.miterLimit = '';
+ this.shadowBlur = '';
+ this.shadowColor = '';
+ this.shadowOffsetX = '';
+ this.shadowOffsetY = '';
+ this.strokeStyle = 'black';
+ this._path = '';
+ this._stateStack = new Array();
+ this._offsetX = 0;
+ this._offsetY = 0;
+ this._rotation = 0;
+ };
+
+ IECanvasContext.prototype.save = function() {
+ var o;
+
+ o = new Object();
+ this._copyState(this, o);
+ this._stateStack.push(o);
+ };
+
+ IECanvasContext.prototype.restore = function() {
+ var o, n;
+
+ n = this._stateStack.length - 1;
+ if (n < 0) { return; }
+
+ o = this._stateStack[n];
+ this._copyState(o, this);
+ this._stateStack.splice(n, 1);
+ };
+
+ IECanvasContext.prototype._copyState = function(oFrom, oTo) {
+ oTo.fillStyle = oFrom.fillStyle;
+ oTo.lineCap = oFrom.lineCap;
+ oTo.lineJoin = oFrom.lineJoin;
+ oTo.lineWidth = oFrom.lineWidth;
+ oTo.miterLimit = oFrom.miterLimit;
+ oTo.shadowBlur = oFrom.shadowBlur;
+ oTo.shadowColor = oFrom.shadowColor;
+ oTo.shadowOffsetX = oFrom.shadowOffsetX;
+ oTo.shadowOffsetY = oFrom.shadowOffsetY;
+ oTo._offsetX = oFrom._offsetX;
+ oTo._offsetY = oFrom._offsetY;
+ oTo._rotation = oFrom._rotation;
+ };
+
+ IECanvasContext.prototype.rotate = function(r) {
+ var MAX = Math.PI * 2;
+
+ this._rotation += r;
+ while (this._rotation > MAX) { this._rotation = MAX - this._rotation; }
+ };
+
+ IECanvasContext.prototype.scale = function() { };
+
+ IECanvasContext.prototype.translate = function(x, y) {
+ this._offsetX += x;
+ this._offsetY += y;
+ };
+
+ IECanvasContext.prototype.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
+ if (this._path) { this._path += ' '; }
+
+ this._path += 'qb' + cp1x + ',' + cp1y + ',' + cp2x + ',' + cp2y + ',' + x + ',' + y;
+ };
+
+
+ IECanvasContext.prototype.clip = function() { };
+
+ IECanvasContext.prototype.beginPath = function() {
+ this._path = '';
+ };
+
+ IECanvasContext.prototype.closePath = function() {
+ if (this._path) { this._path += ' '; }
+ this._path += 'x';
+ };
+
+ IECanvasContext.prototype.lineTo = function(x, y) {
+ if (this._path) { this._path += ' '; }
+ this._path += 'l' + parseInt(x) + ',' + parseInt(y);
+ };
+
+ IECanvasContext.prototype.moveTo = function(x, y) {
+ if (this._path) { this._path += ' '; }
+ this._path += 'm' + parseInt(x) + ',' + parseInt(y);
+ };
+
+ IECanvasContext.prototype.stroke = function() {
+ var o, s, cosa, sina, cx, cy, x, y;
+
+ if (!this._path) { return; }
+
+ this._path += ' e';
+
+ o = element.ownerDocument.createElement('v:shape');
+ o.fillColor = 'none';
+ o.filled = false;
+ o.strokeColor = this.strokeStyle;
+ o.stroked = true;
+ o.weight = this.lineWidth;
+ o.coordsize = element.offsetWidth + ',' + element.offsetHeight;
+ o.style.position = 'absolute';
+ o.style.left = this._offsetX;
+ o.style.top = this._offsetY;
+ o.style.width = element.offsetWidth;
+ o.style.height = element.offsetHeight;
+ o.path = this._path;
+
+ s = element.ownerDocument.createElement('v:stroke');
+ s.opacity = this.globalAlpha;
+ o.appendChild(s);
+
+ if (this._rotation) {
+ r = element.ownerDocument.createElement('v:group');
+ r.style.position = 'absolute';
+ r.style.left = 0;
+ r.style.top = 0;
+ r.style.width = element.offsetWidth;
+ r.style.height = element.offsetHeight;
+ r.coordsize = o.coordsize;
+ r.style.rotation = Math.round((this._rotation * 180) / Math.PI);
+ r.style.rotationCenter = '0,0';
+ r.appendChild(o);
+ element.appendChild(r);
+
+ cosa = Math.cos(this._rotation);
+ sina = Math.sin(this._rotation);
+ cx = element.offsetWidth / 2;
+ cy = element.offsetHeight / 2;
+
+ x = ( cx*(1-cosa) + cy*sina);
+ y = (-cx*sina + cy*(1-cosa));
+
+ r.style.left = x * -1;
+ r.style.top = y * -1;
+ }
+ else { element.appendChild(o); }
+ };
+
+ IECanvasContext.prototype.fill = function() {
+ var o, f, r;
+
+ if (!this._path) { return; }
+
+ this._path += ' e';
+
+ o = element.ownerDocument.createElement('v:shape');
+ o.fillColor = this.fillStyle;
+ o.strokeColor = this.strokeStyle;
+ o.stroked = false;
+ o.weight = this.lineWidth;
+ o.coordsize = element.offsetWidth + ',' + element.offsetHeight;
+ o.style.position = 'absolute';
+ o.style.left = this._offsetX;
+ o.style.top = this._offsetY;
+ o.style.width = element.offsetWidth;
+ o.style.height = element.offsetHeight;
+ o.path = this._path;
+
+ f = element.ownerDocument.createElement('v:fill');
+ f.opacity = this.globalAlpha;
+ o.appendChild(f);
+
+ if (this._rotation) {
+ r = element.ownerDocument.createElement('v:group');
+ r.style.position = 'absolute';
+ r.style.left = 0;
+ r.style.top = 0;
+ r.style.width = element.offsetWidth;
+ r.style.height = element.offsetHeight;
+ r.coordsize = o.coordsize;
+ r.style.rotation = Math.round((this._rotation * 180) / Math.PI);
+ r.style.rotationCenter = '0,0';
+ r.appendChild(o);
+ element.appendChild(r);
+
+ cosa = Math.cos(this._rotation);
+ sina = Math.sin(this._rotation);
+ cx = (element.offsetWidth) / 2;
+ cy = (element.offsetHeight) / 2;
+ x = ( cx*(1-cosa) + cy*sina);
+ y = (-cx*sina + cy*(1-cosa));
+
+ r.style.left = x * -1;
+ r.style.top = y * -1;
+ }
+ else { element.appendChild(o); }
+ };
+
+ IECanvasContext.prototype.arcTo = function(x1, y1, x2, y2, radius) {
+ // not implemented in gecko, not implemented here
+ };
+
+ IECanvasContext.prototype.quadraticCurveTo = function(cpx, cpy, x, y) {
+ if (this._path) { this._path += ' '; }
+
+ this._path += 'qb' + cpx + ',' + cpy + ',' + x + ',' + y;
+ };
+
+ IECanvasContext.prototype.arc = function(x, y, radius, startAngle, endAngle, clockwise) {
+ var xi, yi, x1, y1, x2, y2, x3, y3, x4, y4;
+
+ if (this._path) { this._path += ' '; }
+
+ xi = parseFloat(x);
+ yi = parseFloat(y);
+
+ x1 = xi - radius;
+ y1 = yi - radius;
+
+ x2 = xi + radius;
+ y2 = yi + radius;
+
+ x3 = xi + (Math.cos(startAngle) * radius);
+ y3 = yi + (Math.sin(startAngle) * radius);
+
+ x4 = xi + (Math.cos(endAngle) * radius);
+ y4 = yi + (Math.sin(endAngle) * radius);
+
+ x3 = Math.round(x3);
+ y3 = Math.round(y3);
+ x4 = Math.round(x4);
+ y4 = Math.round(y4);
+
+ this._path += 'ar' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' + x3 + ',' + y3 + ',' + x4 + ',' + y4;
+ };
+
+
+ IECanvasContext.prototype.rect = function(x, y, w, h) {
+ var x1, y1, x2, y2;
+
+ x2 = x + w;
+ y2 = y + h;
+
+ x1 = Math.round(x);
+ y1 = Math.round(y);
+ x2 = Math.round(x2);
+ y2 = Math.round(y2);
+
+ this._path += 'm' + x1 + ',' + y1;
+ this._path += ' l' + x2 + ',' + y1;
+ this._path += ' l' + x2 + ',' + y2;
+ this._path += ' l' + x1 + ',' + y2;
+ this._path += ' x'
+ };
+
+ IECanvasContext.prototype.strokeRect = function(x, y, w, h) {
+ var o, s;
+
+ o = element.ownerDocument.createElement('v:rect');
+ o.fillColor = 'none';
+ o.filled = false;
+ o.strokeColor = this.strokeStyle;
+ o.stroked = true;
+ o.weight = this.lineWidth;
+ o.style.position = 'absolute';
+ o.style.left = this._offsetX + x;
+ o.style.top = this._offsetY + y;
+ o.style.width = w;
+ o.style.height = h;
+
+ s = element.ownerDocument.createElement('v:fill');
+ s.opacity = this.globalAlpha;
+ o.appendChild(s);
+
+ element.appendChild(o);
+ };
+
+ IECanvasContext.prototype.clearRect = function(x, y, w, h) { };
+
+
+ IECanvasContext.prototype.fillRect = function(x, y, w, h) {
+ var o, f;
+
+ if ((x == 0) && (y == 0) && (w == element.offsetWidth) && (h == element.offsetHeight) && (this._offsetX == 0) && (this._offsetY == 0) && (this.globalAlpha == 1)) {
+ while (element.firstChild) { element.removeChild(element.lastChild); }
+ }
+
+ o = element.ownerDocument.createElement('v:rect');
+ o.fillColor = this.fillStyle;
+ o.filled = true;
+ o.stroked = false;
+ o.weight = 0;
+ o.style.position = 'absolute';
+ o.style.left = this._offsetX + x;
+ o.style.top = this._offsetY + y;
+ o.style.width = w;
+ o.style.height = h;
+ o.style.antialias = 'false';
+
+ f = element.ownerDocument.createElement('v:fill');
+ f.opacity = this.globalAlpha;
+ o.appendChild(f);
+
+ element.appendChild(o);
+ };
+
+ IECanvasContext.prototype.addColorStop = function() { };
+ IECanvasContext.prototype.createLinearGradient = function() { };
+ IECanvasContext.prototype.createPattern = function() { };
+ IECanvasContext.prototype.createRadialGradient = function() { };
+
+ IECanvasContext.prototype.drawImage = function() { };
+ IECanvasContext.prototype.drawImageFromRect = function() { };
+
+</script>
--- /dev/null
+/*----------------------------------------------------------------------------\
+| IE Canvas 1.0 |
+| Emulation Initialization Script |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+|-----------------------------------------------------------------------------|
+| Implementation of the canvas API for Internet Explorer. Uses VML. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2005 Emil A Eklund |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| Dependencies: canvas.htc - Actual implementation |
+|-----------------------------------------------------------------------------|
+| 2005-12-27 | Work started. |
+| 2005-12-29 | First version posted. |
+| 2006-01-03 | Added htcFile argument to ieCanvasInit method. |
+|-----------------------------------------------------------------------------|
+| Created 2005-12-27 | All changes are in the log above. | Updated 2006-01-03 |
+\----------------------------------------------------------------------------*/
+
+
+function ieCanvasInit(htcFile) {
+ var ie, opera, a, nodes, i, oVml, oStyle, newNode;
+
+ /*
+ * Only proceed if browser is IE 6 or later (as all other major browsers
+ * support canvas out of the box there is no need to try to emulate for
+ * them, and besides only IE supports VML anyway.
+ */
+ ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
+ opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
+ if ((!ie) || (ie[1] < 6) || (opera)) {
+ return;
+ }
+
+ /*
+ * Recreate elements, as there is no canvas tag in HTML
+ * The canvas tag is replaced by a new one created using createElement,
+ * even though the same tag name is given the generated tag is slightly
+ * different, it's created prefixed with a XML namespace declaration
+ * <?XML:NAMESPACE PREFIX ="PUBLIC" NS="URN:COMPONENT" />
+ */
+ nodes = document.getElementsByTagName('canvas');
+ for (i = 0; i < nodes.length; i++) {
+ node = nodes[i];
+ if (node.getContext) { return; } // Other implementation, abort
+ newNode = document.createElement('canvas');
+ newNode.id = node.id;
+ newNode.style.width = node.width;
+ newNode.style.height = node.height;
+ node.parentNode.replaceChild(newNode, node);
+ }
+
+ /* Add VML includes and namespace */
+ document.namespaces.add("v");
+ oVml = document.createElement('object');
+ oVml.id = 'VMLRender';
+ oVml.codebase = 'vgx.dll';
+ oVml.classid = 'CLSID:10072CEC-8CC1-11D1-986E-00A0C955B42E';
+ document.body.appendChild(oVml);
+
+ /* Add required css rules */
+ if ((!htcFile) || (htcFile == '')) { htcFile = 'iecanvas.htc'; }
+ oStyle = document.createStyleSheet();
+ oStyle.addRule('canvas', "behavior: url('" + htcFile + "');");
+ oStyle.addRule('v\\:*', "behavior: url(#VMLRender);");
+}
--- /dev/null
+/*----------------------------------------------------------------------------\
+| Chart 1.0 |
+| JavaScript Graphics Chart Painter |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+| Modified by Ma Bingyao |
+| (http://www.coolcode.cn) |
+|-----------------------------------------------------------------------------|
+| JavaScript Graphics implementation of the chart painter API. jsGraphics is |
+| used to draw the chart, html elements are used for the legend and axis |
+| labels as, at the time being. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2006 Emil A Eklund & Ma Bingyao |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| 2006-01-03 | Work started. |
+| 2006-01-05 | Added legend and axis labels. Changed the painter api slightly |
+| | to allow two-stage initialization (required for ie/canvas) and |
+| | added legend/axis related methods. Also updated bar chart type |
+| | and added a few options, mostly related to bar charts. |
+| 2006-01-07 | Updated chart size calculations to take legend and axis labels |
+| | into consideration. Split painter implementations to separate |
+| | files. |
+| 2006-02-03 | Modified to use wz_jsgraphics instead of canvas by Ma Bingyao. |
+|-----------------------------------------------------------------------------|
+| Created 2006-01-03 | All changes are in the log above. | Updated 2006-02-03 |
+\----------------------------------------------------------------------------*/
+
+function JsGraphicsChartPainterFactory() {
+ return new JsGraphicsChartPainter();
+}
+
+
+function JsGraphicsChartPainter() {
+ this.base = AbstractChartPainter;
+};
+
+
+JsGraphicsChartPainter.prototype = new AbstractChartPainter;
+
+
+JsGraphicsChartPainter.prototype.create = function(el) {
+ while (el.firstChild) { el.removeChild(el.lastChild); }
+
+ this.el = el;
+ this.w = el.clientWidth;
+ this.h = el.clientHeight;
+
+ this.canvas = document.createElement('div');
+ this.canvas.width = this.w;
+ this.canvas.height = this.h;
+ this.canvas.style.width = this.w + 'px';
+ this.canvas.style.height = this.h + 'px';
+ this.canvas.style.position = "relative";
+ this.canvas.id = "canvas_" + el.id;
+ this.canvas.onselectstart = function () { return false; };
+
+ el.appendChild(this.canvas);
+};
+
+
+JsGraphicsChartPainter.prototype.init = function(xlen, ymin, ymax, xgd, ygd, bLegendLabels) {
+ this.ctx = new jsGraphics(this.canvas.id);
+
+ this.chartx = 0;
+ this.chartw = this.w;
+ this.charth = this.h;
+ this.charty = 0;
+
+ this.xlen = xlen;
+ this.ymin = ymin;
+ this.ymax = ymax;
+ this.xgd = xgd;
+ this.ygd = ygd;
+
+ this.calc(this.chartw, this.charth, xlen, ymin, ymax, xgd, ygd);
+};
+
+
+JsGraphicsChartPainter.prototype.drawLegend = function(series) {
+ var legend, list, item, label;
+
+ legend = document.createElement('div');
+ legend.className = 'legend';
+ legend.style.position = 'absolute';
+ list = document.createElement('ul');
+
+ for (i = 0; i < series.length; i++) {
+ item = document.createElement('li');
+ item.style.color = series[i].color;
+ label = document.createElement('span');
+ label.appendChild(document.createTextNode(series[i].label));
+ label.style.color = 'black';
+ item.appendChild(label);
+ list.appendChild(item);
+ }
+ legend.appendChild(list);
+ this.el.appendChild(legend);
+ legend.style.right = '0px';
+ legend.style.top = this.charty + (this.charth / 2) - (legend.offsetHeight / 2) + 'px';
+ this.legend = legend;
+
+ /* Recalculate chart width and position based on labels and legend */
+ this.chartw = this.w - (this.legend.offsetWidth + 5);
+
+ this.calc(this.chartw, this.charth, this.xlen, this.ymin, this.ymax, this.xgd, this.ygd);
+};
+
+
+JsGraphicsChartPainter.prototype.drawVerticalAxis = function(ygd, precision) {
+ var axis, item, step, y, ty, n, yoffset, value, multiplier, w, items, pos;
+
+ /* Calculate step size and rounding precision */
+ multiplier = Math.pow(10, precision);
+ step = this.range / (ygd - 1);
+
+ /* Create container */
+ axis = document.createElement('div');
+ axis.style.position = 'absolute';
+ axis.style.left = '0px';
+ axis.style.top = '0px';
+ axis.style.textAlign = 'right';
+ this.el.appendChild(axis);
+
+ /* Draw labels and points */
+ this.ctx.setColor('black');
+ w = 0;
+ items = new Array();
+ for (n = 0, i = this.ymax; (i > this.ymin) && (n < ygd - 1); i -= step, n++) {
+ item = document.createElement('span');
+ value = parseInt(i * multiplier) / multiplier;
+ item.appendChild(document.createTextNode(value));
+ axis.appendChild(item);
+ items[items.length] = new Array(i, item);
+ if (item.offsetWidth > w) { w = item.offsetWidth; }
+ }
+
+ /* Draw last label and point (lower left corner of chart) */
+ item = document.createElement('span');
+ item.appendChild(document.createTextNode(this.ymin));
+ axis.appendChild(item);
+ items[items.length] = new Array(this.ymin, item);
+ if (item.offsetWidth > w) { w = item.offsetWidth; }
+
+ /* Set width of container to width of widest label */
+ axis.style.width = w + 'px';
+
+ /* Recalculate chart width and position based on labels and legend */
+ this.chartx = w + 5;
+ this.charty = item.offsetHeight / 2;
+ this.charth = this.h - ((item.offsetHeight * 1.5) + 5);
+ this.chartw = this.w - (((this.legend)?this.legend.offsetWidth:0) + w + 10);
+ this.calc(this.chartw, this.charth, this.xlen, this.ymin, this.ymax, this.xgd, this.ygd);
+
+ /* Position labels on the axis */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+ for (i = 0; i < items.length; i++) {
+ item = items[i][1];
+ pos = items[i][0];
+ if (pos == this.ymin) { y = this.charty + this.charth - 1; }
+ else { y = this.charty + (this.charth - (pos / n) + yoffset); }
+ this.ctx.drawLine(this.chartx - 5, y, this.chartx, y);
+ ty = y - (item.offsetHeight/2);
+ item.style.position = 'absolute';
+ item.style.right = '0px';
+ item.style.top = ty + 'px';
+} };
+
+JsGraphicsChartPainter.prototype.drawHorizontalAxis = function(xlen, labels, xgd, precision) {
+ var axis, item, step, x, tx, y1, y2, n, multiplier;
+
+ /* Calculate offset, step size and rounding precision */
+ multiplier = Math.pow(10, precision);
+ n = this.chartw / (xgd - 1);
+
+ /* Create container */
+ axis = document.createElement('div');
+ axis.style.position = 'absolute';
+ axis.style.left = '0px';
+ axis.style.top = (this.charty + this.charth + 5) + 'px';
+ axis.style.width = this.w + 'px';
+ this.el.appendChild(axis);
+
+ /* Draw labels and points */
+ this.ctx.setColor('black');
+ y1 = this.charty + this.charth;
+ y2 = this.charty + this.charth + 5;
+ for (i = 0; i < xgd; i++) {
+ item = document.createElement('span');
+ item.appendChild(document.createTextNode(labels[i]));
+ axis.appendChild(item);
+ x = this.chartx + (n * i);
+ tx = x - (item.offsetWidth/2)
+ item.style.position = 'absolute';
+ item.style.left = tx + 'px';
+ item.style.top = '0px';
+ this.ctx.drawLine(x, y1, x, y2);
+} };
+
+
+JsGraphicsChartPainter.prototype.drawAxis = function() {
+ var x1, x2, y1, y2;
+ this.ctx.setColor('black');
+ x1 = this.chartx;
+ x2 = this.chartx + this.chartw + 1;
+ y1 = this.charty;
+ y2 = this.charty + this.charth - 1;
+ this.ctx.drawLine(x1, y1, x1, y2);
+ this.ctx.drawLine(x1, y2, x2, y2);
+ this.ctx.paint();
+};
+
+
+JsGraphicsChartPainter.prototype.drawBackground = function() {
+ this.ctx.setColor('white');
+ this.ctx.fillRect(0, 0, this.w, this.h);
+};
+
+
+JsGraphicsChartPainter.prototype.drawChart = function() {
+ this.ctx.setColor('silver');
+ if (this.xgrid) {
+ for (i = this.xgrid; i < this.chartw; i += this.xgrid) {
+ this.ctx.drawLine(this.chartx + i, this.charty, this.chartx + i, this.charty + this.charth - 1);
+ } }
+ if (this.ygrid) {
+ for (i = this.charth - this.ygrid; i > 0; i -= this.ygrid) {
+ this.ctx.drawLine(this.chartx + 1, this.charty + i, this.chartx + this.chartw + 1, this.charty + i);
+} } };
+
+
+JsGraphicsChartPainter.prototype.drawArea = function(color, values) {
+ var i, len, x, y, n, yoffset;
+ var XPoints = new Array();
+ var YPoints = new Array();
+
+ /* Determine distance between points and offset */
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+ this.ctx.setColor(color);
+
+ x = this.chartx + 1;
+ y = this.charty + this.charth - 1;
+ XPoints[XPoints.length] = x;
+ YPoints[YPoints.length] = y;
+
+ y = this.charty + this.charth - (values[0] / n) + yoffset;
+ XPoints[XPoints.length] = x;
+ YPoints[YPoints.length] = y;
+
+ for (i = 1; i < len; i++) {
+ y = this.charty + this.charth - (values[i] / n) + yoffset;
+ x += this.xstep;
+ XPoints[XPoints.length] = x;
+ YPoints[YPoints.length] = y;
+ }
+
+ XPoints[XPoints.length] = x;
+ YPoints[YPoints.length] = this.charty + this.charth - 1;
+
+ this.ctx.fillPolygon(XPoints, YPoints);
+} };
+
+
+JsGraphicsChartPainter.prototype.drawLine = function(color, values) {
+ var i, len, x1, y1, x2, y2, n, yoffset;
+
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+ this.ctx.setStroke(1);
+ this.ctx.setColor(color);
+ x1 = this.chartx + 1;
+ y1 = this.charty + this.charth - (values[0] / n) + yoffset;
+
+ for (i = 1; i < len; i++) {
+ y2 = this.charty + this.charth - (values[i] / n) + yoffset;
+ x2 = x1 + this.xstep;
+ this.ctx.drawLine(x1, y1, x2, y2);
+ x1 = x2;
+ y1 = y2;
+ }
+
+} };
+
+
+JsGraphicsChartPainter.prototype.drawBars = function(color, values, xlen, xoffset, width) {
+ var i, len, x, y, n, yoffset;
+
+ n = this.range / this.charth;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len > xlen) { len = xlen; }
+ if (len) {
+ this.ctx.setColor(color);
+ x = this.chartx + xoffset + 1;
+ for (i = 0; i < len; i++) {
+ y = this.charty + this.charth - (values[i] / n) + yoffset;
+ this.ctx.fillRect(x, y, width, this.charty + this.charth - y);
+ x += this.xstep;
+} } };
--- /dev/null
+/*----------------------------------------------------------------------------\
+| Chart 1.0 |
+| SVG Chart Painter |
+|-----------------------------------------------------------------------------|
+| Created by Emil A Eklund |
+| (http://eae.net/contact/emil) |
+|-----------------------------------------------------------------------------|
+| SVG implementation of the chart painter API. A svg element is used to draw |
+| draw the chart. This implementation is not complete and is provided mostly |
+| as a reference at this stage. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2006 Emil A Eklund |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| This program is free software; you can redistribute it and/or modify it |
+| under the terms of the MIT License. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| Permission is hereby granted, free of charge, to any person obtaining a |
+| copy of this software and associated documentation files (the "Software"), |
+| to deal in the Software without restriction, including without limitation |
+| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
+| and/or sell copies of the Software, and to permit persons to whom the |
+| Software is furnished to do so, subject to the following conditions: |
+| The above copyright notice and this permission notice shall be included in |
+| all copies or substantial portions of the Software. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
+| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
+| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
+| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
+| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
+| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
+| DEALINGS IN THE SOFTWARE. |
+|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
+| http://eae.net/license/mit |
+|-----------------------------------------------------------------------------|
+| 2006-01-03 | Work started. |
+| 2006-01-05 | Added legend and axis labels. Changed the painter api slightly |
+| | to allow two-stage initialization (required for ie/canvas) and |
+| | added legend/axis related methods. Also updated bar chart type |
+| | and added a few options, mostly related to bar charts. |
+| 2006-01-07 | Split painter implementations to separate files. |
+|-----------------------------------------------------------------------------|
+| Created 2006-01-03 | All changes are in the log above. | Updated 2006-01-07 |
+\----------------------------------------------------------------------------*/
+
+function SVGChartPainterFactory() {
+ return new SVGChartPainter();
+}
+
+
+function SVGChartPainter() {
+ this.base = AbstractChartPainter;
+};
+
+
+SVGChartPainter.prototype = new AbstractChartPainter;
+
+
+SVGChartPainter.prototype.create = function(el) {
+ this.svg = el;
+ this.w = this.svg.getAttribute('width');
+ this.h = this.svg.getAttribute('height');
+};
+
+
+SVGChartPainter.prototype.init = function(xlen, ymin, ymax, xgd, ygd, bLegendLabels) {
+ this.calc(this.w, this.h, xlen, ymin, ymax, xgd, ygd);
+};
+
+
+SVGChartPainter.prototype.drawBackground = function() {
+ while (this.svg.firstChild) { this.svg.removeChild(this.svg.lastChild); }
+ this._drawRect('white', 0, 0, this.w, this.h);
+};
+
+
+SVGChartPainter.prototype.drawChart = function() {
+ if (this.xgrid) {
+ for (i = this.xgrid; i < this.w; i += this.xgrid) {
+ this._drawRect('silver', 0 + i, 0, 1, this.h-1);
+ } }
+ if (this.ygrid) {
+ for (i = this.h - this.ygrid; i > 0; i -= this.ygrid) {
+ this._drawRect('silver', 1, 0 + i, this.w, 1);
+} } };
+
+
+SVGChartPainter.prototype.drawAxis = function() {
+ this._drawRect('black', 0, 0, 1, this.h);
+ this._drawRect('black', 0, this.h-1, this.w, 1);
+};
+
+
+SVGChartPainter.prototype.drawArea = function(color, values) {
+ var i, len, x, y, n, yoffset, path, o;
+
+ /* Determine distance between points and offset */
+ n = this.range/this.h;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+
+ /* Begin line in lower left corner */
+ x = 1;
+ path = 'M' + x + ',' + (this.h-1);
+
+ /* Determine position of first point and append line to command */
+ y = this.h - (values[0] / n) + yoffset;
+ path += ' L' + x + ',' + y;
+
+ /* Append commands for succeeding points */
+ for (i = 1; i < len; i++) {
+ y = this.h - (values[i] / n) + yoffset;
+ x += this.xstep;
+ path += ' L' + x + ',' + y;
+ }
+
+ /* Close path and fill it */
+ path += ' L' + x + ',' + (this.h-1) + ' Z';
+ o = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+ o.setAttribute('stroke', color);
+ o.setAttribute('fill', color);
+ o.setAttribute('d', path);
+ this.svg.appendChild(o);
+} };
+
+
+SVGChartPainter.prototype.drawLine = function(color, values) {
+ var i, len, x, y, n, yoffset, path, o;
+
+ /* Determine distance between points and offset */
+ n = this.range/this.h;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+
+ /* Determine position of first point and start path */
+ x = 1;
+ y = this.h - (values[0] / n) + yoffset;
+ path = 'M' + x + ',' + y;
+
+ /* Append line to commands for succeeding points */
+ for (i = 1; i < len; i++) {
+ y = this.h - (values[i] / n) + yoffset;
+ x += this.xstep;
+ path += ' L' + x + ',' + y;
+ }
+
+ /* Draw path */
+ o = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+ o.setAttribute('stroke', color);
+ o.setAttribute('fill', 'none');
+ o.setAttribute('stroke-width', '1px');
+ o.setAttribute('d', path);
+ this.svg.appendChild(o);
+} };
+
+
+SVGChartPainter.prototype.drawBars = function(color, values, xoffset, width) {
+ var i, len, x, y, n, yoffset;
+
+ /* Determine distance between points and offset */
+ n = this.range/this.h;
+ yoffset = (this.ymin / n);
+
+ len = values.length;
+ if (len) {
+
+ /* Determine position of each bar and draw it */
+ x = xoffset + 1;
+ for (i = 0; i < len; i++) {
+ y = this.h - (values[i] / n);
+ this._drawRect(color, x, y, width, this.h - y);
+ x += this.xstep;
+} } };
+
+
+SVGChartPainter.prototype._drawRect = function(color, x, y, w, h) {
+ var rect;
+
+ rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+ rect.setAttribute('stroke', 'none');
+ rect.setAttribute('fill', color);
+ rect.setAttribute('x', x + 'px');
+ rect.setAttribute('y', y + 'px');
+ rect.setAttribute('width', w + 'px');
+ rect.setAttribute('height', h + 'px');
+
+ this.svg.appendChild(rect);
+};
--- /dev/null
+/* This notice must be untouched at all times.
+
+wz_jsgraphics.js v. 2.33
+The latest version is available at
+http://www.walterzorn.com
+or http://www.devira.com
+or http://www.walterzorn.de
+
+Copyright (c) 2002-2004 Walter Zorn. All rights reserved.
+Created 3. 11. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
+Last modified: 24. 10. 2005
+
+Performance optimizations for Internet Explorer
+by Thomas Frank and John Holdsworth.
+fillPolygon method implemented by Matthieu Haller.
+
+High Performance JavaScript Graphics Library.
+Provides methods
+- to draw lines, rectangles, ellipses, polygons
+ with specifiable line thickness,
+- to fill rectangles and ellipses
+- to draw text.
+NOTE: Operations, functions and branching have rather been optimized
+to efficiency and speed than to shortness of source code.
+
+LICENSE: LGPL
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License (LGPL) as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
+or see http://www.gnu.org/copyleft/lesser.html
+*/
+
+
+var jg_ihtm, jg_ie, jg_fast, jg_dom, jg_moz,
+jg_n4 = (document.layers && typeof document.classes != "undefined");
+
+
+function chkDHTM(x, i)
+{
+ x = document.body || null;
+ jg_ie = x && typeof x.insertAdjacentHTML != "undefined";
+ jg_dom = (x && !jg_ie &&
+ typeof x.appendChild != "undefined" &&
+ typeof document.createRange != "undefined" &&
+ typeof (i = document.createRange()).setStartBefore != "undefined" &&
+ typeof i.createContextualFragment != "undefined");
+ jg_ihtm = !jg_ie && !jg_dom && x && typeof x.innerHTML != "undefined";
+ jg_fast = jg_ie && document.all && !window.opera;
+ jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";
+}
+
+
+function pntDoc()
+{
+ this.wnd.document.write(jg_fast? this.htmRpc() : this.htm);
+ this.htm = '';
+}
+
+
+function pntCnvDom()
+{
+ var x = document.createRange();
+ x.setStartBefore(this.cnv);
+ x = x.createContextualFragment(jg_fast? this.htmRpc() : this.htm);
+ this.cnv.appendChild(x);
+ this.htm = '';
+}
+
+
+function pntCnvIe()
+{
+ this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast? this.htmRpc() : this.htm);
+ this.htm = '';
+}
+
+
+function pntCnvIhtm()
+{
+ this.cnv.innerHTML += this.htm;
+ this.htm = '';
+}
+
+
+function pntCnv()
+{
+ this.htm = '';
+}
+
+
+function mkDiv(x, y, w, h)
+{
+ this.htm += '<div style="position:absolute;'+
+ 'left:' + x + 'px;'+
+ 'top:' + y + 'px;'+
+ 'width:' + w + 'px;'+
+ 'height:' + h + 'px;'+
+ 'clip:rect(0,'+w+'px,'+h+'px,0);'+
+ 'background-color:' + this.color +
+ (!jg_moz? ';overflow:hidden' : '')+
+ ';"><\/div>';
+}
+
+
+function mkDivIe(x, y, w, h)
+{
+ this.htm += '%%'+this.color+';'+x+';'+y+';'+w+';'+h+';';
+}
+
+
+function mkDivPrt(x, y, w, h)
+{
+ this.htm += '<div style="position:absolute;'+
+ 'border-left:' + w + 'px solid ' + this.color + ';'+
+ 'left:' + x + 'px;'+
+ 'top:' + y + 'px;'+
+ 'width:0px;'+
+ 'height:' + h + 'px;'+
+ 'clip:rect(0,'+w+'px,'+h+'px,0);'+
+ 'background-color:' + this.color +
+ (!jg_moz? ';overflow:hidden' : '')+
+ ';"><\/div>';
+}
+
+
+function mkLyr(x, y, w, h)
+{
+ this.htm += '<layer '+
+ 'left="' + x + '" '+
+ 'top="' + y + '" '+
+ 'width="' + w + '" '+
+ 'height="' + h + '" '+
+ 'bgcolor="' + this.color + '"><\/layer>\n';
+}
+
+
+var regex = /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
+function htmRpc()
+{
+ return this.htm.replace(
+ regex,
+ '<div style="overflow:hidden;position:absolute;background-color:'+
+ '$1;left:$2;top:$3;width:$4;height:$5"></div>\n');
+}
+
+
+function htmPrtRpc()
+{
+ return this.htm.replace(
+ regex,
+ '<div style="overflow:hidden;position:absolute;background-color:'+
+ '$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1"></div>\n');
+}
+
+
+function mkLin(x1, y1, x2, y2)
+{
+ if (x1 > x2)
+ {
+ var _x2 = x2;
+ var _y2 = y2;
+ x2 = x1;
+ y2 = y1;
+ x1 = _x2;
+ y1 = _y2;
+ }
+ var dx = x2-x1, dy = Math.abs(y2-y1),
+ x = x1, y = y1,
+ yIncr = (y1 > y2)? -1 : 1;
+
+ if (dx >= dy)
+ {
+ var pr = dy<<1,
+ pru = pr - (dx<<1),
+ p = pr-dx,
+ ox = x;
+ while ((dx--) > 0)
+ {
+ ++x;
+ if (p > 0)
+ {
+ this.mkDiv(ox, y, x-ox, 1);
+ y += yIncr;
+ p += pru;
+ ox = x;
+ }
+ else p += pr;
+ }
+ this.mkDiv(ox, y, x2-ox+1, 1);
+ }
+
+ else
+ {
+ var pr = dx<<1,
+ pru = pr - (dy<<1),
+ p = pr-dy,
+ oy = y;
+ if (y2 <= y1)
+ {
+ while ((dy--) > 0)
+ {
+ if (p > 0)
+ {
+ this.mkDiv(x++, y, 1, oy-y+1);
+ y += yIncr;
+ p += pru;
+ oy = y;
+ }
+ else
+ {
+ y += yIncr;
+ p += pr;
+ }
+ }
+ this.mkDiv(x2, y2, 1, oy-y2+1);
+ }
+ else
+ {
+ while ((dy--) > 0)
+ {
+ y += yIncr;
+ if (p > 0)
+ {
+ this.mkDiv(x++, oy, 1, y-oy);
+ p += pru;
+ oy = y;
+ }
+ else p += pr;
+ }
+ this.mkDiv(x2, oy, 1, y2-oy+1);
+ }
+ }
+}
+
+
+function mkLin2D(x1, y1, x2, y2)
+{
+ if (x1 > x2)
+ {
+ var _x2 = x2;
+ var _y2 = y2;
+ x2 = x1;
+ y2 = y1;
+ x1 = _x2;
+ y1 = _y2;
+ }
+ var dx = x2-x1, dy = Math.abs(y2-y1),
+ x = x1, y = y1,
+ yIncr = (y1 > y2)? -1 : 1;
+
+ var s = this.stroke;
+ if (dx >= dy)
+ {
+ if (dx > 0 && s-3 > 0)
+ {
+ var _s = (s*dx*Math.sqrt(1+dy*dy/(dx*dx))-dx-(s>>1)*dy) / dx;
+ _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
+ }
+ else var _s = s;
+ var ad = Math.ceil(s/2);
+
+ var pr = dy<<1,
+ pru = pr - (dx<<1),
+ p = pr-dx,
+ ox = x;
+ while ((dx--) > 0)
+ {
+ ++x;
+ if (p > 0)
+ {
+ this.mkDiv(ox, y, x-ox+ad, _s);
+ y += yIncr;
+ p += pru;
+ ox = x;
+ }
+ else p += pr;
+ }
+ this.mkDiv(ox, y, x2-ox+ad+1, _s);
+ }
+
+ else
+ {
+ if (s-3 > 0)
+ {
+ var _s = (s*dy*Math.sqrt(1+dx*dx/(dy*dy))-(s>>1)*dx-dy) / dy;
+ _s = (!(s-4)? Math.ceil(_s) : Math.round(_s)) + 1;
+ }
+ else var _s = s;
+ var ad = Math.round(s/2);
+
+ var pr = dx<<1,
+ pru = pr - (dy<<1),
+ p = pr-dy,
+ oy = y;
+ if (y2 <= y1)
+ {
+ ++ad;
+ while ((dy--) > 0)
+ {
+ if (p > 0)
+ {
+ this.mkDiv(x++, y, _s, oy-y+ad);
+ y += yIncr;
+ p += pru;
+ oy = y;
+ }
+ else
+ {
+ y += yIncr;
+ p += pr;
+ }
+ }
+ this.mkDiv(x2, y2, _s, oy-y2+ad);
+ }
+ else
+ {
+ while ((dy--) > 0)
+ {
+ y += yIncr;
+ if (p > 0)
+ {
+ this.mkDiv(x++, oy, _s, y-oy+ad);
+ p += pru;
+ oy = y;
+ }
+ else p += pr;
+ }
+ this.mkDiv(x2, oy, _s, y2-oy+ad+1);
+ }
+ }
+}
+
+
+function mkLinDott(x1, y1, x2, y2)
+{
+ if (x1 > x2)
+ {
+ var _x2 = x2;
+ var _y2 = y2;
+ x2 = x1;
+ y2 = y1;
+ x1 = _x2;
+ y1 = _y2;
+ }
+ var dx = x2-x1, dy = Math.abs(y2-y1),
+ x = x1, y = y1,
+ yIncr = (y1 > y2)? -1 : 1,
+ drw = true;
+ if (dx >= dy)
+ {
+ var pr = dy<<1,
+ pru = pr - (dx<<1),
+ p = pr-dx;
+ while ((dx--) > 0)
+ {
+ if (drw) this.mkDiv(x, y, 1, 1);
+ drw = !drw;
+ if (p > 0)
+ {
+ y += yIncr;
+ p += pru;
+ }
+ else p += pr;
+ ++x;
+ }
+ if (drw) this.mkDiv(x, y, 1, 1);
+ }
+
+ else
+ {
+ var pr = dx<<1,
+ pru = pr - (dy<<1),
+ p = pr-dy;
+ while ((dy--) > 0)
+ {
+ if (drw) this.mkDiv(x, y, 1, 1);
+ drw = !drw;
+ y += yIncr;
+ if (p > 0)
+ {
+ ++x;
+ p += pru;
+ }
+ else p += pr;
+ }
+ if (drw) this.mkDiv(x, y, 1, 1);
+ }
+}
+
+
+function mkOv(left, top, width, height)
+{
+ var a = width>>1, b = height>>1,
+ wod = width&1, hod = (height&1)+1,
+ cx = left+a, cy = top+b,
+ x = 0, y = b,
+ ox = 0, oy = b,
+ aa = (a*a)<<1, bb = (b*b)<<1,
+ st = (aa>>1)*(1-(b<<1)) + bb,
+ tt = (bb>>1) - aa*((b<<1)-1),
+ w, h;
+ while (y > 0)
+ {
+ if (st < 0)
+ {
+ st += bb*((x<<1)+3);
+ tt += (bb<<1)*(++x);
+ }
+ else if (tt < 0)
+ {
+ st += bb*((x<<1)+3) - (aa<<1)*(y-1);
+ tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
+ w = x-ox;
+ h = oy-y;
+ if (w&2 && h&2)
+ {
+ this.mkOvQds(cx, cy, -x+2, ox+wod, -oy, oy-1+hod, 1, 1);
+ this.mkOvQds(cx, cy, -x+1, x-1+wod, -y-1, y+hod, 1, 1);
+ }
+ else this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, oy-h+hod, w, h);
+ ox = x;
+ oy = y;
+ }
+ else
+ {
+ tt -= aa*((y<<1)-3);
+ st -= (aa<<1)*(--y);
+ }
+ }
+ this.mkDiv(cx-a, cy-oy, a-ox+1, (oy<<1)+hod);
+ this.mkDiv(cx+ox+wod, cy-oy, a-ox+1, (oy<<1)+hod);
+}
+
+
+function mkOv2D(left, top, width, height)
+{
+ var s = this.stroke;
+ width += s-1;
+ height += s-1;
+ var a = width>>1, b = height>>1,
+ wod = width&1, hod = (height&1)+1,
+ cx = left+a, cy = top+b,
+ x = 0, y = b,
+ aa = (a*a)<<1, bb = (b*b)<<1,
+ st = (aa>>1)*(1-(b<<1)) + bb,
+ tt = (bb>>1) - aa*((b<<1)-1);
+
+ if (s-4 < 0 && (!(s-2) || width-51 > 0 && height-51 > 0))
+ {
+ var ox = 0, oy = b,
+ w, h,
+ pxl, pxr, pxt, pxb, pxw;
+ while (y > 0)
+ {
+ if (st < 0)
+ {
+ st += bb*((x<<1)+3);
+ tt += (bb<<1)*(++x);
+ }
+ else if (tt < 0)
+ {
+ st += bb*((x<<1)+3) - (aa<<1)*(y-1);
+ tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
+ w = x-ox;
+ h = oy-y;
+
+ if (w-1)
+ {
+ pxw = w+1+(s&1);
+ h = s;
+ }
+ else if (h-1)
+ {
+ pxw = s;
+ h += 1+(s&1);
+ }
+ else pxw = h = s;
+ this.mkOvQds(cx, cy, -x+1, ox-pxw+w+wod, -oy, -h+oy+hod, pxw, h);
+ ox = x;
+ oy = y;
+ }
+ else
+ {
+ tt -= aa*((y<<1)-3);
+ st -= (aa<<1)*(--y);
+ }
+ }
+ this.mkDiv(cx-a, cy-oy, s, (oy<<1)+hod);
+ this.mkDiv(cx+a+wod-s+1, cy-oy, s, (oy<<1)+hod);
+ }
+
+ else
+ {
+ var _a = (width-((s-1)<<1))>>1,
+ _b = (height-((s-1)<<1))>>1,
+ _x = 0, _y = _b,
+ _aa = (_a*_a)<<1, _bb = (_b*_b)<<1,
+ _st = (_aa>>1)*(1-(_b<<1)) + _bb,
+ _tt = (_bb>>1) - _aa*((_b<<1)-1),
+
+ pxl = new Array(),
+ pxt = new Array(),
+ _pxb = new Array();
+ pxl[0] = 0;
+ pxt[0] = b;
+ _pxb[0] = _b-1;
+ while (y > 0)
+ {
+ if (st < 0)
+ {
+ st += bb*((x<<1)+3);
+ tt += (bb<<1)*(++x);
+ pxl[pxl.length] = x;
+ pxt[pxt.length] = y;
+ }
+ else if (tt < 0)
+ {
+ st += bb*((x<<1)+3) - (aa<<1)*(y-1);
+ tt += (bb<<1)*(++x) - aa*(((y--)<<1)-3);
+ pxl[pxl.length] = x;
+ pxt[pxt.length] = y;
+ }
+ else
+ {
+ tt -= aa*((y<<1)-3);
+ st -= (aa<<1)*(--y);
+ }
+
+ if (_y > 0)
+ {
+ if (_st < 0)
+ {
+ _st += _bb*((_x<<1)+3);
+ _tt += (_bb<<1)*(++_x);
+ _pxb[_pxb.length] = _y-1;
+ }
+ else if (_tt < 0)
+ {
+ _st += _bb*((_x<<1)+3) - (_aa<<1)*(_y-1);
+ _tt += (_bb<<1)*(++_x) - _aa*(((_y--)<<1)-3);
+ _pxb[_pxb.length] = _y-1;
+ }
+ else
+ {
+ _tt -= _aa*((_y<<1)-3);
+ _st -= (_aa<<1)*(--_y);
+ _pxb[_pxb.length-1]--;
+ }
+ }
+ }
+
+ var ox = 0, oy = b,
+ _oy = _pxb[0],
+ l = pxl.length,
+ w, h;
+ for (var i = 0; i < l; i++)
+ {
+ if (typeof _pxb[i] != "undefined")
+ {
+ if (_pxb[i] < _oy || pxt[i] < oy)
+ {
+ x = pxl[i];
+ this.mkOvQds(cx, cy, -x+1, ox+wod, -oy, _oy+hod, x-ox, oy-_oy);
+ ox = x;
+ oy = pxt[i];
+ _oy = _pxb[i];
+ }
+ }
+ else
+ {
+ x = pxl[i];
+ this.mkDiv(cx-x+1, cy-oy, 1, (oy<<1)+hod);
+ this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
+ ox = x;
+ oy = pxt[i];
+ }
+ }
+ this.mkDiv(cx-a, cy-oy, 1, (oy<<1)+hod);
+ this.mkDiv(cx+ox+wod, cy-oy, 1, (oy<<1)+hod);
+ }
+}
+
+
+function mkOvDott(left, top, width, height)
+{
+ var a = width>>1, b = height>>1,
+ wod = width&1, hod = height&1,
+ cx = left+a, cy = top+b,
+ x = 0, y = b,
+ aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
+ st = (aa2>>1)*(1-(b<<1)) + bb,
+ tt = (bb>>1) - aa2*((b<<1)-1),
+ drw = true;
+ while (y > 0)
+ {
+ if (st < 0)
+ {
+ st += bb*((x<<1)+3);
+ tt += (bb<<1)*(++x);
+ }
+ else if (tt < 0)
+ {
+ st += bb*((x<<1)+3) - aa4*(y-1);
+ tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
+ }
+ else
+ {
+ tt -= aa2*((y<<1)-3);
+ st -= aa4*(--y);
+ }
+ if (drw) this.mkOvQds(cx, cy, -x, x+wod, -y, y+hod, 1, 1);
+ drw = !drw;
+ }
+}
+
+
+function mkRect(x, y, w, h)
+{
+ var s = this.stroke;
+ this.mkDiv(x, y, w, s);
+ this.mkDiv(x+w, y, s, h);
+ this.mkDiv(x, y+h, w+s, s);
+ this.mkDiv(x, y+s, s, h-s);
+}
+
+
+function mkRectDott(x, y, w, h)
+{
+ this.drawLine(x, y, x+w, y);
+ this.drawLine(x+w, y, x+w, y+h);
+ this.drawLine(x, y+h, x+w, y+h);
+ this.drawLine(x, y, x, y+h);
+}
+
+
+function jsgFont()
+{
+ this.PLAIN = 'font-weight:normal;';
+ this.BOLD = 'font-weight:bold;';
+ this.ITALIC = 'font-style:italic;';
+ this.ITALIC_BOLD = this.ITALIC + this.BOLD;
+ this.BOLD_ITALIC = this.ITALIC_BOLD;
+}
+var Font = new jsgFont();
+
+
+function jsgStroke()
+{
+ this.DOTTED = -1;
+}
+var Stroke = new jsgStroke();
+
+
+function jsGraphics(id, wnd)
+{
+ this.setColor = new Function('arg', 'this.color = arg.toLowerCase();');
+
+ this.setStroke = function(x)
+ {
+ this.stroke = x;
+ if (!(x+1))
+ {
+ this.drawLine = mkLinDott;
+ this.mkOv = mkOvDott;
+ this.drawRect = mkRectDott;
+ }
+ else if (x-1 > 0)
+ {
+ this.drawLine = mkLin2D;
+ this.mkOv = mkOv2D;
+ this.drawRect = mkRect;
+ }
+ else
+ {
+ this.drawLine = mkLin;
+ this.mkOv = mkOv;
+ this.drawRect = mkRect;
+ }
+ };
+
+
+ this.setPrintable = function(arg)
+ {
+ this.printable = arg;
+ if (jg_fast)
+ {
+ this.mkDiv = mkDivIe;
+ this.htmRpc = arg? htmPrtRpc : htmRpc;
+ }
+ else this.mkDiv = jg_n4? mkLyr : arg? mkDivPrt : mkDiv;
+ };
+
+
+ this.setFont = function(fam, sz, sty)
+ {
+ this.ftFam = fam;
+ this.ftSz = sz;
+ this.ftSty = sty || Font.PLAIN;
+ };
+
+
+ this.drawPolyline = this.drawPolyLine = function(x, y, s)
+ {
+ for (var i=0 ; i<x.length-1 ; i++ )
+ this.drawLine(x[i], y[i], x[i+1], y[i+1]);
+ };
+
+
+ this.fillRect = function(x, y, w, h)
+ {
+ this.mkDiv(x, y, w, h);
+ };
+
+
+ this.drawPolygon = function(x, y)
+ {
+ this.drawPolyline(x, y);
+ this.drawLine(x[x.length-1], y[x.length-1], x[0], y[0]);
+ };
+
+
+ this.drawEllipse = this.drawOval = function(x, y, w, h)
+ {
+ this.mkOv(x, y, w, h);
+ };
+
+
+ this.fillEllipse = this.fillOval = function(left, top, w, h)
+ {
+ var a = (w -= 1)>>1, b = (h -= 1)>>1,
+ wod = (w&1)+1, hod = (h&1)+1,
+ cx = left+a, cy = top+b,
+ x = 0, y = b,
+ ox = 0, oy = b,
+ aa2 = (a*a)<<1, aa4 = aa2<<1, bb = (b*b)<<1,
+ st = (aa2>>1)*(1-(b<<1)) + bb,
+ tt = (bb>>1) - aa2*((b<<1)-1),
+ pxl, dw, dh;
+ if (w+1) while (y > 0)
+ {
+ if (st < 0)
+ {
+ st += bb*((x<<1)+3);
+ tt += (bb<<1)*(++x);
+ }
+ else if (tt < 0)
+ {
+ st += bb*((x<<1)+3) - aa4*(y-1);
+ pxl = cx-x;
+ dw = (x<<1)+wod;
+ tt += (bb<<1)*(++x) - aa2*(((y--)<<1)-3);
+ dh = oy-y;
+ this.mkDiv(pxl, cy-oy, dw, dh);
+ this.mkDiv(pxl, cy+y+hod, dw, dh);
+ ox = x;
+ oy = y;
+ }
+ else
+ {
+ tt -= aa2*((y<<1)-3);
+ st -= aa4*(--y);
+ }
+ }
+ this.mkDiv(cx-a, cy-oy, w+1, (oy<<1)+hod);
+ };
+
+
+/* fillPolygon method, implemented by Matthieu Haller.
+This javascript function is an adaptation of the gdImageFilledPolygon for Walter Zorn lib.
+C source of GD 1.8.4 found at http://www.boutell.com/gd/
+
+THANKS to Kirsten Schulz for the polygon fixes!
+
+The intersection finding technique of this code could be improved
+by remembering the previous intertersection, and by using the slope.
+That could help to adjust intersections to produce a nice
+interior_extrema. */
+ this.fillPolygon = function(array_x, array_y)
+ {
+ var i;
+ var y;
+ var miny, maxy;
+ var x1, y1;
+ var x2, y2;
+ var ind1, ind2;
+ var ints;
+
+ var n = array_x.length;
+
+ if (!n) return;
+
+
+ miny = array_y[0];
+ maxy = array_y[0];
+ for (i = 1; i < n; i++)
+ {
+ if (array_y[i] < miny)
+ miny = array_y[i];
+
+ if (array_y[i] > maxy)
+ maxy = array_y[i];
+ }
+ for (y = miny; y <= maxy; y++)
+ {
+ var polyInts = new Array();
+ ints = 0;
+ for (i = 0; i < n; i++)
+ {
+ if (!i)
+ {
+ ind1 = n-1;
+ ind2 = 0;
+ }
+ else
+ {
+ ind1 = i-1;
+ ind2 = i;
+ }
+ y1 = array_y[ind1];
+ y2 = array_y[ind2];
+ if (y1 < y2)
+ {
+ x1 = array_x[ind1];
+ x2 = array_x[ind2];
+ }
+ else if (y1 > y2)
+ {
+ y2 = array_y[ind1];
+ y1 = array_y[ind2];
+ x2 = array_x[ind1];
+ x1 = array_x[ind2];
+ }
+ else continue;
+
+ // modified 11. 2. 2004 Walter Zorn
+ if ((y >= y1) && (y < y2))
+ polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
+
+ else if ((y == maxy) && (y > y1) && (y <= y2))
+ polyInts[ints++] = Math.round((y-y1) * (x2-x1) / (y2-y1) + x1);
+ }
+ polyInts.sort(integer_compare);
+ for (i = 0; i < ints; i+=2)
+ this.mkDiv(polyInts[i], y, polyInts[i+1]-polyInts[i]+1, 1);
+ }
+ };
+
+
+ this.drawString = function(txt, x, y)
+ {
+ this.htm += '<div style="position:absolute;white-space:nowrap;'+
+ 'left:' + x + 'px;'+
+ 'top:' + y + 'px;'+
+ 'font-family:' + this.ftFam + ';'+
+ 'font-size:' + this.ftSz + ';'+
+ 'color:' + this.color + ';' + this.ftSty + '">'+
+ txt +
+ '<\/div>';
+ };
+
+
+/* drawStringRect() added by Rick Blommers.
+Allows to specify the size of the text rectangle and to align the
+text both horizontally (e.g. right) and vertically within that rectangle */
+ this.drawStringRect = function(txt, x, y, width, halign)
+ {
+ this.htm += '<div style="position:absolute;overflow:hidden;'+
+ 'left:' + x + 'px;'+
+ 'top:' + y + 'px;'+
+ 'width:'+width +'px;'+
+ 'text-align:'+halign+';'+
+ 'font-family:' + this.ftFam + ';'+
+ 'font-size:' + this.ftSz + ';'+
+ 'color:' + this.color + ';' + this.ftSty + '">'+
+ txt +
+ '<\/div>';
+ };
+
+
+ this.drawImage = function(imgSrc, x, y, w, h, a)
+ {
+ this.htm += '<div style="position:absolute;'+
+ 'left:' + x + 'px;'+
+ 'top:' + y + 'px;'+
+ 'width:' + w + ';'+
+ 'height:' + h + ';">'+
+ '<img src="' + imgSrc + '" width="' + w + '" height="' + h + '"' + (a? (' '+a) : '') + '>'+
+ '<\/div>';
+ };
+
+
+ this.clear = function()
+ {
+ this.htm = "";
+ if (this.cnv) this.cnv.innerHTML = this.defhtm;
+ };
+
+
+ this.mkOvQds = function(cx, cy, xl, xr, yt, yb, w, h)
+ {
+ this.mkDiv(xr+cx, yt+cy, w, h);
+ this.mkDiv(xr+cx, yb+cy, w, h);
+ this.mkDiv(xl+cx, yb+cy, w, h);
+ this.mkDiv(xl+cx, yt+cy, w, h);
+ };
+
+ this.setStroke(1);
+ this.setFont('verdana,geneva,helvetica,sans-serif', String.fromCharCode(0x31, 0x32, 0x70, 0x78), Font.PLAIN);
+ this.color = '#000000';
+ this.htm = '';
+ this.wnd = wnd || window;
+
+ if (!(jg_ie || jg_dom || jg_ihtm)) chkDHTM();
+ if (typeof id != 'string' || !id) this.paint = pntDoc;
+ else
+ {
+ this.cnv = document.all? (this.wnd.document.all[id] || null)
+ : document.getElementById? (this.wnd.document.getElementById(id) || null)
+ : null;
+ this.defhtm = (this.cnv && this.cnv.innerHTML)? this.cnv.innerHTML : '';
+ this.paint = jg_dom? pntCnvDom : jg_ie? pntCnvIe : jg_ihtm? pntCnvIhtm : pntCnv;
+ }
+
+ this.setPrintable(false);
+}
+
+
+
+function integer_compare(x,y)
+{
+ return (x < y) ? -1 : ((x > y)*1);
+}
+
--- /dev/null
+<h3>Plugin概要 <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13"></a> <a href="http://cles.jp/"><img border="0" src="http://blog.cles.jp/cles.png" alt="Powered by CLES" /></a></h3>
+
+<h3>使い方</h3>
+<p>下記のURLを参考に設定を行ってください。</p>
+<ul>
+ <li><a href="http://blog.cles.jp/np_cles/category/31/subcatid/13">作者サイト</a></li>
+ <li><a href="http://japan.nucleuscms.org/wiki/plugins:clap">plugins:clap [Nucleus CMS Japan Wiki]</a></li>
+</ul>
+
+<h3>バージョン履歴</h3>
+<p>新バージョンは<a href="http://blog.cles.jp/np_cles/">NP_cles()</a>で確認してください。</p>
+<ul>
+ <li>Version 1.6: (2008/05/18)</li>
+ <li> [Fixed] カテゴリの関連付けがおかしくなる問題を再度修正</li>
+
+ <li>Version 1.5: (2007/11/11)</li>
+ <li> [Added] メール内にhostname, useragent, refererを表示できるようにした</li>
+ <li> [Fixed] mysql_query()をsql_query()に変更</li>
+ <li> [Added] カテゴリごとに表示するコンテンツ・テンプレートが指定できるようにした</li>
+ <li> [Changed] ログインしている場合にカウントしないようにした</li>
+ <li> [Fixed] カテゴリの関連付けがおかしくなる問題に対応した (1.5.1)</li>
+
+ <li>Version 1.4: (2007/03/17)</li>
+ <li> [Added] カウント表示の日付での絞り込み</li>
+ <li> [Fixed] カウント表示時にブログでの絞込みが効いていない不具合を修正</li>
+ <li> [Fixed] バックアップが正常に行われない不具合を修正</li>
+ <li> [Fixed] 拍手ボタンを押した後にエラーが表示される問題を修正</li>
+ <li> [Fixed] Thanksページのコンテンツごとのプレビューがランダムになっていた問題を修正</li>
+
+ <li>Version 1.3: (2006/11/26)</li>
+ <li> [Added] Ticket処理を追加(CSRF対策)</li>
+ <li> [Added] グラフ表示機能を追加</li>
+ <li> [Added] 管理画面にアイコンを追加</li>
+
+ <li>Version 1.2: (2005/09/30)</li>
+ <li> [Fixed] <%Clap(actionurl)%>の不具合を修正</li>
+ <li> [Fixed] 管理画面の不具合を修正</li>
+ <li> [Fixed] セキュリティの向上</li>
+
+ <li>Version 1.1: (2006/05/20)</li>
+ <li> [Changed] Thanksページの内容の設定をプラグインオプションから、管理画面に移行</li>
+ <li> [Added]プレビュー機能を追加</li>
+
+ <li>Version 1.0: (2006/05/05)</li>
+ <li> [NEW] 初版公開</li>
+</ul>
+
+<h3>開発者について</h3>
+
+<ul>
+ <li><a href="http://blog.cles.jp/member/1">hsur</a> (<a href="http://blog.cles.jp/">cles::blog</a>)</li>
+ <li>ドネーションや仕事のご依頼も歓迎します。</li>
+</ul>
\ No newline at end of file
--- /dev/null
+<?php
+// vim: tabstop=2:shiftwidth=2
+
+/**
+ * index.php ($Revision: 1.1 $)
+ *
+ * by hsur ( http://blog.cles.jp/np_cles )
+ * $Id: index.php,v 1.1 2008-05-17 19:11:10 hsur Exp $
+*/
+
+/*
+ * Copyright (C) 2005-2007 CLES. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
+ * permission to link the code of this program with those files in the PEAR
+ * library that are licensed under the PHP License (or with modified versions
+ * of those files that use the same license as those files), and distribute
+ * linked combinations including the two. You must obey the GNU General Public
+ * License in all respects for all of the code used other than those files in
+ * the PEAR library that are licensed under the PHP License. If you modify
+ * this file, you may extend this exception to your version of the file,
+ * but you are not obligated to do so. If you do not wish to do so, delete
+ * this exception statement from your version.
+*/
+
+$strRel = '../../../';
+include ($strRel.'config.php');
+include ($DIR_LIBS.'PLUGINADMIN.php');
+
+require_once($DIR_PLUGINS . 'sharedlibs/sharedlibs.php');
+require_once('cles/Feedback.php');
+require_once('cles/Template.php');
+
+sendContentType('application/xhtml+xml', 'admin-clap', _CHARSET);
+
+// create the admin area page
+$oPluginAdmin = new PluginAdmin('Clap');
+$oPluginAdmin->start();
+$fb =& new cles_Feedback($oPluginAdmin);
+
+if (!($member->isLoggedIn() && $member->isAdmin())){
+ echo '<p>' . _ERROR_DISALLOWED . '</p>';
+ $oPluginAdmin->end();
+ exit;
+}
+
+//action
+$action = requestVar('action');
+$aActionsNotToCheck = array(
+ '',
+ 'report',
+);
+if (!in_array($action, $aActionsNotToCheck)) {
+ if (!$manager->checkTicket()) doError(_ERROR_BADTICKET);
+}
+
+$templateEngine =& new cles_Template(dirname(__FILE__).'/template');
+define('NP_CLAP_TEMPLATEDIR_INDEX', 'index');
+$tplVars = array(
+ 'indexurl' => serverVar('PHP_SELF'),
+ 'itemperpage' => '20',
+ 'optionurl' => $CONF['AdminURL'] . 'index.php?action=pluginoptions&plugid=' . $oPluginAdmin->plugin->getid(),
+ 'actionurl' => $CONF['ActionURL'],
+ 'ticket' => $manager->_generateTicket(),
+ 'plugindirurl' => $oPluginAdmin->plugin->getAdminURL(),
+);
+
+// menu
+$menu = $templateEngine->fetch('menu', NP_CLAP_TEMPLATEDIR_INDEX);
+echo $templateEngine->fill($menu, $tplVars, false);
+
+switch ($action) {
+ case 'delete' :
+ if( is_numeric(requestVar('id')) ){
+ $oPluginAdmin->plugin->deleteClap(intRequestVar('id'));
+ }
+ $action = 'detail';
+ break;
+ case 'thanksdelete' :
+ if( is_numeric(requestVar('id')) ){
+ $oPluginAdmin->plugin->deleteThanksMsg(intRequestVar('id'));
+ }
+ $action = 'thanksmsg';
+ break;
+ case 'thankssave' :
+ if( is_numeric(requestVar('id')) || requestVar('id') == 'new' ){
+ $msg['id'] = requestVar('id');
+ $msg['comment'] = requestVar('comment');
+ $msg['image'] = requestVar('image');
+ $oPluginAdmin->plugin->setThanksMsg($msg);
+
+ $oPluginAdmin->plugin->setAssociatedCategories(requestVar('id'), requestArray('assoc'), requestVar('assoc_etc'));
+ }
+ $action = 'thanksmsg';
+ case 'docorrect' :
+ $tplVars['message'] = $oPluginAdmin->plugin->_correctBrokenThanksCategory();
+ $action = 'thanksmsg';
+ break;
+}
+
+switch ($action) {
+ case 'convert' :
+ $content = $templateEngine->fetch('convert', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+
+ case 'doconvert' :
+ $query = 'DELETE FROM ' . sql_table('plugin_clap') . ' where ipaddr = \'127.0.0.2\' ';
+ sql_query($query);
+
+ $query = 'SELECT inumber, ikarmapos FROM ' . sql_table('item') . ' where ikarmapos > 0';
+ $res = sql_query($query);
+
+ while( $row = mysql_fetch_assoc($res) ){
+ for($i = $row['ikarmapos'] ; $i > 0 ; $i -= 1 ){
+ $query = sprintf('INSERT INTO ' . sql_table('plugin_clap')
+ . ' ( `itemkey`, `timestamp`, `ipaddr` ) '
+ . " values('%s', '1970-01-01 00:00:00', '127.0.0.2') "
+ , mysql_real_escape_string( $row['inumber'] )
+ );
+ sql_query($query);
+ }
+ }
+
+ $content = $templateEngine->fetch('doconvert', NP_CLAP_TEMPLATEDIR_INDEX); \r $templateEngine->fill($content, $tplVars, null);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+
+ case 'reset' :
+ $content = $templateEngine->fetch('reset', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+
+ case 'doreset' :
+ $query = 'TRUNCATE TABLE ' . sql_table('plugin_clap');
+ sql_query($query);
+
+ $query = 'TRUNCATE TABLE ' . sql_table('plugin_clap_comment');
+ sql_query($query);
+
+ $content = $templateEngine->fetch('doreset', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+
+ case 'report' :
+ $fb->printForm('');
+ break;
+
+ case 'detail' :
+ $tplVars['key'] = requestVar('key') ? requestVar('key') : null;
+ if( ! $tplVars['key'] ){
+ $tplVars['message'] = '"key" is not set.';
+ $content = $templateEngine->fetch('error', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+ }
+ $tplVars['offset'] = intRequestVar('offset') ? intRequestVar('offset') : 0;
+
+ $res = $oPluginAdmin->plugin->getDetail($tplVars['key'], $tplVars['offset'], 99999999);
+ $tplVars['rowcount'] = mysql_num_rows($res);
+
+ $res = $oPluginAdmin->plugin->getDetail($tplVars['key'], $tplVars['offset'], $tplVars['itemperpage']);
+ if( $tplVars['itemperpage'] + $tplVars['offset'] < $tplVars['rowcount'] ){
+ $content = $templateEngine->fetch('detail_nextbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['next_offset'] = $tplVars['offset'] + $tplVars['itemperpage'];
+ $tplVars['next_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+ if( $tplVars['offset'] > 0 ){
+ $content = $templateEngine->fetch('detail_prevbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['prev_offset'] = $tplVars['offset'] - $tplVars['itemperpage'];
+ $tplVars['prev_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+
+ $content = $templateEngine->fetch('detail_header', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ while( $row = mysql_fetch_assoc($res) ){
+ $row = array_merge($tplVars, $row);
+ $row['ipaddr'] = ( $row['ipaddr'] == '127.0.0.2' ) ? 'karma' : $row['ipaddr'];
+ $row['comment'] = nl2br(htmlspecialchars($row['comment'], ENT_QUOTES));
+ $content = $templateEngine->fetch('detail_item', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $row, null);
+ }
+
+ $content = $templateEngine->fetch('detail_footer', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ break;
+
+ case 'messagelist' :
+ $tplVars['offset'] = intRequestVar('offset') ? intRequestVar('offset') : 0;
+ $res = $oPluginAdmin->plugin->getMessageList($tplVars['offset'], 99999999);
+ $tplVars['rowcount'] = mysql_num_rows($res);
+
+ $res = $oPluginAdmin->plugin->getMessageList($tplVars['offset'], $tplVars['itemperpage']);
+ if( $tplVars['itemperpage'] + $tplVars['offset'] < $tplVars['rowcount'] ){
+ $content = $templateEngine->fetch('messagelist_nextbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['next_offset'] = $tplVars['offset'] + $tplVars['itemperpage'];
+ $tplVars['next_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+ if( $tplVars['offset'] > 0 ){
+ $content = $templateEngine->fetch('messagelist_prevbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['prev_offset'] = $tplVars['offset'] - $tplVars['itemperpage'];
+ $tplVars['prev_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+
+ $content = $templateEngine->fetch('messagelist_header', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ while( $row = mysql_fetch_assoc($res) ){
+ $row = array_merge($tplVars, $row);
+ $row['comment'] = nl2br(htmlspecialchars($row['comment'], ENT_QUOTES));
+ $content = $templateEngine->fetch('messagelist_item', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $row, null);
+ }
+
+ $content = $templateEngine->fetch('messagelist_footer', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ break;
+
+ case 'thanksmsg' :
+ $tplVars['offset'] = intRequestVar('offset') ? intRequestVar('offset') : 0;
+ $res = $oPluginAdmin->plugin->getThanksMsgList($tplVars['offset'], 99999999);
+ $tplVars['rowcount'] = mysql_num_rows($res);
+
+ $res = $oPluginAdmin->plugin->getThanksMsgList($tplVars['offset'], $tplVars['itemperpage']);
+ if( $tplVars['itemperpage'] + $tplVars['offset'] < $tplVars['rowcount'] ){
+ $content = $templateEngine->fetch('thanksmsg_nextbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['next_offset'] = $tplVars['offset'] + $tplVars['itemperpage'];
+ $tplVars['next_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+ if( $tplVars['offset'] > 0 ){
+ $content = $templateEngine->fetch('thanksmsg_prevbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['prev_offset'] = $tplVars['offset'] - $tplVars['itemperpage'];
+ $tplVars['prev_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+
+ $content = $templateEngine->fetch('thanksmsg_header', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ while( $row = mysql_fetch_assoc($res) ){
+ $row = array_merge($tplVars, $row);
+ if( trim($row['image']) )
+ $row['image'] = '<img border="0" src="'.$oPluginAdmin->plugin->getAdminURL().'silk/picture.png" />';
+ else
+ $row['image'] = '<img border="0" src="'.$oPluginAdmin->plugin->getAdminURL().'silk/picture_empty.png" />';
+ $row['comment'] = shorten(strip_tags($row['comment']),200,'...');
+
+ $content = $templateEngine->fetch('thanksmsg_item', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $row, null);
+ }
+
+ $content = $templateEngine->fetch('thanksmsg_footer', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ break;
+
+ case 'thanksedit':
+ if( ! ( is_numeric(requestVar('id')) || requestVar('id') == 'new' ) ){
+ $tplVars['message'] = '"id" is not set.';
+ $content = $templateEngine->fetch('error', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+ }
+
+ $assoc = array();
+ if( requestVar('id') != 'new' ){
+ $msg = $oPluginAdmin->plugin->getThanksMsg(intRequestVar('id'));
+ $assoc = $oPluginAdmin->plugin->getAssociatedCategoriesByThanksId(intRequestVar('id'));
+ } else {
+ $msg['id'] = 'new';
+ $msg['image'] = '<img src="#" alt="" title="" />';
+ $assoc[] = NP_CLAP_GLOBALKEY;
+ }
+ $msg = array_merge($tplVars, $msg);
+
+ if( in_array(NP_CLAP_GLOBALKEY, $assoc) )
+ $msg['assoc'] = '<input type="checkbox" name="assoc[]" value="'.NP_CLAP_GLOBALKEY.'" checked="checked" />'.NP_CLAP_GLOBALKEY.'<br />';
+ else
+ $msg['assoc'] = '<input type="checkbox" name="assoc[]" value="'.NP_CLAP_GLOBALKEY.'" />'.NP_CLAP_GLOBALKEY.'<br />';
+
+ $blogs = $oPluginAdmin->plugin->getBloglist();
+ $res = sql_query('SELECT catid, cname, cblog FROM '.sql_table('category').' ORDER BY cblog, catid');
+ if( @mysql_num_rows($res) > 0) {
+ $currrentBlog = null;
+ while( $o = mysql_fetch_object($res) ){
+ if( $currrentBlog != $o->cblog){
+ $currrentBlog = $o->cblog;
+ $msg['assoc'] .= sprintf(NP_CLAP_ALLCHECK, $o->cblog, $o->cblog);
+ }
+
+ if( in_array($o->catid, $assoc) )
+ $tpl_checkbox = '<input id="%s" type="checkbox" name="assoc[]" value="%s" checked="checked" />%s<br />';
+ else
+ $tpl_checkbox = '<input id="%s" type="checkbox" name="assoc[]" value="%s" />%s<br />';
+ $msg['assoc'] .= sprintf($tpl_checkbox,
+ htmlspecialchars($o->cblog.'_'.$o->catid, ENT_QUOTES),
+ htmlspecialchars($o->catid, ENT_QUOTES),
+ htmlspecialchars($o->cname.' ( '.$blogs[$o->cblog].' )', ENT_QUOTES)
+ );
+ }
+ }
+
+ $assoc_etc = array();
+ foreach ( $assoc as $key) {
+ if( $key == NP_CLAP_GLOBALKEY ) continue;
+ if( is_numeric($key) ) continue;
+ $assoc_etc[] = $key;
+ }
+ $msg['assoc_etc'] = implode(',', $assoc_etc);
+
+ $content = $templateEngine->fetch('thanksedit', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $msg, null);
+ break;
+
+ case 'chart' :
+ $content = $templateEngine->fetch('chart', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+ break;
+
+ case 'overview' :
+ default :
+ $tplVars['offset'] = intRequestVar('offset') ? intRequestVar('offset') : 0;
+ $tplVars['blog'] = requestVar('blog') ? requestVar('blog') : '' ;
+
+ $res = $oPluginAdmin->plugin->getOverview($tplVars['blog'], $tplVars['offset'], 99999999);
+ $tplVars['rowcount'] = mysql_num_rows($res);
+
+ $res = $oPluginAdmin->plugin->getOverview($tplVars['blog'], $tplVars['offset'], $tplVars['itemperpage']);
+ if( $tplVars['itemperpage'] + $tplVars['offset'] < $tplVars['rowcount'] ){
+ $content = $templateEngine->fetch('overview_nextbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['next_offset'] = $tplVars['offset'] + $tplVars['itemperpage'];
+ $tplVars['next_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+
+ if( $tplVars['offset'] > 0 ){
+ $content = $templateEngine->fetch('overview_prevbutton', NP_CLAP_TEMPLATEDIR_INDEX);
+ $tplVars['prev_offset'] = $tplVars['offset'] - $tplVars['itemperpage'];
+ $tplVars['prev_button'] = $templateEngine->fill($content, $tplVars, null);
+ }
+
+ $blogs = $oPluginAdmin->plugin->getBloglist();
+ $tplVars['blogselect'] .= '<form method="post" name="blogselect" action="'.$tplVars['indexurl'].'" style="display:inline" >';
+ $tplVars['blogselect'] .= '<input type="hidden" name="action" value="overview" />';
+ $tplVars['blogselect'] .= '<input type="hidden" name="offset" value="0" />';
+ $tplVars['blogselect'] .= '<input type="hidden" name="ticket" value="' . $tplVars['ticket'] . '" />';
+ $tplVars['blogselect'] .= '<select name="blog" onchange="javascript:submit()">';
+ $tplVars['blogselect'] .= '<option value="">ALL</option>';
+ $tplVars['blogselect'] .= ( $tplVars['blog'] == 'global' ) ? '<option value="global" selected="selected">global</option>' : '<option value="global">global</option>';
+ foreach($blogs as $id => $name){
+ $tplVars['blogselect'] .= ( $tplVars['blog'] == $id ) ? '<option value="'.intval($id).'" selected="selected">'.htmlspecialchars($name,ENT_QUOTES).'</option>' : '<option value="'.intval($id).'">'.htmlspecialchars($name,ENT_QUOTES).'</option>';
+ }
+ $tplVars['blogselect'] .= '</select></form>';
+
+ $content = $templateEngine->fetch('overview_header', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ while( $row = mysql_fetch_assoc($res) ){
+ $row = array_merge($tplVars, $row);
+ $row['blogname'] = $blogs[$row['blog']];
+ $content = $templateEngine->fetch('overview_item', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $row, null);
+ }
+
+ $content = $templateEngine->fetch('overview_footer', NP_CLAP_TEMPLATEDIR_INDEX);
+ echo $templateEngine->fill($content, $tplVars, null);
+
+ break;
+}
+
+echo '<div align="right">Powered by <a href="http://www.famfamfam.com/lab/icons/silk/">Silk icon</a></div>';
+
+$oPluginAdmin->end();
--- /dev/null
+function drawUsingKeyAndParam(key, param) {
+ var c = new Chart(document.getElementById(key));
+ c.setDefaultType(CHART_LINE);
+ c.setShowLegend(true);
+
+ m = param['max'];
+ if( m > 30 ) m = 30;
+
+ c.setGridDensity(param['length']-0, m);
+ c.setVerticalRange(0, param['max']-0);
+
+ c.setHorizontalLabels( param['label']);
+ c.add('Total Count','#40ff40', param['count']);
+ c.add('IP Count','#4040FF', param['ipcount']);
+ c.add('Msg Count','#FF4040', param['msgcount']);
+ c.draw();
+}
+
+function updateMyChart(){
+ var yearSelect = $('year');
+ var monthSelect = $('month');
+
+ var pars = '&ajax=1&action=plugin&name=Clap&type=chart';
+ pars = pars + '&year=' + yearSelect.options[yearSelect.selectedIndex].value;
+ pars = pars + '&month=' + monthSelect.options[monthSelect.selectedIndex].value;
+ pars = pars + '&ticket=' + ticket;
+ var myAjax = new Ajax.Request(
+ actionurl,
+ { method: 'get', parameters: pars, onSuccess: drawMyChart, onFailure: updateMyChartFailed }
+ );
+}
+
+function drawMyChart(originalRequest){
+ var param = eval( "(" + originalRequest.responseText + ")" );
+
+ drawUsingKeyAndParam('key', param['key']);
+ drawUsingKeyAndParam('daysOfMonth', param['dayOfMonth']);
+ drawUsingKeyAndParam('daysOfWeek', param['dayOfWeek']);
+ drawUsingKeyAndParam('hours', param['hours']);
+
+ var yearSelect = $('year');
+ var monthSelect = $('month');
+ var date = $('date');
+ date.innerHTML = yearSelect.options[yearSelect.selectedIndex].value + '/' + monthSelect.options[monthSelect.selectedIndex].value
+}
+
+function updateMyChartFailed(originalRequest){
+ var d = $('message');
+ d.innerHTML = 'Update Failed';
+}
+
+function setList(select, list, selected){
+ for(i=0;i<list.length;i++){
+ select.options[i] = new Option(list[i],list[i]);
+ }
+ select.selectedIndex = selected-0;
+}
+
+window.onload = function() {
+ ieCanvasInit('chart/iecanvas.htc');
+ var yearSelect = $('year');
+ var monthSelect = $('month');
+
+ var now = new Date();
+ var yearArr = new Array();
+ for(i=0;i<5;i++){
+ yearArr.push( 1900 + now.getYear() - i + "" );
+ }
+
+ setList(yearSelect, yearArr, 0);
+ setList(monthSelect, ["1","2","3","4","5","6","7","8","9","10","11","12"], now.getMonth() );
+ yearSelect.onchange = updateMyChart;
+ monthSelect.onchange = updateMyChart;
+
+ updateMyChart();
+};
--- /dev/null
+/* Prototype JavaScript framework, version 1.4.0
+ * (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.4.0',
+ ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
+
+ emptyFunction: function() {},
+ K: function(x) {return x}
+}
+
+var Class = {
+ create: function() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+ }
+}
+
+var Abstract = new Object();
+
+Object.extend = function(destination, source) {
+ for (property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+Object.inspect = function(object) {
+ try {
+ if (object == undefined) return 'undefined';
+ if (object == null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+}
+
+Function.prototype.bind = function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+ var __method = this;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ var digits = this.toString(16);
+ if (this < 16) return '0' + digits;
+ return digits;
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ }
+});
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0; i < arguments.length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) {}
+ }
+
+ return returnValue;
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.callback();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+ var elements = new Array();
+
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+
+ if (arguments.length == 1)
+ return element;
+
+ elements.push(element);
+ }
+
+ return elements;
+}
+Object.extend(String.prototype, {
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(eval);
+ },
+
+ escapeHTML: function() {
+ var div = document.createElement('div');
+ var text = document.createTextNode(this);
+ div.appendChild(text);
+ return div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = document.createElement('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
+ },
+
+ toQueryParams: function() {
+ var pairs = this.match(/^\??(.*)$/)[1].split('&');
+ return pairs.inject({}, function(params, pairString) {
+ var pair = pairString.split('=');
+ params[pair[0]] = pair[1];
+ return params;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ camelize: function() {
+ var oStringList = this.split('-');
+ if (oStringList.length == 1) return oStringList[0];
+
+ var camelizedString = this.indexOf('-') == 0
+ ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+ : oStringList[0];
+
+ for (var i = 1, len = oStringList.length; i < len; i++) {
+ var s = oStringList[i];
+ camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+ }
+
+ return camelizedString;
+ },
+
+ inspect: function() {
+ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
+ }
+});
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+var $break = new Object();
+var $continue = new Object();
+
+var Enumerable = {
+ each: function(iterator) {
+ var index = 0;
+ try {
+ this._each(function(value) {
+ try {
+ iterator(value, index++);
+ } catch (e) {
+ if (e != $continue) throw e;
+ }
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ },
+
+ all: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!(iterator || Prototype.K)(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator) {
+ var result = true;
+ this.each(function(value, index) {
+ if (result = !!(iterator || Prototype.K)(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function (iterator) {
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(pattern, iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ var stringValue = value.toString();
+ if (stringValue.match(pattern))
+ results.push((iterator || Prototype.K)(value, index));
+ })
+ return results;
+ },
+
+ include: function(object) {
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inject: function(memo, iterator) {
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.collect(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value >= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator) {
+ var result;
+ this.each(function(value, index) {
+ value = (iterator || Prototype.K)(value, index);
+ if (value <= (result || value))
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator) {
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ ((iterator || Prototype.K)(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value, index) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator) {
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator) {
+ return this.collect(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.collect(Prototype.K);
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (typeof args.last() == 'function')
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ iterator(value = collections.pluck(index));
+ return value;
+ });
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+}
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray
+});
+var $A = Array.from = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+Object.extend(Array.prototype, Enumerable);
+
+Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0; i < this.length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != undefined || value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(value.constructor == Array ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ indexOf: function(object) {
+ for (var i = 0; i < this.length; i++)
+ if (this[i] == object) return i;
+ return -1;
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ shift: function() {
+ var result = this[0];
+ for (var i = 0; i < this.length - 1; i++)
+ this[i] = this[i + 1];
+ this.length--;
+ return result;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ }
+});
+var Hash = {
+ _each: function(iterator) {
+ for (key in this) {
+ var value = this[key];
+ if (typeof value == 'function') continue;
+
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ merge: function(hash) {
+ return $H(hash).inject($H(this), function(mergedHash, pair) {
+ mergedHash[pair.key] = pair.value;
+ return mergedHash;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ return pair.map(encodeURIComponent).join('=');
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ }
+}
+
+function $H(object) {
+ var hash = Object.extend({}, object || {});
+ Object.extend(hash, Enumerable);
+ Object.extend(hash, Hash);
+ return hash;
+}
+ObjectRange = Class.create();
+Object.extend(ObjectRange.prototype, Enumerable);
+Object.extend(ObjectRange.prototype, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ do {
+ iterator(value);
+ value = value.succ();
+ } while (this.include(value));
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+}
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+ function() {return new XMLHttpRequest()}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+}
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responderToAdd) {
+ if (!this.include(responderToAdd))
+ this.responders.push(responderToAdd);
+ },
+
+ unregister: function(responderToRemove) {
+ this.responders = this.responders.without(responderToRemove);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (responder[callback] && typeof responder[callback] == 'function') {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) {}
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() {
+ Ajax.activeRequestCount++;
+ },
+
+ onComplete: function() {
+ Ajax.activeRequestCount--;
+ }
+});
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+ setOptions: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ parameters: ''
+ }
+ Object.extend(this.options, options || {});
+ },
+
+ responseIsSuccess: function() {
+ return this.transport.status == undefined
+ || this.transport.status == 0
+ || (this.transport.status >= 200 && this.transport.status < 300);
+ },
+
+ responseIsFailure: function() {
+ return !this.responseIsSuccess();
+ }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(url, options) {
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+ this.request(url);
+ },
+
+ request: function(url) {
+ var parameters = this.options.parameters || '';
+ if (parameters.length > 0) parameters += '&_=';
+
+ try {
+ this.url = url;
+ if (this.options.method == 'get' && parameters.length > 0)
+ this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+ Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+ this.transport.open(this.options.method, this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) {
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+ }
+
+ this.setRequestHeaders();
+
+ var body = this.options.postBody ? this.options.postBody : parameters;
+ this.transport.send(this.options.method == 'post' ? body : null);
+
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ setRequestHeaders: function() {
+ var requestHeaders =
+ ['X-Requested-With', 'XMLHttpRequest',
+ 'X-Prototype-Version', Prototype.Version];
+
+ if (this.options.method == 'post') {
+ requestHeaders.push('Content-type',
+ 'application/x-www-form-urlencoded');
+
+ /* Force "Connection: close" for Mozilla browsers to work around
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
+ * header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType)
+ requestHeaders.push('Connection', 'close');
+ }
+
+ if (this.options.requestHeaders)
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+ for (var i = 0; i < requestHeaders.length; i += 2)
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState != 1)
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ header: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) {}
+ },
+
+ evalJSON: function() {
+ try {
+ return eval(this.header('X-JSON'));
+ } catch (e) {}
+ },
+
+ evalResponse: function() {
+ try {
+ return eval(this.transport.responseText);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ respondToReadyState: function(readyState) {
+ var event = Ajax.Request.Events[readyState];
+ var transport = this.transport, json = this.evalJSON();
+
+ if (event == 'Complete') {
+ try {
+ (this.options['on' + this.transport.status]
+ || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
+ Ajax.Responders.dispatch('on' + event, this, transport, json);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+ if (event == 'Complete')
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Updater = Class.create();
+
+Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
+ initialize: function(container, url, options) {
+ this.containers = {
+ success: container.success ? $(container.success) : $(container),
+ failure: container.failure ? $(container.failure) :
+ (container.success ? null : $(container))
+ }
+
+ this.transport = Ajax.getTransport();
+ this.setOptions(options);
+
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
+ this.options.onComplete = (function(transport, object) {
+ this.updateContent();
+ onComplete(transport, object);
+ }).bind(this);
+
+ this.request(url);
+ },
+
+ updateContent: function() {
+ var receiver = this.responseIsSuccess() ?
+ this.containers.success : this.containers.failure;
+ var response = this.transport.responseText;
+
+ if (!this.options.evalScripts)
+ response = response.stripScripts();
+
+ if (receiver) {
+ if (this.options.insertion) {
+ new this.options.insertion(receiver, response);
+ } else {
+ Element.update(receiver, response);
+ }
+ }
+
+ if (this.responseIsSuccess()) {
+ if (this.onComplete)
+ setTimeout(this.onComplete.bind(this), 10);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create();
+Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
+ initialize: function(container, url, options) {
+ this.setOptions(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = {};
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(request) {
+ if (this.options.decay) {
+ this.decay = (request.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = request.responseText;
+ }
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
+ this.decay * this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+document.getElementsByClassName = function(className, parentElement) {
+ var children = ($(parentElement) || document.body).getElementsByTagName('*');
+ return $A(children).inject([], function(elements, child) {
+ if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
+ elements.push(child);
+ return elements;
+ });
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Element) {
+ var Element = new Object();
+}
+
+Object.extend(Element, {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ }
+ },
+
+ hide: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = 'none';
+ }
+ },
+
+ show: function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var element = $(arguments[i]);
+ element.style.display = '';
+ }
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ },
+
+ update: function(element, html) {
+ $(element).innerHTML = html.stripScripts();
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
+ getHeight: function(element) {
+ element = $(element);
+ return element.offsetHeight;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).include(className);
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).add(className);
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return Element.classNames(element).remove(className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ for (var i = 0; i < element.childNodes.length; i++) {
+ var node = element.childNodes[i];
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ Element.remove(node);
+ }
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.match(/^\s*$/);
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var x = element.x ? element.x : element.offsetLeft,
+ y = element.y ? element.y : element.offsetTop;
+ window.scrollTo(x, y);
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ var value = element.style[style.camelize()];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style.camelize()];
+ }
+ }
+
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+ return value == 'auto' ? null : value;
+ },
+
+ setStyle: function(element, style) {
+ element = $(element);
+ for (name in style)
+ element.style[name.camelize()] = style[name];
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ if (Element.getStyle(element, 'display') != 'none')
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = '';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = 'none';
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element._overflow = element.style.overflow;
+ if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
+ element.style.overflow = 'hidden';
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return;
+ element.style.overflow = element._overflow;
+ element._overflow = undefined;
+ }
+});
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+ this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+ initialize: function(element, content) {
+ this.element = $(element);
+ this.content = content.stripScripts();
+
+ if (this.adjacency && this.element.insertAdjacentHTML) {
+ try {
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
+ } catch (e) {
+ if (this.element.tagName.toLowerCase() == 'tbody') {
+ this.insertContent(this.contentFromAnonymousTable());
+ } else {
+ throw e;
+ }
+ }
+ } else {
+ this.range = this.element.ownerDocument.createRange();
+ if (this.initializeRange) this.initializeRange();
+ this.insertContent([this.range.createContextualFragment(this.content)]);
+ }
+
+ setTimeout(function() {content.evalScripts()}, 10);
+ },
+
+ contentFromAnonymousTable: function() {
+ var div = document.createElement('div');
+ div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
+ return $A(div.childNodes[0].childNodes[0].childNodes);
+ }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
+ initializeRange: function() {
+ this.range.setStartBefore(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment, this.element);
+ }).bind(this));
+ }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(true);
+ },
+
+ insertContent: function(fragments) {
+ fragments.reverse(false).each((function(fragment) {
+ this.element.insertBefore(fragment, this.element.firstChild);
+ }).bind(this));
+ }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
+ initializeRange: function() {
+ this.range.selectNodeContents(this.element);
+ this.range.collapse(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.appendChild(fragment);
+ }).bind(this));
+ }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
+ initializeRange: function() {
+ this.range.setStartAfter(this.element);
+ },
+
+ insertContent: function(fragments) {
+ fragments.each((function(fragment) {
+ this.element.parentNode.insertBefore(fragment,
+ this.element.nextSibling);
+ }).bind(this));
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set(this.toArray().concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set(this.select(function(className) {
+ return className != classNameToRemove;
+ }).join(' '));
+ },
+
+ toString: function() {
+ return this.toArray().join(' ');
+ }
+}
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+var Field = {
+ clear: function() {
+ for (var i = 0; i < arguments.length; i++)
+ $(arguments[i]).value = '';
+ },
+
+ focus: function(element) {
+ $(element).focus();
+ },
+
+ present: function() {
+ for (var i = 0; i < arguments.length; i++)
+ if ($(arguments[i]).value == '') return false;
+ return true;
+ },
+
+ select: function(element) {
+ $(element).select();
+ },
+
+ activate: function(element) {
+ element = $(element);
+ element.focus();
+ if (element.select)
+ element.select();
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+ serialize: function(form) {
+ var elements = Form.getElements($(form));
+ var queryComponents = new Array();
+
+ for (var i = 0; i < elements.length; i++) {
+ var queryComponent = Form.Element.serialize(elements[i]);
+ if (queryComponent)
+ queryComponents.push(queryComponent);
+ }
+
+ return queryComponents.join('&');
+ },
+
+ getElements: function(form) {
+ form = $(form);
+ var elements = new Array();
+
+ for (tagName in Form.Element.Serializers) {
+ var tagElements = form.getElementsByTagName(tagName);
+ for (var j = 0; j < tagElements.length; j++)
+ elements.push(tagElements[j]);
+ }
+ return elements;
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name)
+ return inputs;
+
+ var matchingInputs = new Array();
+ for (var i = 0; i < inputs.length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) ||
+ (name && input.name != name))
+ continue;
+ matchingInputs.push(input);
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.blur();
+ element.disabled = 'true';
+ }
+ },
+
+ enable: function(form) {
+ var elements = Form.getElements(form);
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ element.disabled = '';
+ }
+ },
+
+ findFirstElement: function(form) {
+ return Form.getElements(form).find(function(element) {
+ return element.type != 'hidden' && !element.disabled &&
+ ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ Field.activate(Form.findFirstElement(form));
+ },
+
+ reset: function(form) {
+ $(form).reset();
+ }
+}
+
+Form.Element = {
+ serialize: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter) {
+ var key = encodeURIComponent(parameter[0]);
+ if (key.length == 0) return;
+
+ if (parameter[1].constructor != Array)
+ parameter[1] = [parameter[1]];
+
+ return parameter[1].map(function(value) {
+ return key + '=' + encodeURIComponent(value);
+ }).join('&');
+ }
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ var parameter = Form.Element.Serializers[method](element);
+
+ if (parameter)
+ return parameter[1];
+ }
+}
+
+Form.Element.Serializers = {
+ input: function(element) {
+ switch (element.type.toLowerCase()) {
+ case 'submit':
+ case 'hidden':
+ case 'password':
+ case 'text':
+ return Form.Element.Serializers.textarea(element);
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element);
+ }
+ return false;
+ },
+
+ inputSelector: function(element) {
+ if (element.checked)
+ return [element.name, element.value];
+ },
+
+ textarea: function(element) {
+ return [element.name, element.value];
+ },
+
+ select: function(element) {
+ return Form.Element.Serializers[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ },
+
+ selectOne: function(element) {
+ var value = '', opt, index = element.selectedIndex;
+ if (index >= 0) {
+ opt = element.options[index];
+ value = opt.value;
+ if (!value && !('value' in opt))
+ value = opt.text;
+ }
+ return [element.name, value];
+ },
+
+ selectMany: function(element) {
+ var value = new Array();
+ for (var i = 0; i < element.length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) {
+ var optValue = opt.value;
+ if (!optValue && !('value' in opt))
+ optValue = opt.text;
+ value.push(optValue);
+ }
+ }
+ return [element.name, value];
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+ initialize: function(element, frequency, callback) {
+ this.frequency = frequency;
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ onTimerEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = function() {}
+Abstract.EventObserver.prototype = {
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ var elements = Form.getElements(this.element);
+ for (var i = 0; i < elements.length; i++)
+ this.registerCallback(elements[i]);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ case 'password':
+ case 'text':
+ case 'textarea':
+ case 'select-one':
+ case 'select-multiple':
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+}
+
+Form.Element.EventObserver = Class.create();
+Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create();
+Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) {
+ var Event = new Object();
+}
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+
+ element: function(event) {
+ return event.target || event.srcElement;
+ },
+
+ isLeftClick: function(event) {
+ return (((event.which) && (event.which == 1)) ||
+ ((event.button) && (event.button == 1)));
+ },
+
+ pointerX: function(event) {
+ return event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
+ },
+
+ pointerY: function(event) {
+ return event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop));
+ },
+
+ stop: function(event) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ event.returnValue = false;
+ event.cancelBubble = true;
+ }
+ },
+
+ // find the first node with the given tagName, starting from the
+ // node the event was triggered on; traverses the DOM upwards
+ findElement: function(event, tagName) {
+ var element = Event.element(event);
+ while (element.parentNode && (!element.tagName ||
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
+ element = element.parentNode;
+ return element;
+ },
+
+ observers: false,
+
+ _observeAndCache: function(element, name, observer, useCapture) {
+ if (!this.observers) this.observers = [];
+ if (element.addEventListener) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.addEventListener(name, observer, useCapture);
+ } else if (element.attachEvent) {
+ this.observers.push([element, name, observer, useCapture]);
+ element.attachEvent('on' + name, observer);
+ }
+ },
+
+ unloadCache: function() {
+ if (!Event.observers) return;
+ for (var i = 0; i < Event.observers.length; i++) {
+ Event.stopObserving.apply(this, Event.observers[i]);
+ Event.observers[i][0] = null;
+ }
+ Event.observers = false;
+ },
+
+ observe: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.attachEvent))
+ name = 'keydown';
+
+ this._observeAndCache(element, name, observer, useCapture);
+ },
+
+ stopObserving: function(element, name, observer, useCapture) {
+ var element = $(element);
+ useCapture = useCapture || false;
+
+ if (name == 'keypress' &&
+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
+ || element.detachEvent))
+ name = 'keydown';
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, observer, useCapture);
+ } else if (element.detachEvent) {
+ element.detachEvent('on' + name, observer);
+ }
+ }
+});
+
+/* prevent memory leaks in IE */
+Event.observe(window, 'unload', Event.unloadCache, false);
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ realOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return [valueL, valueT];
+ },
+
+ offsetParent: function(element) {
+ if (element.offsetParent) return element.offsetParent;
+ if (element == document.body) return element;
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return element;
+
+ return document.body;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = this.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = this.realOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = this.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ clone: function(source, target) {
+ source = $(source);
+ target = $(target);
+ target.style.position = 'absolute';
+ var offsets = this.cumulativeOffset(source);
+ target.style.top = offsets[1] + 'px';
+ target.style.left = offsets[0] + 'px';
+ target.style.width = source.offsetWidth + 'px';
+ target.style.height = source.offsetHeight + 'px';
+ },
+
+ page: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent==document.body)
+ if (Element.getStyle(element,'position')=='absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ } while (element = element.parentNode);
+
+ return [valueL, valueT];
+ },
+
+ clone: function(source, target) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || {})
+
+ // find page position of source
+ source = $(source);
+ var p = Position.page(source);
+
+ // find coordinate system to use
+ target = $(target);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(target,'position') == 'absolute') {
+ parent = Position.offsetParent(target);
+ delta = Position.page(parent);
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if(options.setWidth) target.style.width = source.offsetWidth + 'px';
+ if(options.setHeight) target.style.height = source.offsetHeight + 'px';
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.style.position == 'absolute') return;
+ Position.prepare();
+
+ var offsets = Position.positionedOffset(element);
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';;
+ element.style.left = left + 'px';;
+ element.style.width = width + 'px';;
+ element.style.height = height + 'px';;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.style.position == 'relative') return;
+ Position.prepare();
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ }
+}
+
+// Safari returns margins on body which is incorrect if the child is absolutely
+// positioned. For performance reasons, redefine Position.cumulativeOffset for
+// KHTML/WebKit only.
+if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
+ Position.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return [valueL, valueT];
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+// plugin description
+define('NP_CLAP_mailaddr', 'ÄÌÃÎÀè¥á¡¼¥ë¥¢¥É¥ì¥¹');
+define('NP_CLAP_commentedOnly', '¥³¥á¥ó¥È¤¬½ñ¤«¤ì¤Æ¤¤¤ë¾ì¹ç¤Î¤ßÄÌÃΡ©');
+define('NP_CLAP_antispam_limit', 'Ϣ³Åê¹ÆÀ©¸Â(¡Ö²ó¿ô/ÉáפǻØÄê¡£¡Ö10/600¡×¤Ç600É䢤¿¤ê10²ó¤Þ¤Ç)');
+define('NP_CLAP_antispam_check', 'spam¥Á¥§¥Ã¥¯¤ò͸ú¤Ë¤¹¤ë¤«¡©');
+define('NP_CLAP_listThanksContent', 'Thanks¥Ú¡¼¥¸ÆâÍÆ(²èÁü¤ÎURL¤È¥³¥á¥ó¥È¤ò","¤Ç¶èÀÚ¤ê¤Ã¤ÆÆþÎϤ·¤Þ¤¹)');
+define('NP_CLAP_deleteData', '¥¢¥ó¥¤¥ó¥¹¥È¡¼¥ë»þ¤Ë¥Ç¡¼¥¿¤òºï½ü¤¹¤ë');
+
+define('NP_CLAP_description', 'Nucleus¤Ç¥¦¥§¥ÖÇï¼ê¤ò¼Â¸½¤·¤Þ¤¹');
+define('NP_CLAP_corrected', '¥«¥Æ¥´¥ê¤Î´ØÏ¢ÉÕ¤±¤ò½¤Àµ¤·¤Þ¤·¤¿¡£(%s¸Ä¤Î¥¨¥é¡¼¤¬½¤Àµ¤µ¤ì¤Þ¤·¤¿¡£)');
+
+define('NP_CLAP_1', '¡û');
+define('NP_CLAP_0', '¡Ý');
+
+define('NP_CLAP_NOCONTENT', '<font color="red">¥³¥ó¥Æ¥ó¥Ä¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¡£´ÉÍý²èÌ̤«¤é¥³¥ó¥Æ¥ó¥Ä¤òÄɲ䷤Ƥ¯¤À¤µ¤¤¡£</font>');
+
+define('NP_CLAP_DAYOFWEEK', 'Æü,·î,²Ð,¿å,ÌÚ,¶â,ÅÚ');
+
+define('NP_CLAP_ALLCHECK', '¹àÌܤòÁ´¤Æ<a href="#" onclick="return checkAll(%s,true);">¥Á¥§¥Ã¥¯¤¹¤ë</a>/<a href="#" onclick="return checkAll(%s,false);">¥Á¥§¥Ã¥¯¤ò¤Ï¤º¤¹</a><br />');
\ No newline at end of file
--- /dev/null
+<?php
+
+// plugin description
+define('NP_CLAP_mailaddr', '通知先メールアドレス');
+define('NP_CLAP_commentedOnly', 'コメントが書かれている場合のみ通知?');
+define('NP_CLAP_antispam_limit', '連続投稿制限(「回数/秒」で指定。「10/600」で600秒あたり10回まで)');
+define('NP_CLAP_antispam_check', 'spamチェックを有効にするか?');
+define('NP_CLAP_listThanksContent', 'Thanksページ内容(画像のURLとコメントを","で区切りって入力します)');
+define('NP_CLAP_deleteData', 'アンインストール時にデータを削除する');
+
+define('NP_CLAP_description', 'Nucleusでウェブ拍手を実現します');
+define('NP_CLAP_corrected', 'カテゴリの関連付けを修正しました。(%s個のエラーが修正されました。)');
+
+define('NP_CLAP_1', '○');
+define('NP_CLAP_0', '-');
+
+define('NP_CLAP_NOCONTENT', '<font color="red">コンテンツが見つかりません。管理画面からコンテンツを追加してください。</font>');
+
+define('NP_CLAP_DAYOFWEEK', '日,月,火,水,木,金,土');
+
+define('NP_CLAP_ALLCHECK', '項目を全て<a href="#" onclick="return checkAll(%s,true);">チェックする</a>/<a href="#" onclick="return checkAll(%s,false);">チェックをはずす</a><br />');
\ No newline at end of file
--- /dev/null
+#!/bin/bash -x
+
+FILES=`find . -name '*japanese-utf8*'`
+
+for utf8file in $FILES
+do
+ eucfile=`echo $utf8file | sed 's/japanese-utf8/japanese-euc/'`
+ nkf -e -W -d < $utf8file > $eucfile
+done
--- /dev/null
+Silk icon set 1.3
+
+_________________________________________
+Mark James
+http://www.famfamfam.com/lab/icons/silk/
+_________________________________________
+
+This work is licensed under a
+Creative Commons Attribution 2.5 License.
+[ http://creativecommons.org/licenses/by/2.5/ ]
+
+This means you may use it for any purpose,
+and make any changes you like.
+All I ask is that you include a link back
+to this page in your credits.
+
+Are you using this icon set? Send me an email
+(including a link or picture if available) to
+mjames@gmail.com
+
+Any other questions about this icon set please
+contact mjames@gmail.com
\ No newline at end of file
--- /dev/null
+<script type="text/javascript">
+ actionurl = '{{actionurl}}';
+ ticket = '{{ticket|}}'
+</script>
+<script type="text/javascript" src="{{plugindirurl}}js/prototype-1.4.0.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}js/chart.js"></script>
+<!-- WebFX Chart -->
+<script type="text/javascript" src="{{plugindirurl}}chart/iecanvas.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/excanvas.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/wz_jsgraphics.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/chart.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/canvaschartpainter.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/jgchartpainter.js"></script>
+
+<link rel="stylesheet" type="text/css" href="{{plugindirurl}}chart/canvaschart.css" />
+
+<h2>¸¡º÷¾ò·ï</h2>
+
+<select id="year"></select>
+<select id="month"></select>
+
+<div id="message"></div>
+
+<h2>Åý·× [<span id="date"></span>]</h2>
+
+<h3>¥¨¥ó¥È¥êÊÌ</h3>
+<div id="key" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>ÆüÊÌ</h3>
+<div id="daysOfMonth" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>ÍËÆüÊÌ</h3>
+<div id="daysOfWeek" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>»þ´ÖÂÓÊÌ</h3>
+<div id="hours" class="chart" style="width: 600px; height: 300px;"></div>
--- /dev/null
+<script type="text/javascript">
+ actionurl = '{{actionurl}}';
+ ticket = '{{ticket|}}'
+</script>
+<script type="text/javascript" src="{{plugindirurl}}js/prototype-1.4.0.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}js/chart.js"></script>
+<!-- WebFX Chart -->
+<script type="text/javascript" src="{{plugindirurl}}chart/iecanvas.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/excanvas.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/wz_jsgraphics.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/chart.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/canvaschartpainter.js"></script>
+<script type="text/javascript" src="{{plugindirurl}}chart/jgchartpainter.js"></script>
+
+<link rel="stylesheet" type="text/css" href="{{plugindirurl}}chart/canvaschart.css" />
+
+<h2>検索条件</h2>
+
+<select id="year"></select>
+<select id="month"></select>
+
+<div id="message"></div>
+
+<h2>統計 [<span id="date"></span>]</h2>
+
+<h3>エントリ別</h3>
+<div id="key" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>日別</h3>
+<div id="daysOfMonth" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>曜日別</h3>
+<div id="daysOfWeek" class="chart" style="width: 600px; height: 300px;"></div>
+
+<h3>時間帯別</h3>
+<div id="hours" class="chart" style="width: 600px; height: 300px;"></div>
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+¤³¤³¤«¤ékarma¤ÎÆâÍƤòNP_Clap¤ËÊÑ´¹¤¹¤ë¤³¤È¤¬²Äǽ¤Ç¤¹<br />
+
+<form method="post"><div>
+<input type="hidden" name="action" value="doconvert" />
+<input type="submit" tabindex="10" value="karma -> NP_Clap ÊÑ´¹¤ò¹Ô¤¦" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+</div></form>
+</blockquote>
\ No newline at end of file
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+ここからkarmaの内容をNP_Clapに変換することが可能です<br />
+
+<form method="post"><div>
+<input type="hidden" name="action" value="doconvert" />
+<input type="submit" tabindex="10" value="karma -> NP_Clap 変換を行う" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+</div></form>
+</blockquote>
\ No newline at end of file
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
--- /dev/null
+<h2>¥¢¥¤¥Æ¥à¾ÜºÙ ({{key}}¤Ë¤Ä¤¤¤Æ¡¢Á´{{rowcount}}·ïÃæ/{{offset}}·ïÌܤ«¤é{{itemperpage}}·ï¤òɽ¼¨Ãæ)</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/time.png" />»þ¹ï</th>
+ <th><img border="0" src="{{plugindirurl}}silk/computer.png" />IP¥¢¥É¥ì¥¹</th>
+ <th><img border="0" src="{{plugindirurl}}silk/comment.png" />¥á¥Ã¥»¡¼¥¸ÆâÍÆ<em>(<img border="0" src="{{plugindirurl}}silk/user.png" />¥æ¡¼¥¶Ì¾)</em></th>
+ <th>ºï½ü</th>
+ </tr>
+ </thead>
+ <tbody>
--- /dev/null
+<h2>アイテム詳細 ({{key}}について、全{{rowcount}}件中/{{offset}}件目から{{itemperpage}}件を表示中)</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/time.png" />時刻</th>
+ <th><img border="0" src="{{plugindirurl}}silk/computer.png" />IPアドレス</th>
+ <th><img border="0" src="{{plugindirurl}}silk/comment.png" />メッセージ内容<em>(<img border="0" src="{{plugindirurl}}silk/user.png" />ユーザ名)</em></th>
+ <th>削除</th>
+ </tr>
+ </thead>
+ <tbody>
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{timestamp|}}</td>
+ <td>{{ipaddr}}</td>
+ <td>
+ {{comment}}
+ <em>(<a href="{{mail_or_url|}}">{{user|}}</a>)</em><br />
+ </td>
+ <td><a href="{{indexurl}}?action=delete&key={{key|}}&offset={{offset|}}&id={{id}}&ticket={{ticket|}}"><img alt="ºï½ü" border="0" src="{{plugindirurl}}silk/cross.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{timestamp|}}</td>
+ <td>{{ipaddr}}</td>
+ <td>
+ {{comment}}
+ <em>(<a href="{{mail_or_url|}}">{{user|}}</a>)</em><br />
+ </td>
+ <td><a href="{{indexurl}}?action=delete&key={{key|}}&offset={{offset|}}&id={{id}}&ticket={{ticket|}}"><img alt="削除" border="0" src="{{plugindirurl}}silk/cross.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+ÊÑ´¹¤¬´°Î»¤·¤Þ¤·¤¿
+</blockquote>
\ No newline at end of file
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+変換が完了しました
+</blockquote>
\ No newline at end of file
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+¥ê¥»¥Ã¥È¤¬´°Î»¤·¤Þ¤·¤¿
+</blockquote>
\ No newline at end of file
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+リセットが完了しました
+</blockquote>
\ No newline at end of file
--- /dev/null
+<h2>Error</h2>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+{{message}}
+</blockquote>
\ No newline at end of file
--- /dev/null
+<h2>Error</h2>
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+{{message}}
+</blockquote>
\ No newline at end of file
--- /dev/null
+<h2>NP_Clap menu</h2>
+
+<ul>
+
+<li><img border="0" src="{{plugindirurl}}silk/application_view_list.png" />
+ <a href="{{indexurl}}?action=overview&ticket={{ticket|}}">¥¢¥¤¥Æ¥à¤´¤È¤ÎClap¿ô°ìÍ÷</a>
+</li>
+<li>
+ <img border="0" src="{{plugindirurl}}silk/chart_line.png" />
+ <a href="{{indexurl}}?action=chart&ticket={{ticket|}}">¥°¥é¥Õɽ¼¨</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/comments.png" />
+ <a href="{{indexurl}}?action=messagelist&ticket={{ticket|}}">¥á¥Ã¥»¡¼¥¸°ìÍ÷</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/plugin_edit.png" />
+ <a href="{{optionurl}}">¥×¥é¥°¥¤¥ó¥ª¥×¥·¥ç¥óÀßÄê</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/application_edit.png" />
+ <a href="{{indexurl}}?action=thanksmsg&ticket={{ticket|}}">Thanks¥Ú¡¼¥¸¥³¥ó¥Æ¥ó¥Ä°ìÍ÷</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/database_refresh.png" />
+ <a href="{{indexurl}}?action=convert&ticket={{ticket|}}">karma¤«¤é¤Î°Ü¹Ô</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/database_delete.png" />
+ <a href="{{indexurl}}?action=reset&ticket={{ticket|}}">ÃßÀѥǡ¼¥¿¤Î¥ê¥»¥Ã¥È</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/bug_link.png" />
+ <a href="{{indexurl}}?action=report"><span style="font-weight:bold; color:red">Æ°ºî³ÎǧÊó¹ð</span></a>
+</li>
+
+</ul>
\ No newline at end of file
--- /dev/null
+<h2>NP_Clap menu</h2>
+
+<ul>
+
+<li><img border="0" src="{{plugindirurl}}silk/application_view_list.png" />
+ <a href="{{indexurl}}?action=overview&ticket={{ticket|}}">アイテムごとのClap数一覧</a>
+</li>
+<li>
+ <img border="0" src="{{plugindirurl}}silk/chart_line.png" />
+ <a href="{{indexurl}}?action=chart&ticket={{ticket|}}">グラフ表示</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/comments.png" />
+ <a href="{{indexurl}}?action=messagelist&ticket={{ticket|}}">メッセージ一覧</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/plugin_edit.png" />
+ <a href="{{optionurl}}">プラグインオプション設定</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/application_edit.png" />
+ <a href="{{indexurl}}?action=thanksmsg&ticket={{ticket|}}">Thanksページコンテンツ一覧</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/database_refresh.png" />
+ <a href="{{indexurl}}?action=convert&ticket={{ticket|}}">karmaからの移行</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/database_delete.png" />
+ <a href="{{indexurl}}?action=reset&ticket={{ticket|}}">蓄積データのリセット</a>
+</li>
+<li><img border="0" src="{{plugindirurl}}silk/bug_link.png" />
+ <a href="{{indexurl}}?action=report"><span style="font-weight:bold; color:red">動作確認報告</span></a>
+</li>
+
+</ul>
\ No newline at end of file
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
--- /dev/null
+<h2>¥á¥Ã¥»¡¼¥¸°ìÍ÷ (Á´{{rowcount}}·ïÃæ/{{offset}}·ïÌܤ«¤é{{itemperpage}}·ï¤òɽ¼¨Ãæ)</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/time.png" />»þ¹ï</th>
+ <th><img border="0" src="{{plugindirurl}}silk/key.png" />¥¡¼</th>
+ <th><img border="0" src="{{plugindirurl}}silk/computer.png" />IP¥¢¥É¥ì¥¹</th>
+ <th><img border="0" src="{{plugindirurl}}silk/comment.png" />¥á¥Ã¥»¡¼¥¸ÆâÍÆ<em>(<img border="0" src="{{plugindirurl}}silk/user.png" />¥æ¡¼¥¶Ì¾)</em></th>
+ <th>¾ÜºÙ</th>
+ </tr>
+ </thead>
+ <tbody>
\ No newline at end of file
--- /dev/null
+<h2>メッセージ一覧 (全{{rowcount}}件中/{{offset}}件目から{{itemperpage}}件を表示中)</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/time.png" />時刻</th>
+ <th><img border="0" src="{{plugindirurl}}silk/key.png" />キー</th>
+ <th><img border="0" src="{{plugindirurl}}silk/computer.png" />IPアドレス</th>
+ <th><img border="0" src="{{plugindirurl}}silk/comment.png" />メッセージ内容<em>(<img border="0" src="{{plugindirurl}}silk/user.png" />ユーザ名)</em></th>
+ <th>詳細</th>
+ </tr>
+ </thead>
+ <tbody>
\ No newline at end of file
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{timestamp|}}</td>
+ <td>{{key|}}</td>
+ <td>{{ipaddr}}</td>
+ <td>
+ {{comment}}
+ <em>(<a href="{{mail_or_url|}}">{{user|}}</a>)</em><br />
+ </td>
+ <td><a href="{{indexurl}}?action=detail&key={{key|}}&ticket={{ticket|}}"><img border="0" alt="¾ÜºÙ" src="{{plugindirurl}}silk/magnifier.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{timestamp|}}</td>
+ <td>{{key|}}</td>
+ <td>{{ipaddr}}</td>
+ <td>
+ {{comment}}
+ <em>(<a href="{{mail_or_url|}}">{{user|}}</a>)</em><br />
+ </td>
+ <td><a href="{{indexurl}}?action=detail&key={{key|}}&ticket={{ticket|}}"><img border="0" alt="詳細" src="{{plugindirurl}}silk/magnifier.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="detail" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="key" value="{{key|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
\ No newline at end of file
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
\ No newline at end of file
--- /dev/null
+<h2>¥¢¥¤¥Æ¥à¤´¤È¤ÎClap¿ô°ìÍ÷ (Á´{{rowcount}}·ïÃæ/{{offset}}·ïÌܤ«¤é{{itemperpage}}·ï¤òɽ¼¨Ãæ)</h2>
+blog¤Ç¹Ê¹þ¤ß¡§{{blogselect}}
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/key.png" />¥¡¼</th>
+ <th><img border="0" src="{{plugindirurl}}silk/layout.png" />¥¿¥¤¥È¥ë¡Ê¥Ö¥í¥°Ì¾¡Ë</th>
+ <th><img border="0" src="{{plugindirurl}}silk/coins.png" />¥«¥¦¥ó¥È¿ô</th>
+ <th>¾ÜºÙ</th>
+ </tr>
+ </thead>
+ <tbody>
--- /dev/null
+<h2>アイテムごとのClap数一覧 (全{{rowcount}}件中/{{offset}}件目から{{itemperpage}}件を表示中)</h2>
+blogで絞込み:{{blogselect}}
+
+<table>
+ <thead>
+ <tr>
+ <th><img border="0" src="{{plugindirurl}}silk/key.png" />キー</th>
+ <th><img border="0" src="{{plugindirurl}}silk/layout.png" />タイトル(ブログ名)</th>
+ <th><img border="0" src="{{plugindirurl}}silk/coins.png" />カウント数</th>
+ <th>詳細</th>
+ </tr>
+ </thead>
+ <tbody>
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{key|}}</td>
+ <td>
+ {{title|}}
+ <em>({{blogname|}})</em><br />
+ </td>
+ <td>{{count|}}</td>
+ <td><a href="{{indexurl}}?action=detail&key={{key|}}&ticket={{ticket|}}"><img border="0" alt="¾ÜºÙ" src="{{plugindirurl}}silk/magnifier.png" /></a></td>
+ </tr>
--- /dev/null
+ <tr onmouseover="focusRow(this);" onmouseout="blurRow(this);">
+ <td>{{key|}}</td>
+ <td>
+ {{title|}}
+ <em>({{blogname|}})</em><br />
+ </td>
+ <td>{{count|}}</td>
+ <td><a href="{{indexurl}}?action=detail&key={{key|}}&ticket={{ticket|}}"><img border="0" alt="詳細" src="{{plugindirurl}}silk/magnifier.png" /></a></td>
+ </tr>
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="overview" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="blog" value="{{blog|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="overview" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="blog" value="{{blog|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="overview" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="blog" value="{{blog|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="overview" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="blog" value="{{blog|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+NP_Clap¤ËÃßÀѤµ¤ì¤¿ÆâÍƤò¥ê¥»¥Ã¥È¤·¤Þ¤¹<br />
+<strong>¥ê¥»¥Ã¥È¤·¤¿¾ðÊó¤Ï¸µ¤ËÌ᤻¤Þ¤»¤ó¤Î¤Ç¤´Ãí°Õ¤¯¤À¤µ¤¤</strong><br />
+
+<form method="post"><div>
+<input type="hidden" name="action" value="doreset" />
+<input type="submit" tabindex="10" value="NP_Clap¤Î¾ðÊó¤ò¥ê¥»¥Ã¥È" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+</div></form>
+</blockquote>
\ No newline at end of file
--- /dev/null
+<blockquote style="color: red;border:1px solid red;padding:1em;">
+NP_Clapに蓄積された内容をリセットします<br />
+<strong>リセットした情報は元に戻せませんのでご注意ください</strong><br />
+
+<form method="post"><div>
+<input type="hidden" name="action" value="doreset" />
+<input type="submit" tabindex="10" value="NP_Clapの情報をリセット" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+</div></form>
+</blockquote>
\ No newline at end of file
--- /dev/null
+<h2>¥³¥ó¥Æ¥ó¥Ä¤ÎÊÔ½¸¡¦¿·µ¬ÄɲÃ</h2>
+
+<form method="post" action="{{indexurl}}">
+<input type="hidden" name="action" value="thankssave" />
+<input type="hidden" name="id" value="{{id}}" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+
+<script language="JavaScript">
+//<![CDATA[
+ function checkAll(blogid, checked){
+ var tags = document.getElementsByTagName('input');
+ for(var i=0; i < tags.length; i++){
+ var t = tags[i];
+ if( t.type == 'checkbox' && t.name.indexOf('assoc') == 0 && t.id.indexOf(blogid+'_') == 0 ){
+ t.checked = checked;
+ }
+ }
+ return false;
+ }
+//]]>
+</script>
+
+<table>
+ <thead>
+ <tr>
+ <th>¹àÌÜ</th>
+ <th>ÃÍ</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>¥³¥á¥ó¥È</td>
+ <td><textarea name="comment" rows="15" cols="70">{{comment|}}</textarea></td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>²èÁü¡Ê»ÈÍѤ·¤Ê¤¤¾ì¹ç¤Ë¤Ï¶õÍó¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡Ë</td>
+ <td><input type="text" name="image" size="100" value="{{image|}}" /></td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>ɽ¼¨Âоݤˤ¹¤ë¥«¥Æ¥´¥ê</td>
+ <td>{{assoc}}
+ ¤½¤Î¾¤ÎKey:<input type="text" name="assoc_etc" size="80" value="{{assoc_etc|}}" /><br />
+ ¢¨,(¥«¥ó¥Þ)¤Ç¶èÀڤäÆÊ£¿ô¤Îkey¤òÆþÎϤǤ¤Þ¤¹¡£
+ </td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>¥³¥ó¥Æ¥ó¥Ä¤ÎÊݸ</td>
+ <td><input type="submit" value="¥³¥ó¥Æ¥ó¥Ä¤ÎÊݸ" /></td>
+ </tr>
+ </tbody>
+</table>
+</form>
\ No newline at end of file
--- /dev/null
+<h2>コンテンツの編集・新規追加</h2>
+
+<form method="post" action="{{indexurl}}">
+<input type="hidden" name="action" value="thankssave" />
+<input type="hidden" name="id" value="{{id}}" />
+<input type="hidden" name="ticket" value="{{ticket|}}" />
+
+<script language="JavaScript">
+//<![CDATA[
+ function checkAll(blogid, checked){
+ var tags = document.getElementsByTagName('input');
+ for(var i=0; i < tags.length; i++){
+ var t = tags[i];
+ if( t.type == 'checkbox' && t.name.indexOf('assoc') == 0 && t.id.indexOf(blogid+'_') == 0 ){
+ t.checked = checked;
+ }
+ }
+ return false;
+ }
+//]]>
+</script>
+
+<table>
+ <thead>
+ <tr>
+ <th>項目</th>
+ <th>値</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>コメント</td>
+ <td><textarea name="comment" rows="15" cols="70">{{comment|}}</textarea></td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>画像(使用しない場合には空欄にしてください)</td>
+ <td><input type="text" name="image" size="100" value="{{image|}}" /></td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>表示対象にするカテゴリ</td>
+ <td>{{assoc}}
+ その他のKey:<input type="text" name="assoc_etc" size="80" value="{{assoc_etc|}}" /><br />
+ ※,(カンマ)で区切って複数のkeyを入力できます。
+ </td>
+ </tr>
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>コンテンツの保存</td>
+ <td><input type="submit" value="コンテンツの保存" /></td>
+ </tr>
+ </tbody>
+</table>
+</form>
\ No newline at end of file
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
+
+<h3>´ØÏ¢ÉÕ¤±¤Î½¤Àµ</h3>
+<p><img border="0" src="{{plugindirurl}}silk/database_refresh.png" /><a href="{{indexurl}}?action=docorrect&id=new&ticket={{ticket|}}">¥³¥ó¥Æ¥ó¥Ä¤Î´ØÏ¢ÉÕ¤±¤ò½¤Àµ¤·¤Þ¤¹</a></p>
+<p>¥³¥ó¥Æ¥ó¥Ä¤òÆþÎϤ·¤Æ¤¤¤ë¤Ë¤â¤«¤«¤ï¤é¤º¡Ö¥³¥ó¥Æ¥ó¥Ä¤¬¸«¤Ä¤«¤ê¤Þ¤»¤ó¡£´ÉÍý²èÌ̤«¤é¥³¥ó¥Æ¥ó¥Ä¤òÄɲ䷤Ƥ¯¤À¤µ¤¤¡£¡×¤Èɽ¼¨¤µ¤ì¤ë¾ì¹ç¤Ë¼Â¹Ô¤·¤Æ¤¯¤À¤µ¤¤¡£</p>
--- /dev/null
+ </tbody>
+</table>
+<table class="navigation">
+ <tr>
+ <td style="padding: 0;">
+ {{prev_button}}
+ </td>
+ <td style="padding: 0; text-align: right;">
+ {{next_button}}
+ </td>
+ </tr>
+</table>
+
+<h3>関連付けの修正</h3>
+<p><img border="0" src="{{plugindirurl}}silk/database_refresh.png" /><a href="{{indexurl}}?action=docorrect&id=new&ticket={{ticket|}}">コンテンツの関連付けを修正します</a></p>
+<p>コンテンツを入力しているにもかかわらず「コンテンツが見つかりません。管理画面からコンテンツを追加してください。」と表示される場合に実行してください。</p>
--- /dev/null
+<div style="color: red;">{{message}}</div>
+
+<h2>Thanks¥Ú¡¼¥¸¥³¥ó¥Æ¥ó¥Ä°ìÍ÷</h2>
+
+<h3>¥³¥ó¥Æ¥ó¥Ä¤Î¿·µ¬ÄɲÃ</h3>
+<p><img border="0" src="{{plugindirurl}}silk/page_white_add.png" /><a href="{{indexurl}}?action=thanksedit&id=new&ticket={{ticket|}}">¥³¥ó¥Æ¥ó¥Ä¤òÄɲ乤ë</a></p>
+
+<h3>ÅÐÏ¿¤µ¤ì¤Æ¤¤¤ë¥³¥ó¥Æ¥ó¥Ä(Á´{{rowcount}}·ïÃæ/{{offset}}·ïÌܤ«¤é{{itemperpage}}·ï¤òɽ¼¨Ãæ)</h3>
+<table>
+ <thead>
+ <tr>
+ <th>id</th>
+ <th>¥³¥á¥ó¥È</th>
+ <th>²èÁü</th>
+ <th>¥×¥ì¥Ó¥å¡¼</th>
+ <th>ÊÔ½¸</th>
+ <th>ºï½ü</th>
+ </tr>
+ </thead>
+ <tbody>
\ No newline at end of file
--- /dev/null
+<div style="color: red;">{{message}}</div>
+
+<h2>Thanksページコンテンツ一覧</h2>
+
+<h3>コンテンツの新規追加</h3>
+<p><img border="0" src="{{plugindirurl}}silk/page_white_add.png" /><a href="{{indexurl}}?action=thanksedit&id=new&ticket={{ticket|}}">コンテンツを追加する</a></p>
+
+<h3>登録されているコンテンツ(全{{rowcount}}件中/{{offset}}件目から{{itemperpage}}件を表示中)</h3>
+<table>
+ <thead>
+ <tr>
+ <th>id</th>
+ <th>コメント</th>
+ <th>画像</th>
+ <th>プレビュー</th>
+ <th>編集</th>
+ <th>削除</th>
+ </tr>
+ </thead>
+ <tbody>
\ No newline at end of file
--- /dev/null
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>{{id}}</td>
+ <td>{{comment}}</td>
+ <td>{{image}}</td>
+ <td><a href="{{actionurl}}?action=plugin&name=Clap&type=preview&id={{id}}&ticket={{ticket|}}" target="_blank"><img border="0" alt="¥×¥ì¥Ó¥å¡¼" src="{{plugindirurl}}silk/page_white_magnify.png" /></a></td>
+ <td><a href="{{indexurl}}?action=thanksedit&id={{id}}&ticket={{ticket|}}"><img border="0" alt="ÊÔ½¸" src="{{plugindirurl}}silk/page_white_edit.png" /></a></td>
+ <td><a href="{{indexurl}}?action=thanksdelete&id={{id}}&ticket={{ticket|}}"><img border="0" alt="ºï½ü" src="{{plugindirurl}}silk/page_white_delete.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <tr onmouseover='focusRow(this);' onmouseout='blurRow(this);'>
+ <td>{{id}}</td>
+ <td>{{comment}}</td>
+ <td>{{image}}</td>
+ <td><a href="{{actionurl}}?action=plugin&name=Clap&type=preview&id={{id}}&ticket={{ticket|}}" target="_blank"><img border="0" alt="プレビュー" src="{{plugindirurl}}silk/page_white_magnify.png" /></a></td>
+ <td><a href="{{indexurl}}?action=thanksedit&id={{id}}&ticket={{ticket|}}"><img border="0" alt="編集" src="{{plugindirurl}}silk/page_white_edit.png" /></a></td>
+ <td><a href="{{indexurl}}?action=thanksdelete&id={{id}}&ticket={{ticket|}}"><img border="0" alt="削除" src="{{plugindirurl}}silk/page_white_delete.png" /></a></td>
+ </tr>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="thanksmsg" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="Next >>" />
+ <input type="hidden" name="action" value="thanksmsg" />
+ <input type="hidden" name="offset" value="{{next_offset|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
\ No newline at end of file
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="thanksmsg" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+ <form method="post" action="{{indexurl}}">
+ <div>
+ <input type="submit" value="<< Prev" />
+ <input type="hidden" name="action" value="thanksmsg" />
+ <input type="hidden" name="offset" value="{{prev_offset|}}" />
+ <input type="hidden" name="ticket" value="{{ticket|}}" />
+ </div>
+ </form>
--- /dev/null
+<form action="{{actionurl}}" method="POST" style="display:inline"><!--{{key}}:{{count}}--><input type="submit" value="Çï¼ê¡ª"></form>
--- /dev/null
+<form action="{{actionurl}}" method="POST" style="display:inline"><!--{{key}}:{{count}}--><input type="submit" value="拍手!"></form>
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>Çï¼êÁ÷¿®´°Î» - NP_Clap</title>
+</head>
+<body>
+<center>
+<font size="2">Çï¼ê¤òÁ÷¿®¤¹¤ë¤³¤È¤¬¤Ç¤¤Þ¤»¤ó¤Ç¤·¤¿¡£<br />
+¤·¤Ð¤é¤¯¤¿¤Ã¤Æ¤«¤é¤Þ¤¿¤ª»î¤·¤¯¤À¤µ¤¤¡£</font><br />
+<br />
+<a href="{{returnurl}}">¸µ¤Î¥Ú¡¼¥¸¤ËÌá¤ë</a>
+<br />
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>拍手送信完了 - NP_Clap</title>
+</head>
+<body>
+<center>
+<font size="2">拍手を送信することができませんでした。<br />
+しばらくたってからまたお試しください。</font><br />
+<br />
+<a href="{{returnurl}}">元のページに戻る</a>
+<br />
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<li><!--{{key}}--><a href="{{itemlink}}">{{title}}</a> ({{count}})</li>
--- /dev/null
+<li><!--{{key}}--><a href="{{itemlink}}">{{title}}</a> ({{count}})</li>
--- /dev/null
+̾Á°: {{user}}
+URL/¥á¡¼¥ë¥¢¥É¥ì¥¹: {{mail_or_url}}
+IP¥¢¥É¥ì¥¹: {{ipaddr}}({{hostname}})
+UserAgent: {{useragent}}
+Referer: {{referer}}
+¥³¥á¥ó¥È: {{comment}}
\ No newline at end of file
--- /dev/null
+名前: {{user}}
+URL/メールアドレス: {{mail_or_url}}
+IPアドレス: {{ipaddr}}({{hostname}})
+UserAgent: {{useragent}}
+Referer: {{referer}}
+コメント: {{comment}}
\ No newline at end of file
--- /dev/null
+Çï¼ê¤¬Á÷¿®¤µ¤ì¤Þ¤·¤¿ ({{key}})
\ No newline at end of file
--- /dev/null
+拍手が送信されました ({{key}})
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>Çï¼êÁ÷¿®´°Î» - NP_Clap</title>
+</head>
+<body>
+<center>
+{{img}}<br />
+<font size="2">{{text}}</font><br />
+<br />
+<a href="{{returnurl}}">¸µ¤Î¥Ú¡¼¥¸¤ËÌá¤ë</a><br />
+<br />
+<br />
+<form method="post" action="{{actionurl}}">
+
+<font size="2">¤ªÌ¾Á°:<input type="text" name="user" size="50" /></font><br />
+<font size="2">¥á¡¼¥ë¥¢¥É¥ì¥¹/£Õ£Ò£Ì:<input type="text" name="mail_or_url" size="50" /></font><br />
+<font size="2">¤Ä¤¤¤Ç¤Ë°ì¸À¤¢¤ì¤Ð¤É¤¦¤¾(Çï¼ê¤À¤±¤Ç¤âÁ÷¤ì¤Þ¤¹)</font><br />
+<textarea name="comment" rows="4" cols="50"></textarea><br />
+
+<input type="submit" value="¤â¤Ã¤ÈÁ÷¤ë" />
+
+<!-- hidden -->
+<!-- contentid: {{contentid}} -->
+<input type="hidden" name="key" value="{{key}}" />
+<input type="hidden" name="returnurl" value="{{returnurl}}" />
+<input type="hidden" name="action" value="plugin" />
+<input type="hidden" name="name" value="Clap" />
+<input type="hidden" name="type" value="{{type}}" />
+<!-- hidden -->
+
+</form>
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>拍手送信完了 - NP_Clap</title>
+</head>
+<body>
+<center>
+{{img}}<br />
+<font size="2">{{text}}</font><br />
+<br />
+<a href="{{returnurl}}">元のページに戻る</a><br />
+<br />
+<br />
+<form method="post" action="{{actionurl}}">
+
+<font size="2">お名前:<input type="text" name="user" size="50" /></font><br />
+<font size="2">メールアドレス/URL:<input type="text" name="mail_or_url" size="50" /></font><br />
+<font size="2">ついでに一言あればどうぞ(拍手だけでも送れます)</font><br />
+<textarea name="comment" rows="4" cols="50"></textarea><br />
+
+<input type="submit" value="もっと送る" />
+
+<!-- hidden -->
+<!-- contentid: {{contentid}} -->
+<input type="hidden" name="key" value="{{key}}" />
+<input type="hidden" name="returnurl" value="{{returnurl}}" />
+<input type="hidden" name="action" value="plugin" />
+<input type="hidden" name="name" value="Clap" />
+<input type="hidden" name="type" value="{{type}}" />
+<!-- hidden -->
+
+</form>
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<form id="clap_{{key}}" action="{{actionurl_wo_key}}" method="POST" style="display:inline">
+<input type="submit" value="{{count}}¤Ø¤§" />
+</form>
+<script type="text/javascript">
+var k = document.createElement('input');
+k.type = 'hidden';
+k.name = 'key';
+k.value = '{{key}}';
+document.forms['clap_{{key}}'].appendChild(k);
+</script>
--- /dev/null
+<form id="clap_{{key}}" action="{{actionurl_wo_key}}" method="POST" style="display:inline">
+<input type="submit" value="{{count}}へぇ" />
+</form>
+<script type="text/javascript">
+var k = document.createElement('input');
+k.type = 'hidden';
+k.name = 'key';
+k.value = '{{key}}';
+document.forms['clap_{{key}}'].appendChild(k);
+</script>
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>Çï¼êÁ÷¿®´°Î» - NP_Clap</title>
+</head>
+<body>
+<center>
+<font size="2">Çï¼ê¤òÁ÷¿®¤¹¤ë¤³¤È¤¬¤Ç¤¤Þ¤»¤ó¤Ç¤·¤¿¡£<br />
+¤·¤Ð¤é¤¯¤¿¤Ã¤Æ¤«¤é¤Þ¤¿¤ª»î¤·¤¯¤À¤µ¤¤¡£</font><br />
+<br />
+<a href="{{returnurl}}">¸µ¤Î¥Ú¡¼¥¸¤ËÌá¤ë</a>
+<br />
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>拍手送信完了 - NP_Clap</title>
+</head>
+<body>
+<center>
+<font size="2">拍手を送信することができませんでした。<br />
+しばらくたってからまたお試しください。</font><br />
+<br />
+<a href="{{returnurl}}">元のページに戻る</a>
+<br />
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
--- /dev/null
+<ul class="nobullets">
--- /dev/null
+<ul class="nobullets">
--- /dev/null
+<li><!--{{key}}--><a href="{{itemlink}}">{{title}}</a> ({{count}})</li>
--- /dev/null
+<li><!--{{key}}--><a href="{{itemlink}}">{{title}}</a> ({{count}})</li>
--- /dev/null
+item: http://blog.cles.jp/item/{{key}}
+̾Á°: {{user}}
+URL/¥á¡¼¥ë¥¢¥É¥ì¥¹: {{mail_or_url}}
+IP¥¢¥É¥ì¥¹: {{ipaddr}}({{hostname}})
+UserAgent: {{useragent}}
+Referer: {{referer}}
+¥³¥á¥ó¥È: {{comment}}
\ No newline at end of file
--- /dev/null
+item: http://blog.cles.jp/item/{{key}}
+名前: {{user}}
+URL/メールアドレス: {{mail_or_url}}
+IPアドレス: {{ipaddr}}({{hostname}})
+UserAgent: {{useragent}}
+Referer: {{referer}}
+コメント: {{comment}}
\ No newline at end of file
--- /dev/null
+Çï¼ê¤¬Á÷¿®¤µ¤ì¤Þ¤·¤¿ ({{key}})
\ No newline at end of file
--- /dev/null
+拍手が送信されました ({{key}})
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>Çï¼êÁ÷¿®´°Î» - NP_Clap</title>
+</head>
+<body>
+<center>
+{{img}}<br />
+<font size="2">{{text}}</font><br />
+<br />
+<a href="{{returnurl}}">¸µ¤Î¥Ú¡¼¥¸¤ËÌá¤ë</a><br />
+<br />
+<br />
+<form method="post" action="{{actionurl}}">
+
+<font size="2">¤ªÌ¾Á°:<input type="text" name="user" size="50" /></font><br />
+<font size="2">¥á¡¼¥ë¥¢¥É¥ì¥¹/£Õ£Ò£Ì:<input type="text" name="mail_or_url" size="50" /></font><br />
+<font size="2">¤Ä¤¤¤Ç¤Ë°ì¸À¤¢¤ì¤Ð¤É¤¦¤¾(Çï¼ê¤À¤±¤Ç¤âÁ÷¤ì¤Þ¤¹)</font><br />
+<textarea name="comment" rows="4" cols="50"></textarea><br />
+
+<input type="submit" value="¤â¤Ã¤ÈÁ÷¤ë" />
+
+<!-- hidden -->
+<!-- contentid: {{contentid}} -->
+<input type="hidden" name="key" value="{{key}}" />
+<input type="hidden" name="returnurl" value="{{returnurl}}" />
+<input type="hidden" name="action" value="plugin" />
+<input type="hidden" name="name" value="Clap" />
+<input type="hidden" name="type" value="{{type}}" />
+<!-- hidden -->
+
+</form>
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="{{charset}}"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="ja" xml:lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset={{charset}}" />
+<title>拍手送信完了 - NP_Clap</title>
+</head>
+<body>
+<center>
+{{img}}<br />
+<font size="2">{{text}}</font><br />
+<br />
+<a href="{{returnurl}}">元のページに戻る</a><br />
+<br />
+<br />
+<form method="post" action="{{actionurl}}">
+
+<font size="2">お名前:<input type="text" name="user" size="50" /></font><br />
+<font size="2">メールアドレス/URL:<input type="text" name="mail_or_url" size="50" /></font><br />
+<font size="2">ついでに一言あればどうぞ(拍手だけでも送れます)</font><br />
+<textarea name="comment" rows="4" cols="50"></textarea><br />
+
+<input type="submit" value="もっと送る" />
+
+<!-- hidden -->
+<!-- contentid: {{contentid}} -->
+<input type="hidden" name="key" value="{{key}}" />
+<input type="hidden" name="returnurl" value="{{returnurl}}" />
+<input type="hidden" name="action" value="plugin" />
+<input type="hidden" name="name" value="Clap" />
+<input type="hidden" name="type" value="{{type}}" />
+<!-- hidden -->
+
+</form>
+
+<br />
+<font size="2">Powered by <a href="http://blog.cles.jp/np_cles/category/31/subcatid/13" target="_blank">NP_Clap</a></font><br />
+</center>
+</body></html>
\ No newline at end of file
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+define('JSPHON_ERROR_DECODE_SYNTAX', 1);
+
+require_once(dirname(__FILE__) .'/Jsphon/Error.php');
+require_once(dirname(__FILE__) .'/Jsphon/Decoder.php');
+require_once(dirname(__FILE__) .'/Jsphon/Encoder.php');
+
+Jsphon_Error::singleton();
+
+/**
+ * Jsphon - JSON in PHP
+ *
+ * example:
+ * <code>
+ * //encode
+ * $value = array('foo', 'bar', array('hoge' => array(1,2)));
+ * $json = Jsphon::encode($value);
+ * echo $json;
+ *
+ * //decode
+ * $var = Jsphon::decode($json);
+ * echo $var[2]['hoge'];
+ *
+ * </code>
+ *
+ * @author Hawk
+ */
+class Jsphon
+{
+ /**
+ * Encodes an arbitrary variables into JSON format.
+ * See Jsphon_Encoder::encode() for details.
+ *
+ * @param String $value
+ * @param boolean $escapeNonASCII
+ * @param boolean $escapeOverUCS2
+ * @return String
+ */
+ function encode($value, $escapeNonASCII=true, $escapeOverUCS2=false)
+ {
+ $encoder = new Jsphon_Encoder($escapeNonASCII, $escapeOverUCS2);
+ return $encoder->encode($value);
+ }
+
+
+ /**
+ * Decodes JSON-formatted string into appropriate PHP variable.
+ * See Jsphon_Decoder::decode() for details.
+ *
+ * @param String $json
+ * @param String $decodeOverUCS2
+ * @return mixed
+ */
+ function decode($json, $decodeOverUCS2=false)
+ {
+ $decoder = new Jsphon_Decoder($decodeOverUCS2);
+ return $decoder->decode($json);
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+require_once(dirname(__FILE__) .'/Decoder/Tokenizer.php');
+
+/**
+ * Converts JSON-formatted string to appropriate PHP variable
+ *
+ * example:
+ * <code>
+ * //create a new instance of Jsphon_Decoder
+ * $json =& new Jsphon_Decoder();
+ *
+ * //convert JSON-formatted string to PHP variable
+ * $value = '["foo","bar",{"hoge":[1,2]}]';
+ * $var = $json->decode($value);
+ *
+ * print_r($var);
+ * //array('foo', 'bar', array('hoge' => array(1,2)))
+ * </code>
+ *
+ * @author Hawk
+ */
+class Jsphon_Decoder
+{
+ var $_mbstring;
+
+ var $_decodeOverUCS2;
+
+ var $_internalError;
+
+ var $_transTable = array(
+ '\b' => "\x08",
+ '\t' => "\x09",
+ '\n' => "\x0A",
+ '\f' => "\x0C",
+ '\r' => "\x0D",
+ '\"' => "\x22",
+ '\/' => "\x2F",
+ '\\\\' => "\x5C"
+ );
+
+ var $_allUESreg = '/\\\u([a-fA-F0-9]{4})/';
+
+ var $_utf16surUESreg = '/\\\u(D[89AB][A-F0-9]{2})\\\u(D[C-F][A-F0-9]{2})/i';
+
+ /**
+ * construct a new Jsphon_Decoder instance.
+ *
+ * @param bool $decodeOverUCS2 If true, decodeString() converts the whole
+ * Unicode escape sequences (\uXXXX) including surrogate pairs
+ * to corresponding characters in UTF-8.
+ */
+ function Jsphon_Decoder($decodeOverUCS2=false)
+ {
+ $this->_decodeOverUCS2 = $decodeOverUCS2;
+ $this->_mbstring = extension_loaded('mbstring');
+ }
+
+ /**
+ * decodes a JSON string into appropriate variable
+ *
+ * @param String $json
+ * @return mixed
+ */
+ function decode($json)
+ {
+ $tknz = new Jsphon_Decoder_Tokenizer($json,
+ array(&$this, 'handleInternalError'));
+ $this->_internalError = false;
+
+ $result = null;
+ if($tknz->nextToken()) {
+ $result = $this->_decodeJSValue($tknz);
+ }
+
+ $this->_internalError = false;
+ return $result;
+ }
+
+ /**
+ *
+ * @param Object $tknz
+ * @return mixed
+ */
+ function _decodeJSValue(&$tknz)
+ {
+ switch($tknz->getToken()) {
+ case JSPHON_TOKEN_DATUM:
+ if(is_string($r = $tknz->getTokenValue())) {
+ $r = $this->decodeString($r);
+ }
+ return $r;
+
+ case JSPHON_TOKEN_LBRACKET:
+ return $this->_decodeArray($tknz);
+
+ case JSPHON_TOKEN_LBRACE:
+ return $this->_decodeObject($tknz);
+
+ default:
+ $this->_error("syntax error: Expecting '{', '[' or DAUM.");
+ return null;
+ }
+ }
+
+ /**
+ * Decodes a JSON array format:
+ * [element, element2,...,elementN]
+ *
+ * @param Object $tknz
+ * @return array
+ */
+ function _decodeArray(&$tknz)
+ {
+ $ret = array();
+ if(!($token = $tknz->nextToken())) {
+ return null;
+
+ } elseif($token == JSPHON_TOKEN_RBRACKET) {
+ return $ret;
+ }
+
+ //if false, break
+ while( ($value = $this->_decodeJSValue($tknz) or true)
+ and !$this->_internalError
+ and ($ret[] = $value or true)
+ and $token = $tknz->nextToken()
+ and $token == JSPHON_TOKEN_COMMA
+ and $token = $tknz->nextToken()
+ );
+
+ if($this->_internalError) {
+ return null;
+
+ } elseif($token == JSPHON_TOKEN_RBRACKET) {
+ return $ret;
+ } else {
+ $this->_error("Missing ',' or ']' in array encoding.");
+ return null;
+ }
+ }
+
+ /**
+ * Decodes an object of the form:
+ * { "attribute: value, "attribute2" : value,...}
+ *
+ * _decodeObject() always converts a JSON-object to an associative array,
+ * because you can't access empty property in PHP5.
+ * <code>
+ * // { "":"foo" }
+ * $obj->{""} = "foo"; //Fatal error: Cannot access empty property
+ * </code>
+ *
+ * @param Object $tknz
+ * @return array
+ */
+ function _decodeObject(&$tknz)
+ {
+ $ret = array();
+ if(!($token = $tknz->nextToken())) {
+ return null;
+
+ } elseif($token == JSPHON_TOKEN_RBRACE) {
+ return $ret;
+ }
+
+ //if false, break
+ while( ($key = $this->_decodeJSValue($tknz) or true)
+ and !$this->_internalError
+ and $this->_checkObjectKey($key)
+ and $token = $tknz->nextToken()
+ and $this->_checkKeyValueSep($token)
+ and $token = $tknz->nextToken()
+ and ($value = $this->_decodeJSValue($tknz) or true)
+ and !$this->_internalError
+ and ($ret[$key] = $value or true)
+ and $token = $tknz->nextToken()
+ and $token == JSPHON_TOKEN_COMMA
+ and $token = $tknz->nextToken()
+ );
+
+ if($this->_internalError) {
+ return null;
+
+ } elseif($token == JSPHON_TOKEN_RBRACE) {
+ return $ret;
+ } else {
+ $this->_error("Missing ',' or '}' in object encoding.");
+ return null;
+ }
+ }
+
+ function _checkObjectKey($key)
+ {
+ if(!is_string($key)) {
+ $this->_error("Object's key must be a string, but is ". gettype($key));
+ return false;
+ }
+ return true;
+ }
+
+ function _checkKeyValueSep($token)
+ {
+ if($token != JSPHON_TOKEN_COLON) {
+ $this->_error("Missing ':' in object encoding.");
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ *
+ *
+ * @param String $encoded
+ * @return String
+ */
+ function decodeString($encoded)
+ {
+ $ret = strtr($encoded, $this->_transTable);
+
+ if($this->_decodeOverUCS2) {
+ return $this->_decodeUESOverUCS2($ret);
+ } else {
+ //_decodeUESWithoutMbstring() fastar than _decodeUES()...
+ return $this->_decodeUESWithoutMbstring($ret);
+ }
+
+ /*
+ if(!$this->_mbstring) {
+ return $this->_decodeUESWithoutMbstring($ret);
+ } elseif($this->_decodeOverUCS2) {
+ return $this->_decodeUESOverUCS2($ret);
+ } else {
+ return $this->_decodeUES($ret);
+ }
+ */
+ }
+
+ function _decodeUES($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_allUESreg, $str, $matches)) {
+ return $str;
+ }
+
+ $codepoints = $matches[1];
+ foreach($matches[0] as $i => $escSeq) {
+ if(isset($transTable[$escSeq])) {
+ continue;
+ }
+ $codepoint = hexdec($codepoints[$i]);
+ $transTable[$escSeq] = (0xD800 <= $codepoint && $codepoint <= 0xDFFF) ? ""
+ : mb_convert_encoding(pack('n', $codepoint), 'UTF-8', 'UCS-2');
+ }
+ return strtr($str, $transTable);
+ }
+
+ function _decodeUESOverUCS2($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_allUESreg, $str, $u16)) {
+ return $str;
+ }
+
+ if(preg_match_all($this->_utf16surUESreg, $str, $u16sur)) {
+ $sur1st = $u16sur[1];
+ $sur2nd = $u16sur[2];
+
+ foreach($u16sur[0] as $i => $escSeq) {
+ if(isset($transTable[$escSeq])) {
+ continue;
+ }
+ $transTable[$escSeq] = mb_convert_encoding(
+ pack('n2', hexdec($sur1st[$i]), hexdec($sur2nd[$i])), 'UTF-8', 'UTF-16BE');
+ }
+ }
+
+ $codepoints = $u16[1];
+ foreach($u16[0] as $i => $escSeq) {
+ if(isset($transTable[$escSeq])) {
+ continue;
+ }
+ $codepoint = hexdec($codepoints[$i]);
+ $transTable[$escSeq] = (0xD800 <= $codepoint && $codepoint <= 0xDFFF) ? ""
+ : mb_convert_encoding(pack('n', $codepoint), 'UTF-8', 'UCS-2');
+ }
+ return strtr($str, $transTable);
+ }
+
+ function _decodeUESWithoutMbstring($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_allUESreg, $str, $matches)) {
+ return $str;
+ }
+
+ $codepoints = $matches[1];
+ foreach($matches[0] as $i => $escSeq) {
+ if(isset($transTable[$escSeq])) {
+ continue;
+ }
+
+ $utf8char = "";
+
+ $cp = hexdec($codepoints[$i]);
+ switch(true) {
+ case ($cp < 0x80):
+ $utf8char = chr($cp);
+ break;
+
+ case (0xD800 <= $cp && $cp <= 0xDFFF):
+ break;
+
+ case ($cp < 0x800):
+ $utf8char = chr($cp >> 6 & 0x1F | 0xC0) . chr($cp & 0x3F | 0x80);
+ break;
+
+ case ($cp < 0x10000):
+ $utf8char = chr($cp >> 12 & 0xF | 0xE0) .
+ chr($cp >> 6 & 0x3F | 0x80) . chr($cp & 0x3F | 0x80);
+ break;
+
+ default:
+ }
+ $transTable[$escSeq] = $utf8char;
+ }
+ return strtr($str, $transTable);
+ }
+
+
+ /**
+ * a simple wrapper for PEAR_ErrorStack::push.
+ *
+ * @param String $message
+ * @param array $param
+ * @param string $level
+ * @param int $code
+ * @return String
+ */
+ function _error($message,
+ $param= array(),
+ $level='error',
+ $code=JSPHON_ERROR_DECODE_SYNTAX)
+ {
+ $e = Jsphon_Error::push(
+ $code, $level, $param, $message, false, debug_backtrace());
+ $this->_internalError = true;
+ return $e;
+ }
+
+ function handleInternalError($err)
+ {
+ $this->_internalError = true;
+ }
+}
+
+?>
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+define('JSPHON_TOKEN_EOF', 1);
+define('JSPHON_TOKEN_DATUM', 2);
+define('JSPHON_TOKEN_LBRACE', 3);
+define('JSPHON_TOKEN_LBRACKET', 4);
+define('JSPHON_TOKEN_RBRACE', 5);
+define('JSPHON_TOKEN_RBRACKET', 6);
+define('JSPHON_TOKEN_COMMA', 7);
+define('JSPHON_TOKEN_COLON', 8);
+
+/**
+ * Jsphon_Decoder_Tokenizer
+ *
+ * @author Hawk
+ */
+class Jsphon_Decoder_Tokenizer
+{
+ var $_source;
+
+ var $_token;
+
+ var $_tokenValue;
+
+ var $_tokenTable = array(
+ '{' => JSPHON_TOKEN_LBRACE,
+ '}' => JSPHON_TOKEN_RBRACE,
+ '[' => JSPHON_TOKEN_LBRACKET,
+ ']' => JSPHON_TOKEN_RBRACKET,
+ ',' => JSPHON_TOKEN_COMMA,
+ ':' => JSPHON_TOKEN_COLON
+ );
+
+ var $_tokenValueTable = array(
+ 'true' => true,
+ 'false' => false,
+ 'null' => null
+ );
+
+ var $_errorCallback;
+
+ /**
+ * constructs a new Jsphon_Decoder_Tokenizer instance.
+ *
+ * @param String $source
+ */
+ function Jsphon_Decoder_Tokenizer($source, $internalErrorCallback=null)
+ {
+ $this->_source = $source;
+ $this->_errorCallback = $internalErrorCallback;
+ }
+
+ /**
+ * Retrieves the next token from the source stream.
+ *
+ * @return int or false
+ */
+ function nextToken()
+ {
+ $src = ltrim($this->_source);
+
+ $this->_tokenValue = null;
+ $this->_token = JSPHON_TOKEN_EOF;
+
+ switch(true)
+ {
+ case $src === '':
+ break;
+
+ case ($c = $src{0}) !== "" and isset($this->_tokenTable[$c]):
+ $this->_token = $this->_tokenTable[$c];
+ $src = substr($src, 1);
+ break;
+
+ case $c == '"' and preg_match('/^"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"/', $src, $m):
+ $this->_token = JSPHON_TOKEN_DATUM;
+ $this->_tokenValue = $m[1];
+ $src = substr($src, strlen($m[0]));
+ break;
+
+ case preg_match('/^(true|false|null)\b/', $src, $m):
+ $this->_token = JSPHON_TOKEN_DATUM;
+ $this->_tokenValue = $this->_tokenValueTable[$m[1]];
+ $src = substr($src, ($m[1]{0} == 'f' ? 5 : 4));
+ break;
+
+ case preg_match('/^-?(?:[1-9]\d+|\d)(?:\.\d+)?(?:[eE][-+]?\d+)?/', $src, $m):
+ $this->_token = JSPHON_TOKEN_DATUM;
+ $intV = (int)$m[0];
+ $floatV = (float)$m[0];
+ $this->_tokenValue = ($intV == $floatV) ? $intV : $floatV;
+ $src = substr($src, strlen($m[0]));
+ break;
+
+ default:
+ $err = Jsphon_Error::push(
+ JSPHON_ERROR_DECODE_SYNTAX,
+ 'error',
+ array(),
+ 'Illegal Token',
+ false,
+ debug_backtrace());
+
+ if(is_callable($this->_errorCallback)) {
+ call_user_func($this->_errorCallback, $err);
+ }
+ return false;
+
+ }
+
+ $this->_source = $src;
+ return $this->_token;
+ }
+
+ function getToken()
+ {
+ return $this->_token;
+ }
+
+ function getTokenValue()
+ {
+ return $this->_tokenValue;
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+/**
+ * Converts to JSON format.
+ *
+ * example:
+ * <code>
+ * //create a new instance of Jsphon_Encoder
+ * $json =& new Jsphon_Encoder();
+ *
+ * //convert a complex value to JSON notation, and send it to the browser
+ * $value = array('foo', 'bar', array('hoge' => array(1,2)));
+ * $output = $json->encode($value);
+ *
+ * print_r($output);
+ * //prints: ["foo","bar",{"hoge":[1,2]}]
+ * </code>
+ *
+ * @author Hawk
+ */
+class Jsphon_Encoder
+{
+ var $_mbstring;
+
+ var $_escapeNonASCII;
+
+ var $_escapeOverUCS2;
+
+ var $_transTable;
+
+ var $_utf8UCS2reg;
+
+ var $_utf8overUCS2reg;
+
+ /**
+ * constructs a new Jsphon_Encoder instance.
+ *
+ * @param bool $escapeNonASCII If true, encode() converts non-ASCII characters
+ * to Unicode escape sequence (\uXXXX).
+ * @param bool $escapeOverUCS2 If true, encode() converts all the non-ASCII characters
+ * that is encodable in UTF-16 to Unicode escape sequence.
+ * This parameter affects the encoder's behavior
+ * only if $escapeNonASCII is set to true and
+ * the multibyte string extension is available.
+ *
+ */
+ function Jsphon_Encoder($escapeNonASCII=true, $escapeOverUCS2=false)
+ {
+ $this->_escapeNonASCII = $escapeNonASCII;
+ $this->_escapeOverUCS2 = $escapeOverUCS2;
+ $this->_mbstring = extension_loaded('mbstring');
+
+ $this->_transTable = array(
+ "\x08" => '\b',
+ "\x09" => '\t',
+ "\x0A" => '\n',
+ "\x0C" => '\f',
+ "\x0D" => '\r',
+ "\x22" => '\"',
+ "\x2F" => '\/',
+ "\x5C" => '\\\\'
+ );
+
+ $utf8ucs2 = '[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}';
+ $utf16sur = '[\xF0-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2}';
+ $this->_utf8UCS2reg = "/{$utf8ucs2}/";
+ $this->_utf8overUCS2reg = "/{$utf8ucs2}|{$utf16sur}/";
+ }
+
+ /**
+ * encodes an arbitrary variables into JSON format.
+ *
+ * If $_escapeNonASCII is set to true,
+ * encode() removes all non-ASCII characters to make sure that
+ * returning value dosen't contain any non-ASCII characters.
+ *
+ * @param mixed $value
+ * @return string
+ */
+ function encode($value)
+ {
+ $json = $this->_encode($value);
+ if($this->_escapeNonASCII) {
+ $json = preg_replace('/[\x80-\xFF]/', '', $json);
+ }
+ return $json;
+ }
+
+ function _encode($value)
+ {
+ if($value === null) {
+ return 'null';
+
+ } elseif(is_bool($value)) {
+ return $value ? 'true' : 'false';
+
+ } elseif(is_int($value)) {
+ return (int)$value;
+
+ } elseif(is_float($value)) {
+ return (float)$value;
+
+ } elseif(is_string($value)) {
+ return '"'. $this->_encodeString($value) .'"';
+
+ } elseif(is_array($value)) {
+ if(($len = count($value)) > 0 &&
+ array_keys($value) !== range(0, $len - 1)) {
+ return $this->_encodeObject($value);
+ } else {
+ return '['. join(',', array_map(array(&$this, '_encode'), $value)) .']';
+ }
+
+ } elseif(is_object($value)) {
+ return $this->_encodeObject($value);
+
+ }
+
+ return 'null';
+ }
+
+ function _encodeObject($arr)
+ {
+ $result = array();
+ foreach($arr as $name => $value) {
+ $result[] = $this->_encode((string)$name) .':'. $this->_encode($value);
+ }
+ return '{'. join(',', $result) ."}";
+ }
+
+ function _encodeString($str)
+ {
+ $str = strtr($str, $this->_transTable);
+
+ if(!$this->_escapeNonASCII) {
+ return $str;
+ }
+
+ if(!$this->_mbstring) {
+ return $this->_escapeNonASCIIWithoutMbstring($str);
+ }
+
+ if($this->_escapeOverUCS2) {
+ return $this->_escapeNonASCIIOverUCS2($str);
+ } else {
+ return $this->_escapeNonASCII($str);
+ }
+ }
+
+ function _escapeNonASCII($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_utf8UCS2reg, $str, $matches)) {
+ return $str;
+ }
+
+ foreach($matches[0] as $utf8char) {
+ if(isset($transTable[$utf8char])) {
+ continue;
+ }
+ $transTable[$utf8char] = $this->_formatSeq(
+ mb_convert_encoding($utf8char, 'UTF-16', 'UTF-8'));
+ }
+ return strtr($str, $transTable);
+ }
+
+ function _escapeNonASCIIOverUCS2($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_utf8overUCS2reg, $str, $matches)) {
+ return $str;
+ }
+
+ foreach($matches[0] as $utf8char) {
+ if(isset($transTable[$utf8char])) {
+ continue;
+ }
+ $utf16char = mb_convert_encoding($utf8char, 'UTF-16', 'UTF-8');
+
+ if(($l = strlen($utf16char)) == 2) {
+ $transTable[$utf8char] = $this->_formatSeq($utf16char);
+ } elseif($l == 4) {
+ $transTable[$utf8char] =
+ $this->_formatSeq(substr($utf16char, 0, 2)) . $this->_formatSeq(substr($utf16char, 2));
+ }
+
+ }
+ return strtr($str, $transTable);
+ }
+
+ function _escapeNonASCIIWithoutMbstring($str)
+ {
+ $transTable = array();
+
+ if(!preg_match_all($this->_utf8UCS2reg, $str, $matches)) {
+ return $str;
+ }
+
+ foreach($matches[0] as $utf8char) {
+ if(isset($transTable[$utf8char])) {
+ continue;
+ }
+
+ switch(strlen($utf8char)) {
+ case 2:
+ $code = (
+ ((ord($utf8char{0}) & 0x1F) << 6) |
+ (ord($utf8char{1}) & 0x3F)
+ );
+ break;
+
+ case 3:
+ $code = (
+ ((ord($utf8char{0}) & 0x0F) << 12) |
+ ((ord($utf8char{1}) & 0x3F) << 6 ) |
+ (ord($utf8char{2}) & 0x3F)
+ );
+ break;
+ default:
+ }
+ $transTable[$utf8char] = '\u'. substr('0000'. dechex($code), -4);
+ }
+ return strtr($str, $transTable);
+ }
+
+ function _formatSeq($u16)
+ {
+ return '\u'. substr('0000'. bin2hex($u16), -4);
+ }
+}
+
+?>
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+/**
+ * Jsphon_Error
+ *
+ * @author Hawk
+ */
+class Jsphon_Error
+{
+ var $_isPHP5;
+
+ var $_existsPEAR;
+
+ var $_throwException = true;
+
+ var $_exceptionCreator;
+
+ /**
+ * Constructor
+ *
+ * @access private
+ */
+ function Jsphon_Error()
+ {
+ $this->_isPHP5 = version_compare(phpversion(), '5', '>=');
+ $this->_existsPEAR = (@include_once "PEAR/ErrorStack.php");
+
+ if($this->_isPHP5) {
+ if($this->_existsPEAR) {
+ require_once(dirname(__FILE__) .'/Exception.php');
+ $method = '_createJsphonException';
+ } else {
+ $method = '_createException';
+ }
+ $this->_exceptionCreator = array(__CLASS__, $method);
+ }
+ }
+
+ /**
+ *
+ *
+ * @since 06/09/05 13:21
+ * @return Jsphon_Error
+ */
+ function &singleton()
+ {
+ static $instance = null;
+ if($instance === null) {
+ $instance = new Jsphon_Error();
+ }
+ return $instance;
+ }
+
+ /**
+ *
+ * @static
+ * @param int $code
+ * @param string $level
+ * @param array $params
+ * @param string $msg
+ * @param mixed $repackage
+ * @param array $backtrace
+ * @return array
+ */
+ function push($code, $level = 'error', $params = array(), $msg = false,
+ $repackage = false, $backtrace = false)
+ {
+ if (!$backtrace) {
+ $backtrace = debug_backtrace();
+ }
+
+ $self =& Jsphon_Error::singleton();
+
+ if(!$self->_isPHP5 || !$self->_throwException) {
+ return $self->_push(
+ $code, $level, $params, $msg, $repackage, $backtrace);
+ }
+
+ $pushOnly = true;
+ $err = $self->_push(
+ $code, $level, $params, $msg, $repackage, $backtrace, $pushOnly);
+
+ array_shift($backtrace);
+ throw (call_user_func($self->_exceptionCreator, $msg, $code, $backtrace));
+ return ;
+ }
+
+ /**
+ *
+ *
+ * @access private
+ * @static
+ * @param int $code
+ * @param string $level
+ * @param array $params
+ * @param string $msg
+ * @param mixed $repackage
+ * @param array $backtrace
+ * @param bool $pushOnly
+ * @return array or null unless PEAR_ErrorStack exists.
+ */
+ function _push($code, $level='error', $params=array(), $msg=false,
+ $repackage=false, $backtrace=false, $pushOnly=false)
+ {
+ if(!$this->_existsPEAR) {
+ return;
+ }
+
+ $stack =& PEAR_ErrorStack::singleton('Jsphon');
+ if($pushOnly) {
+ $stack->pushCallback(array(__CLASS__, 'pushOnly'));
+ }
+
+ $err = $stack->push($code, $level, $params, $msg, $repackage, $backtrace);
+
+ if($pushOnly) {
+ $stack->popCallback();
+ }
+ return $err;
+ }
+
+ /**
+ *
+ * @since 06/09/05 13:34
+ * @param String $bool
+ * @param String $exceptionClass
+ * @return String
+ */
+ function setThrowException($bool)
+ {
+ $this->_throwException = $bool;
+ }
+
+ /**
+ * error handler which do nothing
+ *
+ * @since 06/09/05 13:52
+ * @param array $err
+ * @return int
+ */
+ function pushOnly($err)
+ {
+ return PEAR_ERRORSTACK_PUSH;
+ }
+
+ /**
+ *
+ * @static
+ * @param String $msg
+ * @param int $code
+ * @param array $trace
+ * @return Exception
+ */
+ function _createJsphonException($msg, $code, $trace)
+ {
+ return new Jsphon_Exception($msg, $code, $trace);
+ }
+
+ /**
+ *
+ * @static
+ * @param String $msg
+ * @param int $code
+ * @param array $trace
+ * @return Exception
+ */
+ function _createException($msg, $code, $trace)
+ {
+ return new Exception($msg, $code);
+ }
+}
+
+
+?>
--- /dev/null
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PHP version 4 and 5 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 2006 Hawk |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Hawk <scholar@hawklab.jp> |
+// +--------------------------------------------------------------------+
+//
+//
+
+require_once('PEAR/Exception.php');
+
+/**
+ * Jsphon_Exception
+ *
+ * @author Hawk
+ */
+class Jsphon_Exception extends PEAR_Exception
+{
+ private $_outerTrace;
+
+ /**
+ * Constructor
+ *
+ */
+ public function __construct($msg, $code, $outerTrace)
+ {
+ $this->_outerTrace = $outerTrace;
+ parent::__construct($msg, $code);
+ }
+
+ /**
+ *
+ *
+ * @override
+ * @return array
+ */
+ public function getTraceSafe()
+ {
+ if(count($this->_outerTrace) > 0) {
+ return $this->_outerTrace;
+ }
+ return parent::getTraceSafe();
+ }
+
+}
+
+?>
--- /dev/null
+<?php
+// vim: tabstop=2:shiftwidth=2
+
+/**
+ * Feedback.php ($Revision: 1.1 $)
+ *
+ * by hsur ( http://blog.cles.jp/np_cles )
+ * $Id: Feedback.php,v 1.1 2008-05-17 19:11:11 hsur Exp $
+*/
+
+/*
+ * Copyright (C) 2006 CLES. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
+ * permission to link the code of this program with those files in the PEAR
+ * library that are licensed under the PHP License (or with modified versions
+ * of those files that use the same license as those files), and distribute
+ * linked combinations including the two. You must obey the GNU General Public
+ * License in all respects for all of the code used other than those files in
+ * the PEAR library that are licensed under the PHP License. If you modify
+ * this file, you may extend this exception to your version of the file,
+ * but you are not obligated to do so. If you do not wish to do so, delete
+ * this exception statement from your version.
+*/
+
+class cles_Feedback {
+ var $oPluginAdmin;
+ function CLES_Feedback(&$pluginAdmin){
+ $this->oPluginAdmin = $pluginAdmin;
+ }
+
+ function getMenuStr(){
+ return mb_convert_encoding('動作確認/不具合報告', _CHARSET, 'UTF-8');
+ }
+
+ function printForm($extra = '') {
+ ob_start();
+
+ global $nucleus, $CONF;
+
+ echo "<h2>動作確認/不具合報告</h2>";
+ echo '<p>下記より、作者への動作確認/不具合の報告を行うことができます。</p>';
+
+ // js
+ echo '<script langage="JavaScript">
+ function selectall(){
+ var elements = document.getElementsByTagName(\'input\');
+ for( var i=0; i < elements.length; i++){
+ var e = elements[i];
+ if( e.type == \'checkbox\' ){
+ e.checked = true;
+ }
+ }
+ return false;
+ }
+ </script>';
+
+ echo "<h3>収集する情報と公開について</h3>";
+ echo '<p>デフォルトで必要最低限の環境情報(赤字のもの)を開発者のサーバへ送信します。<br />
+ <span style="font-weight:bold; color:red">差し支えない範囲で環境情報の提供にご協力ください。</span></p>
+ <p>※ 収集した情報は統計処理、及びプラグインのBugFixのみに利用されます。また統計処理した結果については公表することがあります。</p>';
+ echo '<p><a href="#" onclick="javascript:selectall();return false;">全て送信する場合はここをクリック</a></p>';
+
+ echo "<h3>サイト固有コードについて</h3>";
+ echo '<p>動作報告の重複を取り除くため、管理画面のURLのmd5を計算したものを送信しています。この情報から管理画面のURLを復元することはできないようになっています。<a href="http://computers.yahoo.co.jp/dict/security/hash/677.html" target="_blank">md5の解説についてはこちらをご覧ください。(Yahoo!コンピュータ用語辞典)</a></p>';
+
+ // form
+ echo '<form method="post" action="http://blog.cles.jp/support/report.php">' . "\n";
+
+ // table
+ echo "<table>\n";
+ echo "<tr>\n";
+ echo "<th>項目の説明</th>\n";
+ echo "<th>送信される値</th>\n";
+ echo "<th><a href=\"#\" onclick=\"javascript:selectall();return false;\">全て送信する</th>\n";
+ echo "</tr>\n";
+
+ $res = sql_query("show variables like 'version'");
+ $assoc = mysql_fetch_assoc($res);
+ $mysqlVersion = $assoc['Value'];
+
+ if( function_exists('gd_info') )
+ $gdinfo = @gd_info();
+ else
+ $gdinfo['GD Version'] = 'GD is not supported';
+
+ global $CONF;
+
+ $this->_printtr('siteid', 'サイトの固有コード', md5(trim($CONF['AdminURL'])));
+ $this->_printtr('plugin_name', 'プラグイン名', $this->oPluginAdmin->plugin->getName());
+ $this->_printtr('plugin_version', 'プラグインのバージョン', $this->oPluginAdmin->plugin->getVersion());
+ $this->_printtr('plugin_info', 'プラグインの情報', $extra, true);
+ $this->_printtr('nucleus_version', 'Nucleusのバージョン', $nucleus['version'], true);
+ $this->_printtr('nucleus_charset', 'Nucleusのキャラクタセット', _CHARSET);
+ $this->_printtr('php_version', 'PHPのバージョン', PHP_VERSION, true);
+ $this->_printtr('php_sapi', 'PHPの種類', php_sapi_name());
+ $this->_printtr('php_os', 'OSの種類', PHP_OS, true);
+ $this->_printtr('php_safemode', 'セーフモードの有無', ini_get('safe_mode') ? 'on' : 'off');
+ $this->_printtr('php_gd_version', 'GDのバージョン', $gdinfo['GD Version'], true);
+ $this->_printtr('php_gd_support', 'サポートしているイメージタイプ', implode(',', $this->_supportedImageTypes()) );
+ $this->_printtr('mysql_version', 'MySQLのバージョン', $mysqlVersion, true);
+
+ echo "<tr>\n";
+ echo "<td>このプラグインは機能しましたか?</td>\n";
+ echo '<td colsan="2"><input type="radio" name="user_intention" value="ok" />はい <br/> <input type="radio" name="intention" value="ng" />いいえ'."</td>\n";
+ echo "</tr>\n";
+
+ echo "<tr>\n";
+ echo "<td>不具合の内容をお寄せください<br /><em>必ず回答が必要な質問については、<a href=\"http://japan.nucleuscms.org/bb/\">Nucleusサポートフォーラム</a>もしくは<a href=\"http://blog.cles.jp/np_cles/\">作者ページ</a>でご質問ください。</em></td>\n";
+ echo '<td colspan="2"><textarea name="user_freetext" rows="10" cols="70"></textarea>'."</td>\n";
+ echo "</tr>\n";
+
+ echo "<tr>\n";
+ echo "<td>よろしければサイトのURLを教えてください</td>\n";
+ echo '<td colspan="2"><textarea name="user_url" rows="1" cols="70"></textarea>'."</td>\n";
+ echo "</tr>\n";
+
+ echo "<tr>\n";
+ echo "<td>リンク集作成の際、リンクをはらせていただけますか?</td>\n";
+ echo '<td colspan="2"><input type="radio" name="user_disclose" value="yes" />はい <br/> <input type="radio" name="intention" value="no" />いいえ'."</td>\n";
+ echo "</tr>\n";
+
+ echo '<tr><td colspan="3"><div align="right"><input type="submit" name="submit" value="動作確認を送信する" /></div></td></tr>';
+ echo "</table>\n";
+ echo "</form>\n";
+
+ $contents = ob_get_contents();
+ ob_end_clean();
+ echo mb_convert_encoding($contents, _CHARSET, 'UTF-8');
+ }
+
+ function _printtr($name, $desc, $value, $canDisable = false) {
+ echo "<tr>\n";
+
+ if ($canDisable) {
+ echo "<td>".$desc."</td>\n";
+ echo "<td>".htmlspecialchars($value)."</td>\n";
+ echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" /></td>'."\n";
+ } else {
+ echo '<td><span style="font-weight:bold; color:red">'.$desc."</span></td>\n";
+ echo '<td><span style="font-weight:bold; color:red">'.htmlspecialchars($value)."</span></td>\n";
+ echo '<td><input type="checkbox" name="'.htmlspecialchars($name).'" value="'.htmlspecialchars($value).'" readonly="readonly" checked="checked"/></span></td>'."\n";
+ }
+ echo "</tr>\n";
+ }
+
+ function _supportedImageTypes() {
+ if( !function_exists('gd_info') ) return "";
+
+ $aSupportedTypes = array ();
+ $aPossibleImageTypeBits = array (IMG_GIF => 'GIF', IMG_JPG => 'JPG', IMG_PNG => 'PNG', IMG_WBMP => 'WBMP');
+
+ foreach ($aPossibleImageTypeBits as $iImageTypeBits => $sImageTypeString) {
+ if (imagetypes() & $iImageTypeBits) {
+ $aSupportedTypes[] = $sImageTypeString;
+ }
+ }
+
+ return $aSupportedTypes;
+ }
+
+}
--- /dev/null
+<?php
+// vim: tabstop=2:shiftwidth=2
+
+/**
+ * Template.php ($Revision: 1.1 $)
+ *
+ * by hsur ( http://blog.cles.jp/np_cles )
+ * $Id: Template.php,v 1.1 2008-05-17 19:11:11 hsur Exp $
+*/
+
+/*
+ * Copyright (C) 2006 CLES. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
+ * permission to link the code of this program with those files in the PEAR
+ * library that are licensed under the PHP License (or with modified versions
+ * of those files that use the same license as those files), and distribute
+ * linked combinations including the two. You must obey the GNU General Public
+ * License in all respects for all of the code used other than those files in
+ * the PEAR library that are licensed under the PHP License. If you modify
+ * this file, you may extend this exception to your version of the file,
+ * but you are not obligated to do so. If you do not wish to do so, delete
+ * this exception statement from your version.
+*/
+
+class cles_Template {
+ var $defaultLang = 'japanese-utf8';
+ var $defalutPattern = '#{{(.*?)(\|)?}}#ie';
+ var $lang;
+ var $templateDir;
+
+ function cles_Template($templateDir) {
+ global $CONF;
+ $this->templateDir = $templateDir;
+ $this->lang = ereg_replace( '[\\|/]', '', getLanguageName());
+ }
+
+ function fetch($name, $dir = null, $suffix = 'html') {
+ $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->lang.( $suffix ? '.'.strtolower($suffix) : '' );
+ if ( ! file_exists($path) ){
+ $path = $this->templateDir.'/'.( $dir ? strtolower($dir) . '/' : '' ).strtolower($name).'_'.$this->defaultLang.( $suffix ? '.'.strtolower($suffix) : '' );
+ if ( ! file_exists($path) )
+ return '';
+ }
+
+ $fsize = filesize($path);
+ if ($fsize <= 0) return '';
+
+ $fd = fopen($path, 'r');
+ $contents = fread($fd, $fsize);
+ fclose($fd);
+ return $contents;
+ }
+
+ function fill($template, $values, $default = null) {
+ if( $default )
+ return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : $default', $template);
+ if( $default === null )
+ return preg_replace($this->defalutPattern, '("$2") ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]', $template);
+ return preg_replace($this->defalutPattern, 'isset($values["$1"]) ? ("$2" ? htmlspecialchars($values["$1"], ENT_QUOTES) : $values["$1"]) : "{{$1}}" ', $template);
+ }
+}
--- /dev/null
+<?php
+// vim: tabstop=2:shiftwidth=2
+
+/**
+ * sharedlibs.php ($Revision: 1.1 $)
+ *
+ * by hsur ( http://blog.cles.jp/np_cles )
+ * $Id: sharedlibs.php,v 1.1 2008-05-17 19:11:11 hsur Exp $
+*/
+
+/*
+ * Copyright (C) 2006 CLES. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * In addition, as a special exception, cles( http://blog.cles.jp/np_cles ) gives
+ * permission to link the code of this program with those files in the PEAR
+ * library that are licensed under the PHP License (or with modified versions
+ * of those files that use the same license as those files), and distribute
+ * linked combinations including the two. You must obey the GNU General Public
+ * License in all respects for all of the code used other than those files in
+ * the PEAR library that are licensed under the PHP License. If you modify
+ * this file, you may extend this exception to your version of the file,
+ * but you are not obligated to do so. If you do not wish to do so, delete
+ * this exception statement from your version.
+*/
+
+if (!defined('NP_SHAREDLIBS_LOADED')) {
+ if (!defined('PATH_SEPARATOR')) {
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ define('PATH_SEPARATOR', ';');
+ } else {
+ define('PATH_SEPARATOR', ':');
+ }
+ }
+ ini_set('include_path', dirname(__FILE__).PATH_SEPARATOR.ini_get('include_path'));
+
+ define('NP_SHAREDLIBS_LOADED', true);
+}