From 6630dd0bb73aefa8567dbe3f154ec52231e3573c Mon Sep 17 00:00:00 2001 From: wangyidong Date: Tue, 10 Dec 2019 13:55:01 +0800 Subject: [PATCH] * code for task #6708. --- config/filter.php | 14 + config/zentaopms.php | 17 +- db/update11.7.sql | 66 ++ framework/base/router.class.php | 2 +- lib/scm/config.php | 3 + lib/scm/git.class.php | 455 +++++++++ lib/scm/scm.class.php | 107 +++ lib/scm/scmTest.php | 15 + lib/scm/subversion.class.php | 464 ++++++++++ module/admin/model.php | 2 +- module/common/lang/menuOrder.php | 13 +- module/common/lang/zh-cn.php | 7 + module/repo/config.php | 32 + module/repo/control.php | 642 +++++++++++++ module/repo/css/common.css | 179 ++++ module/repo/css/diff.css | 22 + module/repo/css/revision.css | 3 + module/repo/css/view.css | 14 + module/repo/js/common.js | 86 ++ module/repo/js/diff.js | 5 + module/repo/js/log.js | 18 + module/repo/js/view.js | 19 + module/repo/lang/de.php | 150 +++ module/repo/lang/en.php | 150 +++ module/repo/lang/fr.php | 150 +++ module/repo/lang/zh-cn.php | 150 +++ module/repo/lang/zh-tw.php | 150 +++ module/repo/model.php | 964 ++++++++++++++++++++ module/repo/view/ajaxsidelogs.html.php | 88 ++ module/repo/view/create.html.php | 83 ++ module/repo/view/diff.html.php | 174 ++++ module/repo/view/log.html.php | 76 ++ module/repo/view/revision.html.php | 91 ++ module/repo/view/settings.html.php | 87 ++ module/repo/view/showsynccomment.html.php | 45 + module/repo/view/view.html.php | 98 ++ www/js/misc/highlight/export.html | 87 ++ www/js/misc/highlight/highlight.pack.js | 1 + www/js/misc/highlight/styles/github.css | 124 +++ www/js/misc/highlight/styles/googlecode.css | 147 +++ 40 files changed, 4988 insertions(+), 12 deletions(-) create mode 100644 db/update11.7.sql create mode 100644 lib/scm/config.php create mode 100644 lib/scm/git.class.php create mode 100644 lib/scm/scm.class.php create mode 100644 lib/scm/scmTest.php create mode 100644 lib/scm/subversion.class.php create mode 100644 module/repo/config.php create mode 100644 module/repo/control.php create mode 100644 module/repo/css/common.css create mode 100644 module/repo/css/diff.css create mode 100644 module/repo/css/revision.css create mode 100644 module/repo/css/view.css create mode 100644 module/repo/js/common.js create mode 100644 module/repo/js/diff.js create mode 100644 module/repo/js/log.js create mode 100644 module/repo/js/view.js create mode 100644 module/repo/lang/de.php create mode 100644 module/repo/lang/en.php create mode 100644 module/repo/lang/fr.php create mode 100644 module/repo/lang/zh-cn.php create mode 100644 module/repo/lang/zh-tw.php create mode 100644 module/repo/model.php create mode 100644 module/repo/view/ajaxsidelogs.html.php create mode 100644 module/repo/view/create.html.php create mode 100644 module/repo/view/diff.html.php create mode 100644 module/repo/view/log.html.php create mode 100644 module/repo/view/revision.html.php create mode 100644 module/repo/view/settings.html.php create mode 100644 module/repo/view/showsynccomment.html.php create mode 100644 module/repo/view/view.html.php create mode 100755 www/js/misc/highlight/export.html create mode 100644 www/js/misc/highlight/highlight.pack.js create mode 100644 www/js/misc/highlight/styles/github.css create mode 100644 www/js/misc/highlight/styles/googlecode.css diff --git a/config/filter.php b/config/filter.php index 2fb1c193d3..ac8436cdab 100644 --- a/config/filter.php +++ b/config/filter.php @@ -45,6 +45,7 @@ $filter->mail = new stdclass(); $filter->user = new stdclass(); $filter->block = new stdclass(); $filter->file = new stdclass(); +$filter->repo = new stdclass(); $filter->block->default = new stdclass(); $filter->block->main = new stdclass(); @@ -212,3 +213,16 @@ $filter->git->cat->get['repoUrl'] = 'reg::base64'; $filter->git->diff->get['repoUrl'] = 'reg::base64'; $filter->svn->cat->get['repoUrl'] = 'reg::base64'; $filter->svn->diff->get['repoUrl'] = 'reg::base64'; + +$filter->repo->default = new stdclass(); +$filter->repo->diff = new stdclass(); +$filter->repo->view = new stdclass(); + +$filter->repo->default->get['path'] = 'reg::base64'; +$filter->repo->default->get['entry'] = 'reg::base64'; + +$filter->repo->default->cookie['repoBranch'] = 'reg::any'; +$filter->repo->diff->cookie['arrange'] = 'reg::word'; +$filter->repo->diff->cookie['repoPairs'] = 'array'; +$filter->repo->view->cookie['repoPairs'] = 'array'; +$filter->repo->ajaxsynccomment->cookie['syncBranch'] = 'reg::any'; diff --git a/config/zentaopms.php b/config/zentaopms.php index 1f2afa89ae..61d3d5e986 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -163,12 +163,17 @@ define('TABLE_TESTSUITE', '`' . $config->db->prefix . 'testsuite`'); define('TABLE_SUITECASE', '`' . $config->db->prefix . 'suitecase`'); define('TABLE_TESTREPORT', '`' . $config->db->prefix . 'testreport`'); -define('TABLE_ENTRY', '`' . $config->db->prefix . 'entry`'); -define('TABLE_WEBHOOK', '`' . $config->db->prefix . 'webhook`'); -define('TABLE_LOG', '`' . $config->db->prefix . 'log`'); -define('TABLE_SCORE', '`' . $config->db->prefix . 'score`'); -define('TABLE_NOTIFY', '`' . $config->db->prefix . 'notify`'); -define('TABLE_OAUTH', '`' . $config->db->prefix . 'oauth`'); +define('TABLE_ENTRY', '`' . $config->db->prefix . 'entry`'); +define('TABLE_WEBHOOK', '`' . $config->db->prefix . 'webhook`'); +define('TABLE_LOG', '`' . $config->db->prefix . 'log`'); +define('TABLE_SCORE', '`' . $config->db->prefix . 'score`'); +define('TABLE_NOTIFY', '`' . $config->db->prefix . 'notify`'); +define('TABLE_OAUTH', '`' . $config->db->prefix . 'oauth`'); + +define('TABLE_REPO', '`' . $config->db->prefix . 'repo`'); +define('TABLE_REPOHISTORY', '`' . $config->db->prefix . 'repohistory`'); +define('TABLE_REPOFILES', '`' . $config->db->prefix . 'repofiles`'); +define('TABLE_REPOBRANCH', '`' . $config->db->prefix . 'repobranch`'); if(!defined('TABLE_LANG')) define('TABLE_LANG', '`' . $config->db->prefix . 'lang`'); $config->objectTables['product'] = TABLE_PRODUCT; diff --git a/db/update11.7.sql b/db/update11.7.sql new file mode 100644 index 0000000000..d1a3049229 --- /dev/null +++ b/db/update11.7.sql @@ -0,0 +1,66 @@ +-- DROP TABLE IF EXISTS `zt_repo`; +CREATE TABLE IF NOT EXISTS `zt_repo` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL, + `path` varchar(255) NOT NULL, + `prefix` varchar(100) NOT NULL, + `encoding` varchar(20) NOT NULL, + `SCM` varchar(10) NOT NULL, + `client` varchar(100) NOT NULL, + `commits` mediumint(8) unsigned NOT NULL, + `account` varchar(30) NOT NULL, + `password` varchar(30) NOT NULL, + `encrypt` varchar(30) NOT NULL DEFAULT 'plain', + `acl` text NOT NULL, + `synced` tinyint(1) NOT NULL DEFAULT '0', + `lastSync` datetime NOT NULL, + `deleted` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- DROP TABLE IF EXISTS `zt_repobranch`; +CREATE TABLE IF NOT EXISTS `zt_repobranch` ( + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `branch` varchar(255) NOT NULL, + UNIQUE KEY `repo_revision_branch` (`repo`,`revision`,`branch`), + KEY `branch` (`branch`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- DROP TABLE IF EXISTS `zt_repohistory`; +CREATE TABLE IF NOT EXISTS `zt_repohistory` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `repo` mediumint(9) NOT NULL, + `revision` varchar(40) NOT NULL, + `commit` mediumint(8) unsigned NOT NULL, + `comment` text NOT NULL, + `committer` varchar(100) NOT NULL, + `time` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- DROP TABLE IF EXISTS `zt_repofiles`; +CREATE TABLE IF NOT EXISTS `zt_repofiles` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `repo` mediumint(8) unsigned NOT NULL, + `revision` mediumint(8) unsigned NOT NULL, + `path` varchar(255) NOT NULL, + `parent` varchar(255) NOT NULL, + `type` varchar(20) NOT NULL, + `action` char(1) NOT NULL, + PRIMARY KEY (`id`), + KEY `path` (`path`), + KEY `parent` (`parent`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_bug` CHANGE `caseVersion` `caseVersion` smallint(6) NOT NULL AFTER `case`; +ALTER TABLE `zt_bug` ADD `repo` mediumint(8) unsigned NOT NULL AFTER `result`; +ALTER TABLE `zt_bug` ADD `lines` varchar(10) COLLATE 'utf8_general_ci' NOT NULL AFTER `repo`; +ALTER TABLE `zt_bug` ADD `v1` varchar(40) COLLATE 'utf8_general_ci' NOT NULL AFTER `lines`; +ALTER TABLE `zt_bug` ADD `v2` varchar(40) COLLATE 'utf8_general_ci' NOT NULL AFTER `v1`; +ALTER TABLE `zt_bug` ADD `repoType` varchar(30) COLLATE 'utf8_general_ci' NOT NULL DEFAULT '' AFTER `v2`; +ALTER TABLE `zt_bug` ADD `entry` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `repo`; diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 6274f40f78..ed768a633a 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -2273,7 +2273,7 @@ class baseRouter * 为了安全起见,对公网环境隐藏脚本路径。 * If the ip is pulic, hidden the full path of scripts. */ - $remoteIP = helper::getRemoteIp(); + $remoteIP = zget($_SERVER, "REMOTE_ADDR", ''); if(!defined('IN_SHELL') and !($remoteIP == '127.0.0.1' or filter_var($remoteIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false)) { $errorLog = str_replace($this->getBasePath(), '', $errorLog); diff --git a/lib/scm/config.php b/lib/scm/config.php new file mode 100644 index 0000000000..80d0d8d16d --- /dev/null +++ b/lib/scm/config.php @@ -0,0 +1,3 @@ +debug = false; diff --git a/lib/scm/git.class.php b/lib/scm/git.class.php new file mode 100644 index 0000000000..6e9b943fae --- /dev/null +++ b/lib/scm/git.class.php @@ -0,0 +1,455 @@ +client = $client; + $this->root = rtrim($root, DIRECTORY_SEPARATOR); + $this->branch = isset($_COOKIE['repoBranch']) ? $_COOKIE['repoBranch'] : ''; + + chdir($this->root); + exec("{$this->client} config core.quotepath false"); + } + + public function ls($path, $revision = 'HEAD') + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + $sub = ''; + chdir($this->root); + if(!empty($path)) $sub = ":$path"; + if(!empty($this->branch))$revision = $this->branch; + $cmd = escapeCmd("$this->client ls-tree -l $revision$sub"); + $list = execCmd($cmd . ' 2>&1', 'array', $result); + if($result) return array(); + + $infos = array(); + foreach($list as $entry) + { + list($mod, $kind, $revision, $size, $name) = preg_split('/[\t ]+/', $entry); + + /* Get commit info. */ + $pathName = ltrim($path . DIRECTORY_SEPARATOR . $name, DIRECTORY_SEPARATOR); + $cmd = escapeCmd("$this->client log -1 $this->branch -- $pathName"); + $commit = execCmd($cmd, 'array'); + $logs = $this->parseLog($commit); + + if($size > 1024 * 1024) + { + $size = round($size / (1024 * 1024), 2) . 'MB'; + } + else if($size > 1024) + { + $size = round($size / 1024, 2) . 'KB'; + } + else + { + $size .= 'Bytes'; + } + + $info = new stdClass(); + $info->name = $name; + $info->kind = $kind == 'tree' ? 'dir' : 'file'; + $info->revision = $logs ? $logs[0]->revision : $revision; + $info->size = $size; + $info->account = $logs ? $logs[0]->committer : ''; + $info->date = $logs ? $logs[0]->time : ''; + $info->comment = $logs ? $logs[0]->comment : ''; + $infos[] = $info; + unset($info); + } + + /* Sort by kind */ + foreach($infos as $key => $info) $kinds[$key] = $info->kind; + if($infos) array_multisort($kinds, SORT_ASC, $infos); + + return $infos; + } + + public function branch() + { + chdir($this->root); + + /* Get local branch. */ + $cmd = escapeCmd("$this->client branch"); + $list = execCmd($cmd . ' 2>&1', 'array', $result); + if($result) return array(); + + $branches = array(); + foreach($list as $localBranch) + { + if($localBranch{0} == '*') $localBranch = substr($localBranch, 1); + + $localBranch = trim($localBranch); + $branches[$localBranch] = $localBranch; + } + asort($branches); + return $branches; + } + + public function getLastLog($path, $count = 10) + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + $revision = $this->branch ? $this->branch : 'HEAD'; + + chdir($this->root); + $list = execCmd(escapeCmd("$this->client log -10 $revision -- $path"), 'array'); + $logs = $this->parseLog($list); + + return $logs; + } + + public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0) + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + $count = $count == 0 ? '' : "-n $count"; + /* compatible with svn. */ + if($fromRevision == 'HEAD' and $this->branch) $fromRevision = $this->branch; + if($toRevision == 'HEAD' and $this->branch) $toRevision = $this->branch; + if($fromRevision === $toRevision) + { + $logs = array(); + chdir($this->root); + + $list = execCmd(escapeCmd("$this->client log --stat=1024 --name-status -1 $fromRevision -- $path"), 'array'); + $logs = $this->parseLog($list); + return $logs; + } + + if(!$fromRevision) + { + $revisions = " $toRevision"; + } + else + { + $revisions = "$fromRevision..$toRevision"; + } + chdir($this->root); + $list = execCmd(escapeCmd("$this->client log $count $revisions -- $path"), 'array'); + $logs = $this->parseLog($list); + + return $logs; + } + + public function blame($path, $revision) + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + chdir($this->root); + $list = execCmd(escapeCmd("$this->client blame -l $revision -- $path"), 'array'); + + $blames = array(); + $revLine = 0; + $revision = ''; + foreach($list as $line) + { + if(empty($line)) continue; + if($line{0} == '^') $line = substr($line, 1); + preg_match('/^([0-9a-f]{39,40})\s.*\((\S+)\s+([\d-]+)\s(.*)\s(\d+)\)(.*)$/U', $line, $matches); + + if(isset($matches[1]) and $matches[1] != $revision) + { + $blame = array(); + $blame['revision'] = $matches[1]; + $blame['committer'] = $matches[2]; + $blame['time'] = $matches[3]; + $blame['line'] = $matches[5]; + $blame['lines'] = 1; + $blame['content'] = strpos($matches[6], ' ') === false ? $matches[6] : substr($matches[6], 1); + + $revision = $matches[1]; + $revLine = $matches[5]; + $blames[$revLine] = $blame; + } + elseif(isset($matches[5])) + { + $blame = array(); + $blame['line'] = $matches[5]; + $blame['content'] = strpos($matches[6], ' ') === false ? $matches[6] : substr($matches[6], 1); + + $blames[$matches[5]] = $blame; + $blames[$revLine]['lines'] ++; + } + } + return $blames; + } + + public function diff($path, $fromRevision, $toRevision) + { + $path = ltrim($path, DIRECTORY_SEPARATOR); + chdir($this->root); + if($toRevision == 'HEAD' and $this->branch) $toRevision = $this->branch; + if($fromRevision == '^') $fromRevision = $toRevision . '^'; + if(strpos($fromRevision, '^') !== false) + { + $list = execCmd(escapeCmd("$this->client log -2 $toRevision --pretty=format:%H -- $path"), 'array'); + if(isset($list[1])) $fromRevision = $list[1]; + } + $lines = execCmd(escapeCmd("$this->client diff $fromRevision $toRevision -- $path"), 'array'); + return $lines; + } + + public function cat($entry, $revision = 'HEAD') + { + chdir($this->root); + if($revision == 'HEAD' and $this->branch) $revision = $this->branch; + $cmd = escapeCmd("$this->client show $revision:$entry"); + $content = execCmd($cmd); + if(is_array($content)) $content = implode("\n", $content); + return $content; + } + + public function info($entry, $revision = 'HEAD') + { + chdir($this->root); + if($revision == 'HEAD' and $this->branch) $revision = $this->branch; + $path = ltrim($entry, DIRECTORY_SEPARATOR); + $cmd = escapeCmd("$this->client ls-tree $revision -- $path"); + $result = execCmd($cmd); + $kind = ''; + if($result) + { + $results = explode("\n", trim($result)); + if(count($results) >= 2) + { + $kind = 'dir'; + } + else + { + list($mode, $type) = explode(' ', $results[0]); + $kind = $type == 'tree' ? 'dir' : 'file'; + } + } + + $list = execCmd(escapeCmd("$this->client log -1 $revision --pretty=format:%H -- $path"), 'array'); + $revision = $list[0]; + $info = new stdclass(); + $info->kind = $kind; + $info->path = $entry; + $info->revision = $revision; + $info->root = $this->root; + return $info; + } + + public function parseDiff($lines) + { + if(empty($lines)) return array(); + $diffs = array(); + $num = count($lines); + $endLine = end($lines); + if(strpos($endLine, '\ No newline at end of file') === 0) $num -= 1; + + $newFile = false; + for($i = 0; $i < $num; $i ++) + { + $diffFile = new stdclass(); + if(strpos($lines[$i], "diff --git ") === 0) + { + $fileInfo = explode(' ',$lines[$i]); + $fileName = substr($fileInfo[2], strpos($fileInfo[2], '/') + 1); + $diffFile->fileName = $fileName; + for($i++; $i < $num; $i ++) + { + $diff = new stdclass(); + /* Fix bug #1757. */ + if($lines[$i] == '+++ /dev/null') $newFile = true; + if(strpos($lines[$i], '+++', 0) !== false) continue; + if(strpos($lines[$i], '---', 0) !== false) continue; + if(strpos($lines[$i], '======', 0) !== false) continue; + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@/A', $lines[$i])) + { + $startLines = trim(str_replace(array('@', '+', '-'), '', $lines[$i])); + list($oldStartLine, $newStartLine) = explode(' ', $startLines); + list($diff->oldStartLine) = explode(',', $oldStartLine); + list($diff->newStartLine) = explode(',', $newStartLine); + $oldCurrentLine = $diff->oldStartLine; + $newCurrentLine = $diff->newStartLine; + if($newFile) + { + $oldCurrentLine = $diff->newStartLine; + $newCurrentLine = $diff->oldStartLine; + } + $newLines = array(); + for($i++; $i < $num; $i ++) + { + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@/A', $lines[$i])) + { + $i --; + break; + } + if(strpos($lines[$i], "diff --git ") === 0) break; + + $line = $lines[$i]; + if(strpos($line, '\ No newline at end of file') === 0)continue; + $sign = empty($line) ? '' : $line{0}; + if($sign == '-' and $newFile) $sign = '+'; + $type = $sign != '-' ? $sign == '+' ? 'new' : 'all' : 'old'; + if($sign == '-' || $sign == '+') + { + $line = substr_replace($line, ' ', 1, 0); + if($newFile) $line = preg_replace('/^\-/', '+', $line); + } + + $newLine = new stdclass(); + $newLine->type = $type; + $newLine->oldlc = $type != 'new' ? $oldCurrentLine : ''; + $newLine->newlc = $type != 'old' ? $newCurrentLine : ''; + $newLine->line = htmlspecialchars($line); + + if($type != 'new') $oldCurrentLine++; + if($type != 'old') $newCurrentLine++; + + $newLines[] = $newLine; + } + + $diff->lines = $newLines; + $diffFile->contents[] = $diff; + } + + if(isset($lines[$i]) and strpos($lines[$i], "diff --git ") === 0) + { + $i --; + $newFile = false; + break; + } + } + $diffs[] = $diffFile; + } + } + return $diffs; + } + + public function getCommitCount($commits = 0, $lastVersion = '') + { + chdir($this->root); + $revision = $this->branch ? $this->branch : 'HEAD'; + return execCmd(escapeCmd("$this->client rev-list --count $revision -- ./"), 'string'); + } + + public function getFirstRevision() + { + chdir($this->root); + $list = execCmd(escapeCmd("$this->client rev-list --reverse HEAD -- ./"), 'array'); + return $list[0]; + } + + public function getLatestRevision() + { + chdir($this->root); + $revision = $this->branch ? $this->branch : 'HEAD'; + $list = execCmd(escapeCmd("$this->client rev-list -1 $revision -- ./"), 'array'); + return $list[0]; + } + + public function getCommits($version = '', $count = 0, $branch = '') + { + if($version == 'HEAD' and $branch) $version = $branch; + $revision = empty($version) ? $revision : $version; + $revision = is_numeric($revision) ? "--skip=$revision $branch" : $revision; + $count = $count == 0 ? '' : "-n $count"; + + chdir($this->root); + $list = execCmd(escapeCmd("$this->client log $count $revision -- ./"), 'array'); + $commits = $this->parseLog($list); + + $logs = array(); + foreach($commits as $commit) + { + $hash = $commit->revision; + $log = new stdClass(); + $log->committer = $commit->committer; + $log->revision = $commit->revision; + $log->comment = $commit->comment; + $log->time = $commit->time; + $logs['commits'][$hash] = $log; + $logs['files'][$hash] = array(); + } + if(empty($logs)) return $logs; + + $hash = ''; + $files = execCmd(escapeCmd("$this->client whatchanged $count $revision --pretty=format:%an@_@%cd@_@%H@_@%s -- ./"), 'array'); + foreach($files as $commit) + { + $commit = trim($commit); + if(empty($commit)) continue; + $parsedCommit = explode('@_@', $commit); + if(count($parsedCommit) == 4) + { + list($account, $date, $hash, $comment) = $parsedCommit; + } + else + { + $file = explode(' ', $commit); + $file = end($file); + list($action, $path) = explode("\t", $file); + $parsedFile = new stdclass(); + $parsedFile->revision = $hash; + $parsedFile->path = '/' . trim($path); + $parsedFile->type = 'file'; + $parsedFile->action = $action; + $logs['files'][$hash][] = $parsedFile; + } + } + return $logs; + } + + public function parseLog($logs) + { + $parsedLogs = array(); + $i = 0; + foreach($logs as $line) + { + if(strpos($line, 'commit ') === 0) + { + if(isset($log)) + { + $log->comment = trim($comment); + $log->change = $changes; + $parsedLogs[$i] = $log; + $i++; + } + + $log = new stdclass(); + $comment = ''; + $changes = array(); + + $log->revision = trim(preg_replace('/^commit/', '', $line)); + } + elseif(strpos($line, 'Author:') === 0) + { + $account = preg_replace('/^Author:/', '', $line); + $log->committer = trim(preg_replace('/<[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+>/', '', $account)); + } + elseif(strpos($line, 'Date:') === 0) + { + $date = trim(preg_replace('/^Date:/', '', $line)); + $log->time = date('Y-m-d H:i:s', strtotime($date)); + } + elseif(preg_match('/^\s{2,}/', $line)) + { + $comment .= $line; + } + elseif(strpos($line, "\t") !== false) + { + list($action, $entry) = explode("\t", $line); + $entry = '/' . trim($entry); + $pathInfo = array(); + $pathInfo['action'] = $action; + $pathInfo['kind'] = 'file'; + $changes[$entry] = $pathInfo; + } + } + + if(isset($log)) + { + $log->comment = trim($comment); + $log->change = $changes; + $parsedLogs[$i] = $log; + } + + return $parsedLogs; + } +} diff --git a/lib/scm/scm.class.php b/lib/scm/scm.class.php new file mode 100644 index 0000000000..c2bdb6c48a --- /dev/null +++ b/lib/scm/scm.class.php @@ -0,0 +1,107 @@ +SCM; + if(!class_exists($className)) require(strtolower($className) . '.class.php'); + $this->engine = new $className($repo->client, $repo->path, $repo->account, $repo->password, $repo->encoding); + } + + public function ls($path, $revision = 'HEAD') + { + return $this->engine->ls($path, $revision); + } + + public function branch() + { + return $this->engine->branch(); + } + + public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0) + { + return $this->engine->log($path, $fromRevision, $toRevision); + } + + public function blame($path, $revision) + { + return $this->engine->blame($path, $revision); + } + + public function getLastLog($path, $count = 10) + { + return $this->engine->getLastLog($path, $count); + } + + public function diff($path, $fromRevision = 0, $toRevision = 'HEAD', $parse = 'yes') + { + $diffs = $this->engine->diff($path, $fromRevision, $toRevision); + + if($parse != 'yes') return implode("\n", $diffs); + return $this->engine->parseDiff($diffs); + } + + public function cat($entry, $revision = 'HEAD') + { + return $this->engine->cat($entry, $revision); + } + + public function info($entry, $revision = 'HEAD') + { + return $this->engine->info($entry, $revision); + } + + public function getCommitCount($commits = 0, $lastVersion = 0) + { + return $this->engine->getCommitCount($commits, $lastVersion); + } + + public function getLatestRevision() + { + return $this->engine->getLatestRevision(); + } + + public function getFirstRevision() + { + return $this->engine->getFirstRevision(); + } + + public function getCommits($version = '', $count = 0, $branch = '') + { + return $this->engine->getCommits($version, $count, $branch); + } +} + +function escapeCmd($cmd) +{ + $codes = array('#', '&', ';', '`', '|', '*', '?', '~', '<', '>', '^', '[', ']', '{', '}', '$', ',', '\x0A', '\xFF'); + if(DIRECTORY_SEPARATOR == '/') $codes[] = '\\'; + foreach($codes as $code) $cmd = str_replace($code, '\\' . $code, $cmd); + return $cmd; +} + +function execCmd($cmd, $return = 'string', &$result = 0, $type = 'utf-8') +{ + if(file_exists(dirname(__FILE__) . '/config.php')) include dirname(__FILE__) . '/config.php'; + if($type != 'utf-8') $cmd = iconv('utf-8', $type . '//TRANSLIT', $cmd); + + $debug = (isset($config->debug) and $config->debug); + if($debug and strpos($cmd, '2>&1') === false) $cmd = $cmd . ' 2>&1'; + + ob_start(); + passthru($cmd, $result); + $output = ob_get_clean(); + if($debug and $result) + { + a('The command is ' . $cmd); + a('The result is ' . $result); + a($output); + } + + /* When output is empty and with chinese then try execute again in windows. */ + if(strtolower(substr(PHP_OS, 0, 3)) == 'win' and empty($output) and $type == 'utf-8' and preg_match("/[\x7f-\xff]/", $cmd)) $output = execCmd($cmd, 'string', $result, 'gbk'); + if($return == 'array') return explode("\n", trim($output)); + return $output; +} diff --git a/lib/scm/scmTest.php b/lib/scm/scmTest.php new file mode 100644 index 0000000000..e88efa027c --- /dev/null +++ b/lib/scm/scmTest.php @@ -0,0 +1,15 @@ +SCM = 'Subversion'; + $repo->client = '/usr/bin/svn'; + $repo->account = 'aaa'; + $repo->password = 'aaaaaa'; + + $scm = new scm($repo); + print_r($scm->cat("http://svn.aaa.5upm.cn/bb/aaa")); +} + +subversionTest(); diff --git a/lib/scm/subversion.class.php b/lib/scm/subversion.class.php new file mode 100644 index 0000000000..943023e50d --- /dev/null +++ b/lib/scm/subversion.class.php @@ -0,0 +1,464 @@ +root = rtrim($root, DIRECTORY_SEPARATOR); + $this->account = $account; + $this->password = $password; + $this->encoding = $encoding; + $this->ssh = (stripos($this->root, 'svn') === 0 or stripos($this->root, 'https') === 0) ? true : false; + $this->remote = !(stripos($this->root, 'file') === 0); + $this->client = $this->remote ? $client . " --username @account@ --password @password@" : $client; + if($this->encoding == 'utf-8') $this->encoding = 'gbk'; + } + + public function ls($path, $revision = 'HEAD') + { + $resourcePath = $path; + $path = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($path)) . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'ls', "-r $revision --xml"))); + $list = execCmd($cmd, 'string', $result); + if($result) + { + $path = '"' . $this->root . '/' . $resourcePath . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'ls', "-r $revision --xml"))); + $list = execCmd($cmd, 'string', $result); + if($result) $list = ''; + } + $listObject = simplexml_load_string($list); + if(!empty($list) and empty($listObject)) + { + $list = helper::convertEncoding($list, $this->encoding, 'utf-8'); + $listObject = simplexml_load_string($list); + } + if(!empty($listObject->list->entry)) $listObject = $listObject->list->entry; + $infos = array(); + if(empty($listObject)) return $infos; + + foreach($listObject as $list) + { + $info = new stdclass(); + $info->name = (string)$list->name; + $info->kind = (string)$list['kind']; + $info->revision = (int)$list->commit['revision']; + $info->account = (string)$list->commit->author; + $info->date = date('Y-m-d H:i:s', strtotime($list->commit->date)); + $info->size = $info->kind == 'file' ? (int)$list->size > 1024 ? round((int)$list->size / 1024, 2) . "KB" : (int)$list->size . 'Bytes' : 0; + $info->comment = ''; + $infos[] = $info; + } + + /* Sort by kind */ + foreach($infos as $key => $info) $kind[$key] = $info->kind; + if($infos) array_multisort($kind, SORT_ASC, $infos); + + return $infos; + } + + public function branch() + { + return array(); + } + + public function getLastLog($path, $count = 10) + { + $resourcePath = $path; + $path = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($path)) . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'log', "--limit $count --xml"))); + $comments = execCmd($cmd, 'string', $result); + if($result) + { + $path = '"' . $this->root . '/' . $resourcePath . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'log', "--limit $count --xml"))); + $comments = execCmd($cmd, 'string', $result); + if($result) $comments = ''; + } + + $parsedComments = simplexml_load_string($comments); + if(!empty($comments) and empty($parsedComments)) + { + $comments = helper::convertEncoding($comments, $this->encoding, 'utf-8'); + $parsedComments = simplexml_load_string($comments); + } + $logs = array(); + foreach($parsedComments->logentry as $entry) + { + $log = new stdclass(); + $log->committer = (string)$entry->author; + $log->revision = (int)$entry['revision']; + $log->comment = trim((string)$entry->msg); + $log->time = date('Y-m-d H:i:s', strtotime($entry->date)); + $log->change = array(); + $logs[] = $log; + unset($log); + } + + /* Sort by kind */ + foreach($logs as $key => $log) $revision[$key] = $log->revision; + if($logs) array_multisort($revision, SORT_DESC, $logs); + + return $logs; + } + + public function log($path, $fromRevision = 0, $toRevision = 'HEAD', $count = 0, $quiet = false) + { + $resourcePath = $path; + $count = $count == 0 ? '' : "--limit $count"; + $param = $quiet ? '-q' : '-v'; + $path = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($path)) . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'log', "$count $param -r $fromRevision:$toRevision --xml"))); + $comments = execCmd($cmd, 'string', $result); + if($result) + { + $path = '"' . $this->root . '/' . $resourcePath . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'log', "$count $param -r $fromRevision:$toRevision --xml"))); + $comments = execCmd($cmd, 'string', $result); + if($result) $comments = ''; + } + + $parsedComments = simplexml_load_string($comments); + if(!empty($comments) and empty($parsedComments)) + { + $comments = helper::convertEncoding($comments, $this->encoding, 'utf-8'); + $parsedComments = simplexml_load_string($comments); + } + $logs = array(); + $revision = array(); + if(empty($parsedComments->logentry)) return $logs; + + foreach($parsedComments->logentry as $entry) + { + $log = new stdclass(); + $log->committer = (string)$entry->author; + $log->revision = (int)$entry['revision']; + $log->comment = trim((string)$entry->msg); + $log->time = date('Y-m-d H:i:s', strtotime($entry->date)); + $log->change = array(); + if(!empty($entry->paths)) + { + foreach($entry->paths->path as $path) + { + $pathInfo = array(); + foreach($path->attributes() as $attr => $value) $pathInfo[$attr] = (string)$value; + $log->change[(string)$path] = $pathInfo; + } + } + if(in_array($log->revision, $revision)) continue; + + $logs[] = $log; + $revision[] = $log->revision; + unset($log); + } + + /* Sort by kind */ + if($logs) array_multisort($revision, SORT_DESC, $logs); + return $logs; + } + + public function blame($path, $revision) + { + $resourcePath = $path; + $path = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($path)) . '"'; + $file = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'cat', "-r $revision"))); + $blame = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'blame', "-r $revision --xml"))); + $output = execCmd($blame, 'string', $result); + if($result) + { + $path = '"' . $this->root . '/' . $resourcePath . '"'; + $file = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'cat', "-r $revision"))); + $blame = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'blame', "-r $revision --xml"))); + $output = execCmd($blame, 'string', $result); + + if($result) return array(); + } + + $content = execCmd($file, 'array'); + + $parsedResult = simplexml_load_string($output); + if(!empty($output) and empty($parsedResult)) + { + $output = helper::convertEncoding($output, $this->encoding, 'utf-8'); + $parsedResult = simplexml_load_string($output); + } + + $blames = array(); + $revLine = 0; + $revision = ''; + if($parsedResult->target->entry) + { + foreach($parsedResult->target->entry as $line) + { + if($line->commit['revision'] != $revision) + { + $blame = array(); + $blame['revision'] = (int)$line->commit['revision']; + $blame['committer'] = (string)$line->commit->author; + $blame['time'] = substr($line->commit->date, 0, 10); + $blame['line'] = (int)$line['line-number']; + $blame['lines'] = 1; + $blame['content'] = $content[$blame['line'] - 1]; + $revision = $blame['revision']; + $revLine = $blame['line']; + $blames[$revLine] = $blame; + } + else + { + $blame = array(); + $blame['line'] = (int)$line['line-number']; + $blame['content'] = $content[$blame['line'] - 1]; + + $blames[$blame['line']] = $blame; + $blames[$revLine]['lines'] ++; + } + } + } + return $blames; + } + + public function diff($path, $fromRevision, $toRevision) + { + $resourcePath = $path; + if($fromRevision == '^') $fromRevision = $toRevision - 1; + $path = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($path)) . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'diff', "-r $fromRevision:$toRevision"))); + $lines = execCmd($cmd, 'array', $result); + if($result) + { + $path = '"' . $this->root . '/' . $resourcePath . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($path, 'diff', "-r $fromRevision:$toRevision"))); + $lines = execCmd($cmd, 'array', $result); + } + return $lines; + } + + public function cat($entry, $revision = 'HEAD') + { + $resourcePath = $entry; + $entry = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($entry)) . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'cat', "-r $revision"))); + $content = execCmd($cmd, 'string', $result); + if($result) + { + $entry = '"' . $this->root . '/' . $resourcePath . '"'; + $cmd = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'cat', "-r $revision"))); + $content = execCmd($cmd, 'string', $result); + } + return $content; + } + + public function info($entry, $revision = 'HEAD') + { + $resourcePath = $entry; + $entry = '"' . $this->root . '/' . str_replace('%2F', '/', urlencode($entry)) . '"'; + $svnInfo = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'info', "-r $revision --xml"))); + $svninfo = execCmd($svnInfo, 'string', $result); + if($result) + { + $entry = '"' . $this->root . '/' . $resourcePath . '"'; + $svnInfo = $this->replaceAuth(escapeCmd($this->buildCMD($entry, 'info', "-r $revision --xml"))); + $svninfo = execCmd($svnInfo, 'string', $result); + if($result) $svninfo = ''; + } + + $parsedSvnInfo = simplexml_load_string($svninfo); + if(!empty($svninfo) and empty($parsedSvnInfo)) + { + $svninfo = helper::convertEncoding($svninfo, $this->encoding, 'utf-8'); + $parsedSvnInfo = simplexml_load_string($svninfo); + } + $info = new stdclass(); + $info->kind = empty($parsedSvnInfo->entry['kind']) ? '' : (string)$parsedSvnInfo->entry['kind']; + $info->path = empty($parsedSvnInfo->entry['path']) ? '' : (string)$parsedSvnInfo->entry['path']; + $info->revision = empty($parsedSvnInfo->entry['revision']) ? '' : (int)$parsedSvnInfo->entry['revision']; + $info->cRevision = empty($parsedSvnInfo->entry->commit['revision']) ? '' : (int)$parsedSvnInfo->entry->commit['revision']; + $info->root = empty($parsedSvnInfo->entry->repository->root) ? '' : (string)$parsedSvnInfo->entry->repository->root; + return $info; + } + + public function parseDiff($lines) + { + if(empty($lines)) return array(); + $diffs = array(); + $num = count($lines); + $endLine = end($lines); + if(strpos($endLine, '\ No newline at end of file') === 0) $num -= 1; + + for($i = 0; $i < $num; $i ++) + { + $diffFile = new stdclass(); + if(strpos($lines[$i], "Index: ") === 0) + { + $fileName = str_replace('Index: ', '', $lines[$i]); + $diffFile->fileName = $fileName; + for($i++; $i < $num; $i ++) + { + $diff = new stdclass(); + if(strpos($lines[$i], '+++', 0) !== false) continue; + if(strpos($lines[$i], '---', 0) !== false) continue; + if(strpos($lines[$i], '======', 0) !== false) continue; + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@\\s*($)/A', $lines[$i])) + { + $startLines = trim(str_replace(array('@', '+', '-'), '', $lines[$i])); + list($oldStartLine, $newStartLine) = explode(' ', $startLines); + list($diff->oldStartLine) = explode(',', $oldStartLine); + list($diff->newStartLine) = explode(',', $newStartLine); + $oldCurrentLine = $diff->oldStartLine; + $newCurrentLine = $diff->newStartLine; + $newLines = array(); + for($i++; $i < $num; $i ++) + { + if(preg_match('/^@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@\\s*($)/A', $lines[$i])) + { + $i --; + break; + } + if(strpos($lines[$i], "Index: ") === 0) break; + + $line = $lines[$i]; + if(strpos($line, '\ No newline at end of file') === 0)continue; + $sign = empty($line) ? '' : $line{0}; + $type = $sign != '-' ? $sign == '+' ? 'new' : 'all' : 'old'; + if($sign == '-' || $sign == '+') $line = substr_replace($line, ' ', 1, 0); + + $newLine = new stdclass(); + $newLine->type = $type; + $newLine->oldlc = $type != 'new' ? $oldCurrentLine : ''; + $newLine->newlc = $type != 'old' ? $newCurrentLine : ''; + $newLine->line = $line; + + if($type != 'new') $oldCurrentLine++; + if($type != 'old') $newCurrentLine++; + + $newLines[] = $newLine; + } + + $diff->lines = $newLines; + $diffFile->contents[] = $diff; + } + + if(isset($lines[$i]) and strpos($lines[$i], "Index: ") === 0) + { + $i --; + break; + } + } + $diffs[] = $diffFile; + } + } + return $diffs; + } + + public function getCommitCount($commits = 0, $lastVersion = 0) + { + if(empty($commits)) $commits = 0; + if(empty($lastVersion)) $lastVersion = 0; + $lastRevision = $this->getLatestRevision(); + + $count = 10000; + $from = $lastVersion; + while(true) + { + $logs = $this->log('', $from, $lastRevision, empty($from) ? $count : $count + 1, $quiet = true); + if(empty($logs)) break; + + $num = empty($from) ? count($logs) : count($logs) - 1; + $commits += $num; + + $from = reset($logs); + $from = $from->revision; + if($from == $lastRevision) break; + } + return $commits; + } + + public function getFirstRevision() + { + $logs = $this->log('', 0, 'HEAD', 1, $quiet = true); + if(empty($logs)) return 0; + $firstLog = end($logs); + return $firstLog->revision; + } + + public function getLatestRevision() + { + $info = $this->info(''); + return $info->cRevision; + } + + public function getCommits($version = '', $count = 0) + { + $count = $count == 0 ? '' : "--limit $count"; + $path = '"' . $this->root . '"'; + if(stripos($this->root, 'https') === 0 or stripos($this->root, 'svn') === 0) + { + $comments = str_replace("\\", "/", "$this->client log $count -v -r $version:0 --non-interactive --trust-server-cert-failures=cn-mismatch --trust-server-cert --no-auth-cache --xml $path"); + } + else + { + $comments = str_replace("\\", "/", "$this->client log $count -v -r $version:0 --no-auth-cache --xml $path"); + } + $comments = $this->replaceAuth(escapeCmd($comments)); + $comments = execCmd($comments, 'string', $result); + if($result) $comments = ''; + + $parsedComments = simplexml_load_string($comments); + if(!empty($comments) and empty($parsedComments)) + { + $comments = helper::convertEncoding($comments, $this->encoding, 'utf-8'); + $parsedComments = simplexml_load_string($comments); + } + $logs = array(); + foreach($parsedComments->logentry as $entry) + { + $parsedLog = new stdClass(); + $parsedLog->committer = (string)$entry->author; + $parsedLog->revision = (int)$entry['revision']; + $parsedLog->comment = trim((string)$entry->msg); + $parsedLog->time = date('Y-m-d H:i:s', strtotime($entry->date)); + $logs['commits'][$parsedLog->revision] = $parsedLog; + $logs['files'][$parsedLog->revision] = array(); + if(!empty($entry->paths)) + { + foreach($entry->paths->path as $file) + { + $parsedFile = new stdclass(); + $parsedFile->revision = $parsedLog->revision; + $parsedFile->path = (string)$file; + $parsedFile->type = (string)$file['kind']; + $parsedFile->action = (string)$file['action']; + $logs['files'][$parsedLog->revision][] = $parsedFile; + } + } + } + return $logs; + } + + public function replaceAuth($cmd) + { + return str_replace(array('@account@', '@password@'), array($this->account, $this->password), $cmd); + } + + public function buildCMD($path, $action, $param) + { + if($this->ssh) + { + $cmd = str_replace("\\", "/", "$this->client $action $param --non-interactive --trust-server-cert-failures=cn-mismatch --trust-server-cert --no-auth-cache $path"); + } + else + { + $cmd = str_replace("\\", "/", "$this->client $action $param --no-auth-cache $path"); + } + + return $cmd; + } +} diff --git a/module/admin/model.php b/module/admin/model.php index e097e06407..12bfb1090d 100644 --- a/module/admin/model.php +++ b/module/admin/model.php @@ -239,7 +239,7 @@ class adminModel extends model /* Check weak password when login. */ if($this->app->moduleName == 'user' and $this->app->methodName == 'login') { - if(!isset($_POST['passwordStrength']) return true; + if(!isset($_POST['passwordStrength'])) return true; if(isset($this->config->safe->mode) and $this->post->passwordStrength < $this->config->safe->mode) return true; } diff --git a/module/common/lang/menuOrder.php b/module/common/lang/menuOrder.php index 9e737216ae..561bf1e0e5 100644 --- a/module/common/lang/menuOrder.php +++ b/module/common/lang/menuOrder.php @@ -4,10 +4,11 @@ $lang->menuOrder[5] = 'my'; $lang->menuOrder[10] = 'product'; $lang->menuOrder[15] = 'project'; $lang->menuOrder[20] = 'qa'; -$lang->menuOrder[25] = 'doc'; -$lang->menuOrder[30] = 'report'; -$lang->menuOrder[35] = 'company'; -$lang->menuOrder[40] = 'admin'; +$lang->menuOrder[25] = 'repo'; +$lang->menuOrder[30] = 'doc'; +$lang->menuOrder[35] = 'report'; +$lang->menuOrder[40] = 'company'; +$lang->menuOrder[45] = 'admin'; /* index menu order. */ $lang->index->menuOrder[5] = 'product'; @@ -76,6 +77,10 @@ $lang->testsuite->menuOrder = $lang->testcase->menuOrder; $lang->caselib->menuOrder = $lang->testcase->menuOrder; $lang->testreport->menuOrder = $lang->testcase->menuOrder; +$lang->repo->menuOrder[5] = 'log'; +$lang->repo->menuOrder[15] = 'settings'; +$lang->repo->menuOrder[20] = 'delete'; + /* doc menu order. */ $lang->doc->menuOrder[5] = 'list'; $lang->doc->menuOrder[10] = 'product'; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index ad7731d052..961bbcf146 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -123,6 +123,7 @@ $lang->menu->my = ' 我的地盘|my|index'; $lang->menu->product = $lang->productCommon . '|product|index|locate=no'; $lang->menu->project = $lang->projectCommon . '|project|index|locate=no'; $lang->menu->qa = '测试|qa|index'; +$lang->menu->repo = '代码|repo|log'; $lang->menu->doc = '文档|doc|index'; $lang->menu->report = '统计|report|index'; $lang->menu->company = '组织|company|index'; @@ -331,6 +332,12 @@ $lang->caselib->menu->testsuite = array('link' => '套件|testsuite|browse|'); $lang->caselib->menu->report = array('link' => '报告|testreport|browse|'); $lang->caselib->menu->caselib = array('link' => '用例库|caselib|browse', 'alias' => 'create,createcase,view,edit,batchcreatecase,showimport', 'subModule' => 'tree,testcase'); +$lang->repo = new stdclass(); +$lang->repo->menu = new stdclass(); +$lang->repo->menu->log = array('link' =>'浏览|repo|log|repoID=%s&entry=', 'alias' => 'diff,view,revision,showsynccomment'); +$lang->repo->menu->settings = '设置|repo|settings|repoID=%s'; +$lang->repo->menu->delete = array('link' => '删除|repo|delete|repoID=%s', 'target' => 'hiddenwin'); + /* 文档视图菜单设置。*/ $lang->doc = new stdclass(); $lang->doc->menu = new stdclass(); diff --git a/module/repo/config.php b/module/repo/config.php new file mode 100644 index 0000000000..b5e1de4207 --- /dev/null +++ b/module/repo/config.php @@ -0,0 +1,32 @@ +program = new stdclass(); +$config->program->suffix['c'] = "cpp"; +$config->program->suffix['cpp'] = "cpp"; +$config->program->suffix['asp'] = "asp"; +$config->program->suffix['php'] = "php"; +$config->program->suffix['cs'] = "cs"; +$config->program->suffix['sh'] = "bash"; +$config->program->suffix['jsp'] = "java"; +$config->program->suffix['lua'] = "lua"; +$config->program->suffix['sql'] = "sql"; +$config->program->suffix['js'] = "javascript"; +$config->program->suffix['ini'] = "ini"; +$config->program->suffix['conf'] = "apache"; +$config->program->suffix['bat'] = "dos"; +$config->program->suffix['py'] = "python"; +$config->program->suffix['rb'] = "ruby"; +$config->program->suffix['as'] = "actionscript"; +$config->program->suffix['html'] = "xml"; +$config->program->suffix['xml'] = "xml"; +$config->program->suffix['htm'] = "xml"; +$config->program->suffix['pl'] = "perl"; + +$config->repo->cacheTime = 10; +$config->repo->syncTime = 10; +$config->repo->batchNum = 100; +$config->repo->images = '|png|gif|jpg|ico|jpeg|bmp|'; +$config->repo->binary = '|pdf|'; + +$config->repo->editor = new stdclass(); +$config->repo->editor->view = array('id' => 'commentText', 'tools' => 'simpleTools'); +$config->repo->editor->diff = array('id' => 'commentText', 'tools' => 'simpleTools'); diff --git a/module/repo/control.php b/module/repo/control.php new file mode 100644 index 0000000000..cf6fa92749 --- /dev/null +++ b/module/repo/control.php @@ -0,0 +1,642 @@ +lang->repo->error->useless); + die(js::locate('back')); + } + + $this->scm = $this->app->loadClass('scm'); + $this->repos = $this->repo->getRepoPairs(); + if(common::hasPriv('repo', 'create')) $this->lang->modulePageActions = html::a(helper::createLink('repo', 'create'), " " . $this->lang->repo->create, '', "class='btn'"); + if(empty($this->repos) and $this->methodName != 'create') die(js::locate($this->repo->createLink('create'))); + + /* Unlock session for wait to get data of repo. */ + session_write_close(); + } + + /** + * Create repo. + * + * @access public + * @return void + */ + public function create() + { + $this->repo->setMenu($this->repos); + if(!empty($_POST)) + { + $repoID = $this->repo->create(); + if(dao::isError()) die(js::error(dao::getError())); + die(js::locate($this->repo->createLink('showSyncComment', "repoID=$repoID"), 'parent')); + } + + $this->view->title = $this->lang->repo->create; + $this->view->groups = $this->loadModel('group')->getPairs(); + $this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted'); + $this->display(); + } + + /** + * Set repo. + * + * @param int $repoID + * @access public + * @return void + */ + public function settings($repoID = 0) + { + $this->repo->setMenu($this->repos, $repoID); + if($repoID == 0) $repoID = $this->session->repoID; + if(!empty($_POST)) + { + $needSync = $this->repo->saveSettings($repoID); + if(dao::isError()) die(js::error(dao::getError())); + if(!$needSync) + { + die(js::locate($this->repo->createLink('showSyncComment', "repoID=$repoID"), 'parent')); + } + die(js::locate($this->repo->createLink('log', "repoID=$repoID"), 'parent')); + } + + $this->view->title = $this->lang->repo->settings; + $this->view->repo = $this->repo->getRepoByID($repoID); + $this->view->groups = $this->loadModel('group')->getPairs(); + $this->view->users = $this->loadModel('user')->getPairs('noletter|noempty|nodeleted', !empty($repo->acl->users) ? $repo->acl->users : ''); + $this->display(); + } + + /** + * Delete repo. + * + * @param int $repoID + * @param string $confirm + * @access public + * @return void + */ + public function delete($repoID, $confirm = 'no') + { + if($confirm == 'no') + { + die(js::confirm($this->lang->repo->notice->delete, $this->repo->createLink('delete', "repoID=$repoID&confirm=yes"))); + } + $this->dao->delete()->from(TABLE_REPO)->where('id')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOBRANCH)->where('repo')->eq($repoID)->exec(); + echo js::alert($this->lang->repo->notice->successDelete); + die(js::locate($this->repo->createLink('log'), 'parent')); + } + + /** + * View repo file. + * + * @param int $repoID + * @param string $entry + * @param string $revision + * @param string $showBug + * @param string $encoding + * @access public + * @return void + */ + public function view($repoID, $entry, $revision = 'HEAD', $showBug = 'false', $encoding = '') + { + if($this->get->entry) $entry = $this->get->entry; + $this->repo->setMenu($this->repos, $repoID); + $this->repo->setBackSession('view', $withOtherModule = true); + if($repoID == 0) $repoID = $this->session->repoID; + + $file = $entry; + $repo = $this->repo->getRepoByID($repoID); + $entry = $this->repo->decodePath($entry); + + $this->scm->setEngine($repo); + $info = $this->scm->info($entry, $revision); + if($info->kind == 'dir') $this->locate($this->repo->createLink('log', "repoID=$repoID&entry=&revision=$revision", "entry=" . $this->repo->encodePath($info->path))); + $content = $this->scm->cat($entry, $revision); + $entry = urldecode($entry); + $pathInfo = pathinfo($entry); + $encoding = empty($encoding) ? $repo->encoding : $encoding; + $encoding = strtolower(str_replace('_', '-', $encoding)); + + $suffix = ''; + if(isset($pathInfo["extension"])) $suffix = strtolower($pathInfo["extension"]); + if(!$suffix or (!array_key_exists($suffix, $this->config->program->suffix) and strpos($this->config->repo->images, "|$suffix|") === false)) $suffix = $this->repo->isBinary($content, $suffix) ? 'binary' : 'c'; + + if(strpos($this->config->repo->images, "|$suffix|") !== false) + { + $content = base64_encode($content); + } + elseif($encoding != 'utf-8') + { + $content = helper::convertEncoding($content, $encoding); + } + + $this->app->loadClass('pager', $static = true); + $pager = new pager(0, 8, 1); + + $commiters = $this->loadModel('user')->getCommiters(); + $logType = 'file'; + $revisions = $this->repo->getLogs($repo, '/' . $entry, 'HEAD', $logType, $pager); + + $i = 0; + foreach($revisions as $log) + { + if($revision == 'HEAD' and $i == 0) $revision = $log->revision; + if($revision == $log->revision) $revisionName = $repo->SCM == 'Git' ? $this->repo->getGitRevisionName($log->revision, $log->commit) : $log->revision; + $log->committer = zget($commiters, $log->committer, $log->committer); + $i++; + } + if(!isset($revisionName)) + { + if($repo->SCM == 'Git') $gitCommit = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('revision')->eq($revision)->andWhere('repo')->eq($repo->id)->fetch('commit'); + $revisionName = ($repo->SCM == 'Git' and isset($gitCommit)) ? $this->repo->getGitRevisionName($revision, $gitCommit) : $revision; + } + + $this->view->revisions = $revisions; + $this->view->title = $this->lang->repo->common; + $this->view->type = 'view'; + $this->view->showBug = $showBug; + $this->view->encoding = str_replace('-', '_', $encoding); + $this->view->repoID = $repoID; + $this->view->repo = $repo; + $this->view->revision = $revision; + $this->view->revisionName = $revisionName; + $this->view->preAndNext = $this->repo->getPreAndNext($repo, '/' . $entry, $revision); + $this->view->file = $file; + $this->view->entry = $entry; + $this->view->path = $entry; + $this->view->suffix = $suffix; + $this->view->content = $content; + $this->view->pager = $pager; + $this->view->logType = $logType; + $this->view->info = $info; + + $this->display(); + } + + /** + * Browse repo. + * + * @param int $repoID + * @param string $path + * @param string $revision + * @param int $refresh + * @access public + * @return void + */ + public function browse($repoID = 0, $path = '', $revision = 'HEAD', $refresh = 0) + { + if($this->get->path) $path = $this->get->path; + $this->locate($this->repo->createLink('log', "repoID=$repoID&entry=&revision=$revision", "entry=$path")); + } + + /** + * show repo log. + * + * @param int $repoID + * @param string $entry + * @param string $revision + * @param string $type + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function log($repoID = 0, $entry = '', $revision = 'HEAD', $type = 'dir', $recTotal = 0, $recPerPage = 50, $pageID = 1) + { + if($this->get->entry) $entry = $this->get->entry; + $this->repo->setMenu($this->repos, $repoID); + $this->repo->setBackSession('list', $withOtherModule = true); + if($repoID == 0) $repoID = $this->session->repoID; + + $repo = $this->repo->getRepoByID($repoID); + $file = $entry; + $entry = $this->repo->decodePath($entry); + + $this->app->loadClass('pager', $static = true); + $pager = new pager($recTotal, $recPerPage, $pageID); + + $this->scm->setEngine($repo); + $info = $this->scm->info($entry, $revision); + + $logs = $this->repo->getLogs($repo, $entry, $revision, $type, $pager); + $commiters = $this->loadModel('user')->getCommiters(); + foreach($logs as $log) $log->committer = zget($commiters, $log->committer, $log->committer); + + $this->view->repo = $repo; + $this->view->title = $this->lang->repo->common; + $this->view->logs = $logs; + $this->view->revision = $revision; + $this->view->repoID = $repoID; + $this->view->entry = urldecode($entry); + $this->view->path = urldecode($entry); + $this->view->file = urldecode($file); + $this->view->pager = $pager; + $this->view->info = $info; + $this->display(); + } + + /** + * Show repo revision. + * + * @param int $repoID + * @param int $revision + * @param string $path + * @param string $type + * + * @access public + * @return void + */ + public function revision($repoID, $revision, $root = '', $type = 'dir') + { + $this->repo->setMenu($this->repos, $repoID); + $this->repo->setBackSession(); + if($repoID == 0) $repoID = $this->session->repoID; + $repo = $this->repo->getRepoByID($repoID); + + $this->scm->setEngine($repo); + $log = $this->scm->log('', $revision, $revision); + + $history = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('revision')->eq($log[0]->revision)->andWhere('repo')->eq($repoID)->fetch(); + if($history) + { + $oldRevision = $this->dao->select('revision')->from(TABLE_REPOHISTORY)->where('commit')->eq($history->commit - 1)->andWhere('repo')->eq($repoID)->fetch('revision'); + $log[0]->commit = $history->commit; + } + if(empty($oldRevision)) $oldRevision = '^'; + + $changes = array(); + $viewPriv = common::hasPriv('repo', 'view'); + $diffPriv = common::hasPriv('repo', 'diff'); + foreach($log[0]->change as $path => $change) + { + if($repo->prefix) $path = str_replace($repo->prefix, '', $path); + $encodePath = $this->repo->encodePath($path); + if($change['kind'] == '' or $change['kind'] == 'file') + { + $change['view'] = $viewPriv ? html::a($this->repo->createLink('view', "repoID=$repoID&entry=&revision=$revision", "entry=$encodePath"), $this->lang->repo->viewA) : ''; + if($change['action'] == 'M') $change['diff'] = $diffPriv ? html::a($this->repo->createLink('diff', "repoID=$repoID&entry=&oldRevision=$oldRevision&newRevision=$revision", "entry=$encodePath"), $this->lang->repo->diffAB) : ''; + } + else + { + $change['view'] = $viewPriv ? html::a($this->repo->createLink('log', "repoID=$repoID&entry=&revision=$revision", "entry=$encodePath"), $this->lang->repo->log) : ''; + if($change['action'] == 'M') $change['diff'] = $diffPriv ? html::a($this->repo->createLink('diff', "repoID=$repoID&entry=&oldRevision=$oldRevision&newRevision=$revision", "entry=$encodePath"), $this->lang->repo->diffAB) : ''; + } + $changes[$path] = $change; + } + + $root = $this->repo->decodePath($root); + $parent = ''; + if($type == 'file') + { + $parent = $this->dao->select('parent')->from(TABLE_REPOFILES) + ->where('revision')->eq($history->id) + ->andWhere('path')->eq('/' . $root) + ->fetch('parent'); + } + + $this->view->title = $this->lang->repo->common; + $this->view->log = $log[0]; + $this->view->repo = $repo; + $this->view->path = $root; + $this->view->type = $type; + $this->view->changes = $changes; + $this->view->repoID = $repoID; + $this->view->revision = $log[0]->revision; + $this->view->parentDir = $parent; + $this->view->oldRevision = $oldRevision; + $this->view->preAndNext = $this->repo->getPreAndNext($repo, $root, $revision, $type, 'revision'); + + $this->display(); + } + + /** + * Show diff. + * + * @param int $repoID + * @param string $entry + * @param string $oldRevision + * @param string $newRevision + * @param string $showBug + * @param string $encoding + * @access public + * @return void + */ + public function diff($repoID, $entry = '', $oldRevision = '0', $newRevision = 'HEAD', $showBug = 'false', $encoding = '') + { + if($this->get->entry) $entry = $this->get->entry; + $this->repo->setMenu($this->repos, $repoID); + if($repoID == 0) $repoID = $this->session->repoID; + $file = $entry; + $repo = $this->repo->getRepoByID($repoID); + $entry = $this->repo->decodePath($entry); + + $pathInfo = pathinfo($entry); + $suffix = ''; + if(isset($pathInfo["extension"])) $suffix = strtolower($pathInfo["extension"]); + + $arrange = $this->cookie->arrange ? $this->cookie->arrange : 'inline'; + if(!empty($_POST)) + { + $oldRevision = isset($this->post->revision[1]) ?$this->post->revision[1] : ''; + $newRevision = isset($this->post->revision[0]) ?$this->post->revision[0] : ''; + if($this->post->arrange) + { + $arrange = $this->post->arrange; + setcookie('arrange', $arrange); + } + if($this->post->encoding) $encoding = $this->post->encoding; + if(!$oldRevision) + { + echo js::alert($this->lang->repo->error->diff); + die(js::locate('back')); + } + } + + $this->scm->setEngine($repo); + $encoding = empty($encoding) ? $repo->encoding : $encoding; + $encoding = strtolower(str_replace('_', '-', $encoding)); + $info = $this->scm->info($entry, $newRevision); + $diffs = $this->scm->diff($entry, $oldRevision, $newRevision); + foreach($diffs as $diff) + { + if($encoding != 'utf-8') + { + $diff->fileName = helper::convertEncoding($diff->fileName, $encoding); + if(empty($diff->contents)) continue; + foreach($diff->contents as $content) + { + if(empty($content->lines)) continue; + foreach($content->lines as $lines) + { + if(empty($lines->line)) continue; + $lines->line = helper::convertEncoding($lines->line, $encoding); + } + } + } + } + + /* When arrange is appose then adjust data for show them easy.*/ + if($arrange == 'appose') + { + foreach($diffs as $diffFile) + { + if(empty($diffFile->contents)) continue; + foreach($diffFile->contents as $content) + { + $old = array(); + $new = array(); + foreach($content->lines as $line) + { + if($line->type != 'new') $old[$line->oldlc] = $line->line; + if($line->type != 'old') $new[$line->newlc] = $line->line; + } + $content->old = $old; + $content->new = $new; + } + } + } + + $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->diff; + $this->view->position[] = $this->lang->repo->diff; + + $this->view->type = 'diff'; + $this->view->showBug = $showBug; + $this->view->entry = urldecode($entry); + $this->view->suffix = $suffix; + $this->view->file = $file; + $this->view->repoID = $repoID; + $this->view->repo = $repo; + $this->view->encoding = str_replace('-', '_', $encoding); + $this->view->arrange = $arrange; + $this->view->diffs = $diffs; + $this->view->newRevision = $newRevision; + $this->view->oldRevision = $oldRevision; + $this->view->revision = $newRevision; + $this->view->historys = $repo->SCM == 'Git' ? $this->dao->select('revision,commit')->from(TABLE_REPOHISTORY)->where('revision')->in("$oldRevision,$newRevision")->andWhere('repo')->eq($repo->id)->fetchPairs() : ''; + $this->view->info = $info; + + $this->display(); + } + + /** + * Download repo file. + * + * @param int $repoID + * @param string $path + * @param string $fromRevision + * @param string $toRevision + * @param string $type + * @access public + * @return void + */ + public function download($repoID, $path, $fromRevision = 'HEAD', $toRevision = '', $type = 'file') + { + if($this->get->path) $path = $this->get->path; + $entry = $this->repo->decodePath($path); + $repo = $this->repo->getRepoByID($repoID); + $this->scm->setEngine($repo); + $content = $type == 'file' ? $this->scm->cat($entry, $fromRevision) : $this->scm->diff($entry, $fromRevision, $toRevision, 'patch'); + $fileName = basename(urldecode($entry)); + if($type != 'file') $fileName .= "r$fromRevision--r$toRevision.patch"; + $extension = ltrim(strrchr($fileName, '.'), '.'); + $this->fetch('file', 'sendDownHeader', array("fileName" => $fileName, "fileType" => $extension, "content" => $content)); + } + + /** + * Show sync comment. + * + * @param int $repoID + * @access public + * @return void + */ + public function showSyncComment($repoID = 0) + { + $this->repo->setMenu($this->repos, $repoID); + if($repoID == 0) $repoID = $this->session->repoID; + + $this->view->title = $this->lang->repo->common . $this->lang->colon . $this->lang->repo->showSyncComment; + $this->view->position[] = $this->lang->repo->showSyncComment; + + $latestInDB = $this->repo->getLatestComment($repoID); + $this->view->version = $latestInDB ? (int)$latestInDB->commit : 1; + $this->view->repoID = $repoID; + $this->display(); + } + + /** + * Ajax sync comment. + * + * @param int $repoID + * @param string $type + * @access public + * @return void + */ + public function ajaxSyncComment($repoID = 0, $type = 'batch') + { + set_time_limit(0); + $repo = $this->repo->getRepoByID($repoID); + if(empty($repo)) die(); + if($repo->synced) die('finish'); + + $this->scm->setEngine($repo); + + $branchID = ''; + if($repo->SCM == 'Git' and empty($branchID)) + { + $branches = $this->scm->branch(); + if($branches) + { + /* Init branchID. */ + if($this->cookie->syncBranch) $branchID = $this->cookie->syncBranch; + if(!isset($branches[$branchID])) $branchID = ''; + if(empty($branchID)) $branchID = reset($branches); + + /* Get unsynced branches. */ + foreach($branches as $branch) + { + unset($branches[$branch]); + if($branch == $branchID) + { + $this->repo->setRepoBranch($branchID); + setcookie("syncBranch", $branchID, 0, $this->config->webRoot); + break; + } + } + } + } + + $latestInDB = $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($repo->SCM == 'Git' and $this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->orderBy('t1.time') + ->limit(1) + ->fetch(); + + $version = empty($latestInDB) ? 1 : $latestInDB->commit + 1; + $logs = array(); + $revision = $version == 1 ? 'HEAD' : ($repo->SCM == 'Git' ? $latestInDB->commit : $latestInDB->revision); + if($type == 'batch') + { + $logs = $this->scm->getCommits($revision, $this->config->repo->batchNum, $branchID); + } + else + { + $logs = $this->scm->getCommits($revision, 0, $branchID); + } + + $commitCount = $this->repo->saveCommit($repoID, $logs, $version, $branchID); + if(empty($commitCount)) + { + if(!$repo->synced) + { + if($repo->SCM == 'Git') + { + if($branchID) $this->repo->saveExistsLogBranch($repo->id, $branchID); + + $branchID = reset($branches); + setcookie("syncBranch", $branchID, 0, $this->config->webRoot); + + if($branchID) $this->repo->fixCommit($repoID); + } + + if(empty($branchID)) + { + $this->repo->markSynced($repoID); + die('finish'); + } + } + } + + $this->dao->update(TABLE_REPO)->set('commits=commits + ' . $commitCount)->where('id')->eq($repoID)->exec(); + echo $type == 'batch' ? $commitCount : 'finish'; + } + + /** + * Ajax show side logs. + * + * @param int $repoID + * @param string $path + * @param string $type + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function ajaxSideLogs($repoID, $path, $type = 'dir', $recTotal = 0, $recPerPage = 8, $pageID = 1) + { + if($this->get->path) $path = $this->get->path; + $this->app->loadClass('pager', $static = true); + $pager = new pager($recTotal, $recPerPage, $pageID); + + $repo = $this->repo->getRepoByID($repoID); + $path = $this->repo->decodePath($path); + $commiters = $this->loadModel('user')->getCommiters(); + $revisions = $this->repo->getLogs($repo, $path, 'HEAD', $type, $pager); + foreach($revisions as $revision) $revision->committer = zget($commiters, $revision->committer, $revision->committer); + + $this->view->repo = $this->repo->getRepoByID($repoID); + $this->view->revisions = $revisions; + $this->view->pager = $pager; + $this->view->repoID = $repoID; + $this->view->logType = $type; + $this->view->path = urldecode($path); + $this->display(); + } + + /** + * Ajax get committer. + * + * @param int $repoID + * @param string $entry + * @param int $revision + * @param int $line + * @access public + * @return void + */ + public function ajaxGetCommitter($repoID, $entry, $revision, $line) + { + if($this->get->entry) $entry = $this->get->entry; + $repo = $this->repo->getRepoByID($repoID); + $entry = $this->repo->decodePath($entry); + + $this->scm->setEngine($repo); + $blames = $this->scm->blame($entry, $revision); + $committer = ''; + while($line > 0) + { + if(isset($blames[$line]['committer'])) + { + $committer = $blames[$line]['committer']; + break; + } + $line--; + } + die($committer); + } +} diff --git a/module/repo/css/common.css b/module/repo/css/common.css new file mode 100644 index 0000000000..1040249fa2 --- /dev/null +++ b/module/repo/css/common.css @@ -0,0 +1,179 @@ +a{color:#169;} +a:hover, a:active{ text-decoration:underline; color:#C61A1A;} +h2,h3 {font-size:20px; padding:0 0 10px; margin: 0; clear: both;} +h3 {font-size:16px;} +.revision{font-size:12px; line-height:20px; text-align:right; padding-right:8px;} +.directory{ background-image:url('theme/default/images/repo/dir.png')} +.file{ background-image:url('theme/default/images/repo/txt.png')} +//.icon{ width:17px; padding-left:10px; padding-right:2px;} +.mini-icon{ display: inline-block; height: 16px; width: 16px; background-color: transparent; background-position: 0 0; background-repeat: no-repeat; vertical-align: text-bottom;} +.action {float:left;} +.input-group select#encrypt{border-left:0px;} +.arrange {float:right;} +.versions {position:relative} +#diffRepo {position:absolute; left:20px;z-index:1000;} +#repoID {display: inline-block; width: auto;} +.repoCode a:hover{text-decoration:none;} +.commentButton +{ + background-repeat: no-repeat; + position: absolute; + left: -28px; + width: 40px; + z-index: 10; + cursor:pointer; + font-size:18px; + color:#4183C4; + display: none; +} +.repoCode tr.over .commentButton {display: block;} +.bug +{ + background-repeat: no-repeat; + position: absolute; + left: -7px; + width: 20px; + z-index: 0; + cursor:pointer; + font-size:18px; + color: #4183C4; + line-height: 18px; +} +.repoCode .icon{opacity:1;} +.icon-comment-add:before {content: '\e74c'; transform: scale(-1, 1); display: inline-block; font-weight: normal;} +.icon-comment-add:after {content: '+'; display: block; font-weight: normal; position: absolute; left: 16px; top: 1px; font-family: Arial; font-weight: bold; font-size: 12px;} +.icon-comments:before {content: '\e750'; transform: scale(-1, 1); display: inline-block; font-weight: normal; font-size: 18px; line-height: 18px;} +.commentButton:hover,.bug:hover{color: #d20b0b;} +.commentBoard{border-top: 1px solid #E4E4E4; border-bottom: 1px solid #E4E4E4; padding:0; white-space:normal; background-color:#eee; padding: 10px;} +.commentBoard .table-form th {background: none} +.lines input{width:40px;} +.commentSubmit, .commentCancel{margin-right:10px;} +.commentFoot .optional{float:left;} +.commentBoard.using .bugContainer {border: 1px solid #ddd; background: #fff} +.commentBoard.using .bugContainer > .commentHeader {background: #f1f1f1; padding: 0 10px; border-bottom: 1px solid #ddd} +.commentHeaderAuthor{max-width: 600px; line-height: 33px; font-weight: bold; color: #222; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;} +.comment{width:100%; word-break:keep-all; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;} +.commentContent{margin:10px;} +.commentHeaderRight{float:right;} + +/* Sider */ +#mainContent > #sidebar > .side-body.affix {top:0px; z-index:10000;} + +/* Pre row */ +.repoCode table > tbody > tr.over td {background: #f8eec7;} +.repoCode table > tbody > tr.over th {background: #cdcdcd; color: #333} + +/* Comment-btn */ +.comment-btn {position: relative; margin: 0; padding: 0; display: none} +.repoCode tr.over .comment-btn {display: block;} +.comment-btn .icon-wrapper {display: block; position: absolute; background: #4183C4; border-radius: 2px; width: 24px; height: 20px; left: -6px; top: 0; line-height: 20px; text-align: center; color: #fff; cursor: pointer; transition: transform 0.2s;} +.comment-btn .icon-wrapper:before {display: block; content: ' '; right: -4px; top: 6px; position: absolute; border-left: 4px solid #4183C4; border-right: 0 solid transparent; border-bottom: 4px solid transparent; border-top: 4px solid transparent; width: 0; height: 0} +.comment-btn .icon-wrapper:hover {background: #169; transform: scale(1.1)} +.comment-btn .icon-wrapper:hover:before {border-left-color: #169} + +.repoCode tr.commented {cursor: pointer;} + +.repoCode tr.commented .comment-btn {display: block;} +.repoCode tr.commented .comment-btn .icon-wrapper {background: none; border: none; width: 24px; line-height: 18px; height: 18px; left: -6px; color: #4183c4} +.repoCode tr.commented .comment-btn .icon-wrapper:hover {border-color: #169; color: #169} +.repoCode tr.commented .comment-btn .icon-wrapper > i:before {content: '\e750'; font-size: 18px; transform: scale(-1, 1); display: inline-block;} +.repoCode tr.commented .comment-btn .icon-wrapper:before {display: none} + +.repoCode tr.over.commented .comment-btn .icon-wrapper, .repoCode tr.selected.commented .comment-btn .icon-wrapper {line-height: 20px; height: 20px; background: #4183C4; color: #fff; left: -6px} +.repoCode tr.over.commented .comment-btn .icon-wrapper > i:before {content: "\e661"; font-size: 14px;} +.repoCode tr.selected.commented .comment-btn .icon-wrapper > i:before {font-size: 14px;} +.repoCode tr.over.commented .comment-btn .icon-wrapper:before, .repoCode tr.selected.commented .comment-btn .icon-wrapper:before {display: block;} + +/* repo action form */ +.repoCode .comment-list, .repoCode .comment-actions {max-width: 900px} +.repoCode .bugFormContainer {border: 1px solid #bbb; margin: 0 0 0 15px; padding: 10px 20px 10px 10px; max-width: 880px; background: #fff} +.repoCode .bugFormContainer th {width: 70px} + +.repoCode .action-row {display: none} +.repoCode .with-action-row .action-row {display: table-row} +.repoCode .action-cell {background: #EEE; white-space: normal; padding: 10px 15px 10px 0;} +.repoCode .with-action-row table tr.selected .comment-btn:last-child {display: block;} +.repoCode .with-action-row table tr.selected td {background: #f8eec7} +.repoCode .with-action-row table tr.selected th {background: #cdcdcd} +.repoCode .with-action-row table tr.selected .comment-btn .icon-wrapper > i:before, .repoCode #diff.with-action-row tr.selected .comment-btn .icon-wrapper > i:before {content: '\d7'} + +.repoCode .comment-row {display: none} +.repoCode .comment-row.show {display: table-row !important;} +.repoCode .comment-cell {background: #eee; white-space: normal;} +.repoCode .comment-cell .panel {margin: 10px; border-color: #bbb} +.repoCode .comment-cell .panel-body {padding: 6px 10px} +.repoCode .comment-cell .panel-actions.pull-right {margin-right: 0; margin-top: 0;} +.repoCode .comment-cell .editing .panel-body, .repoCode .comment-cell .commentContainer.show-form .panel-body {display: none} +.repoCode .comment-cell .bug-edit-form, {padding: 10px; display: none} +.repoCode .comment-cell .editing .bug-edit-form, .repoCode .comment-cell .commentContainer.show-form .comment-edit-form {display: block;} + +.repoCode .comment {border: 1px solid #e5e5e5; background: #fafafa; padding: 5px 10px; margin-bottom: 10px;} +.repoCode .comment .comment-edit-form {margin-top: 10px;} +.repoCode .panel-bug .steps {background: #f1f1f1; padding: 5px 10px} +.repoCode .panel-bug .bug-edit-form {margin-bottom: 10px;} +.repoCode .panel-bug .panel-body {display: none} +.repoCode .panel-bug .panel-heading {cursor: pointer;} +.repoCode .panel-bug.show .panel-body {display: block;} +.repoCode .panel-bug.show .icon-chevron-sign-down:before {content: '\e711'} +.repoCode .panel-bug.show-edit-form .bug-edit-form, +.repoCode .panel-bug.show-form .commentForm, +.repoCode .comment.show-form .comment-edit-form {display: block;} +.repoCode .panel-bug .bug-edit-form, +.repoCode .panel-bug.show-form .addComment, +.repoCode .panel-bug .commentForm, +.repoCode .comment .comment-edit-form, +.repoCode .panel-bug.show-edit-form .panel-body .title, +.repoCode .panel-bug.show-edit-form .bug-date, +.repoCode .comment.show-form .comment-content, .repoCode .comment.show-form .comment-date {display: none} + +.repoCode .text-content {white-space: normal; white-space: pre-line} +.repoCode .text-muted {color: #aaa} + +.repoCode tr {transition: all 1s;} +.repoCode tr.highlight {background: #fff4e5;} +.repoCode tr.highlight td, .repoCode tr.highlight th {background: none; border-top: 1px solid #e48600; border-bottom: 1px solid #e48600} +.repoCode tr.highlight.commented th {color: #e48600;} +.repoCode tr.highlight.commented td, .repoCode tr.highlight.commented th {border-bottom: none} +.repoCode tr.highlight + tr.highlight td, .repoCode tr.highlight + tr.highlight th {border-top: none} + +.repoCode .row-tip {display: none;} +.repoCode tr.commented .row-tip {display: block; position: relative; right: -3px; bottom: -1px;} +.repoCode tr.commented .tip, .repoCode tr.commented.open .tip.on-collapse {display: block; position: absolute; right: 0; bottom: 0; color: #4183c4; opacity: 0; padding:0 5px; background: #edf3ff; height: 20px; line-height: 20px; transition: opacity 0.2s;} +.repoCode tr.commented:hover .tip.on-expand {opacity: 1} +.repoCode tr.commented.open .tip.on-collapse {opacity: 1} +.repoCode tr.commented.open .tip.on-expand {display: none} +.repoCode tr.commented.open .tip.on-collapse span {display: none} +.repoCode tr.commented.open:hover .tip.on-collapse span {display: inline;} +.repoCode tr.commented.open {background: #f8fafe} +.repoCode tr.commented .preview-icon {position: absolute; left: -6px; bottom: 0; width: 20px; height: 20px; line-height: 20px; text-align: center; color: #4183c4; background: #edf3ff; display: none} +.repoCode tr.commented:hover .preview-icon {display: block; transform: scale(-1, 1);} +.repoCode tr.commented .preview-icon:before {font-size: 18px;} + +.repoCode #diff tr.commented .row-tip {right: 0} +.repoCode #diff tr.commented .icon-chat-dot {left: 0} + +.repoCode .panel, .bugFormContainer {transition: border 0.4s;} +.repoCode .panel.highlight, #bugForm.highlight .bugFormContainer {border-color: #e48600;} + +#bugsPreview {white-space: normal;} +#bugsPreview .dropdown-menu {top: -100%; left: 30%; padding-top: 0; min-width: 300px; max-width: 500px;} +#bugsPreview .dropdown-menu > li.dropdown-header {background: #f1f1f1; padding-top: 8px;} +#bugsPreview .dropdown-menu > li > a {border-top: 1px solid #e5e5e5; text-overflow : ellipsis; overflow: hidden;} +#bugsPreview .dropdown-menu.show {display: block;} + +.icon-comments {position: relative; left: -50px} + +/* bug form */ +#bugForm, #bugForm table {margin: 0; padding: 0;} + +.panel .table + .panel-footer {border-top: 0; background: #fff} + +.transparent{ border-color:transparent;background: none repeat scroll 0 0 transparent;} +.transparent:hover{ border-color:transparent;background: none repeat scroll 0 0 transparent;} + +.side-col{width: 600px;} +#sidebar > .side-body{width: 580px;} +.hide-sidebar #sidebar > .side-body{display: none;} + +#sidebar>.sidebar-toggle{left:5px; right:auto;} +#sidebar>.sidebar-toggle>.icon{right:-4px; left:auto;} diff --git a/module/repo/css/diff.css b/module/repo/css/diff.css new file mode 100644 index 0000000000..736639694c --- /dev/null +++ b/module/repo/css/diff.css @@ -0,0 +1,22 @@ +body{padding-bottom:0px;} +.w-code{ width:48%; word-break: break-all;} +td.code { color:#484848; padding:0px 3px; white-space: pre-wrap} +.none {background:#EAF2F5;} +table.diff {margin-bottom:0px;} +.diff caption{ border: 1px solid #e4e4e4; background: #edf3fe; margin: 0; padding: 6px 2px 6px 10px; text-align: left; font-weight: bold; font-size: 13px;} +.diff th, .diff td{ border:none;} +.diff th{ padding-top:2px; padding-bottom:2px;} +.diff .line-new, .diff .line-new { background:#CFC;} +.diff .line-old, .diff .line-old { background:#FCC;} +.diff .line-all, .diff .line-all { background:#FFF;} +.diff .w-num { width:25px; border-right:1px solid #E4E4E4; color:#999; border-left:1px solid #E4E4E4; color:#999; font-weight:normal;} + +.repoCode .diff tr .comment-btn .icon-wrapper {left: -23px;} +.repoCode .diff tr.over td.line-all, .repoCode .diff tr.over td.line-all {background: #f8eec7} +.repoCode .diff tr.over td.line-new, .repoCode .diff tr.over td.line-new {background: #8eff8e} +.repoCode .diff tr.over td.line-old, .repoCode .diff tr.over td.line-old {background: #f6b2b2} + +.repoCode form > .btn {margin-right: 10px;} +.label-exchange {background-color: #566F7C;cursor: pointer} +.label-exchange i{padding:0;} +.btn-download{border-right: none;} diff --git a/module/repo/css/revision.css b/module/repo/css/revision.css new file mode 100644 index 0000000000..afdb877ab2 --- /dev/null +++ b/module/repo/css/revision.css @@ -0,0 +1,3 @@ +.change ul{margin:8px;margin-left:1.5em;} +.change li{ list-style:none;} +.panel-heading {border-bottom: 1px solid #ddd;} diff --git a/module/repo/css/view.css b/module/repo/css/view.css new file mode 100644 index 0000000000..1fc8387c42 --- /dev/null +++ b/module/repo/css/view.css @@ -0,0 +1,14 @@ +.code {position:relative; padding:3px; border-radius:3px;} +.code .content {border:1px solid #CCC; padding:0;} +.repoCode pre {margin:0;padding:0;background:white; border:none;} +tr,th,td {border-bottom:none;} +.repoCode pre > table > tbody > tr > th {border: none; background-color:#ECECEC; width: 40px; text-align:right; vertical-align: top;} +.repoCode pre > table > tbody > tr > th, .repoCode pre > table > tbody > tr > td {padding:1px 3px;} +.repoCode pre .commentContent{line-height:1.4;} +.repoCode pre > table{border:0px; width:100%;} +.repoCode .binary {text-align:center;} +.repoCode .binary a {display:block; margin:100px 0px;} +.repoCode .binary a .icon-download {font-size:50px;} +.repoCode .image {text-align:center; padding-top:10px;} +.panel .panel-heading .action .input-group {margin-top:-6px; margin-right:-10px;} +.body-modal .panel-actions {right:30px;} diff --git a/module/repo/js/common.js b/module/repo/js/common.js new file mode 100644 index 0000000000..65f666b25f --- /dev/null +++ b/module/repo/js/common.js @@ -0,0 +1,86 @@ +/** + * Swtich repo. + * + * @param int $repoID + * @param string $module + * @param string $method + * @access public + * @return void + */ +function switchRepo(repoID, module, method) +{ + if(typeof(eventKeyCode) == 'undefined') eventKeyCode = 0; + if(eventKeyCode > 0 && eventKeyCode != 13) return false; + + /* The projec id is a string, use it as the project model. */ + if(isNaN(repoID)) + { + $.cookie('projectMode', repoID, {expires:config.cookieLife, path:config.webRoot}); + repoID = 0; + } + + if(method != 'settings') method ="browse"; + link = createLink(module, method, 'repoID=' + repoID); + location.href=link; +} + +/** + * Switch branch for git. + * + * @param string $branchID + * @access public + * @return void + */ +function switchBranch(branchID) +{ + $.cookie('repoBranch', branchID, {expires:config.cookieLife, path:config.webRoot}); + $.cookie('repoRefresh', 1, {expires:config.cookieLife, path:config.webRoot}); + location.href=location.href; +} + +/** + * Limit select two. + * @return void + */ +if($("input:checkbox[name='revision[]']:checked").length < 2) +{ + $("input:checkbox[name='revision[]']:lt(2)").attr('checked', 'checked'); +} +$("input:checkbox[name='revision[]']").each(function(){ if(!$(this).is(':checked')) $(this).attr("disabled","disabled")}); +$("input:checkbox[name='revision[]']").click(function(){ + var checkNum = $("input:checkbox[name='revision[]']:checked").length; + if (checkNum >= 2) + { + $("input:checkbox[name='revision[]']").each(function(){ if(!$(this).is(':checked')) $(this).attr("disabled","disabled")}); + } + else + { + $("input:checkbox[name='revision[]']").each(function(){$(this).attr("disabled", false)}); + } +}); + +$(function() +{ + $(document).on('click', '.ajaxPager', function() + { + $('#sidebar .side-body').load($(this).attr('href')); + return false; + }) + + if($('#sidebar').size() > 0) + { + var fixH = $("#sidebar").offset().top; + $(window).scroll(function() + { + var scroH = $(this).scrollTop(); + if(scroH>=fixH) + { + $("#sidebar > .side-body").addClass('affix'); + } + else if(scroH .side-body").removeClass('affix'); + } + }); + } +}) diff --git a/module/repo/js/diff.js b/module/repo/js/diff.js new file mode 100644 index 0000000000..e2caeb5a9e --- /dev/null +++ b/module/repo/js/diff.js @@ -0,0 +1,5 @@ +function changeEncoding(encoding) +{ + $('#encoding').val(encoding); + $('#encoding').parents('form').submit(); +} diff --git a/module/repo/js/log.js b/module/repo/js/log.js new file mode 100644 index 0000000000..d9767c33e1 --- /dev/null +++ b/module/repo/js/log.js @@ -0,0 +1,18 @@ +$(document).ready(function() +{ + $("input:checkbox[name='revision[]']").each(function() + { + $(this).click(function() + { + var checkNum = $("input:checkbox[name='revision[]']:checked").length; + if (checkNum >= 2) + { + $("input:checkbox[name='revision[]']").each(function(){if($(this).attr('checked') == false) $(this).attr("disabled","disabled")}); + } + else + { + $("input:checkbox[name='revision[]']").each(function(){if($(this).attr('checked') == false) $(this).attr("enabled","enabled")}); + } + }); + }); +}); diff --git a/module/repo/js/view.js b/module/repo/js/view.js new file mode 100644 index 0000000000..b1d38c5a07 --- /dev/null +++ b/module/repo/js/view.js @@ -0,0 +1,19 @@ +$(document).ready(function() +{ + var $pre = $('.repoCode .content pre'); + var rowTip = $('#rowTip').html(); + + if($pre.length) + { + hljs.initHighlightingOnLoad(); + var content = hljs.highlight($pre.attr('class'), $pre.text()); + var code = '', line; + var arr = content.value.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/); + for(var i = 0 ; i < arr.length ; i++) + { + line = i + 1; + code += "
" + line + "" + (arr[i] || ' ') + rowTip + ""; + } + $pre.html("" + code + "
"); + } +}); diff --git a/module/repo/lang/de.php b/module/repo/lang/de.php new file mode 100644 index 0000000000..f666a0368d --- /dev/null +++ b/module/repo/lang/de.php @@ -0,0 +1,150 @@ +repo->common = 'Repo'; +$lang->repo->create = 'Create Repo'; +$lang->repo->settings = 'Settings'; +$lang->repo->browse = 'View Repo'; +$lang->repo->delete = 'Delete Repo'; +$lang->repo->showSyncComment = 'Display Synchronization'; +$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note'; +$lang->repo->download = 'Download File'; +$lang->repo->downloadDiff = 'Download Diff'; +$lang->repo->diffAction = 'Revision Diff'; +$lang->repo->revisionAction = 'Revision Detail'; +$lang->repo->blameAction = 'Repo Blame'; +$lang->repo->addBug = 'Add Review'; +$lang->repo->editBug = 'Edit Bug'; +$lang->repo->deleteBug = 'Delete Bug'; +$lang->repo->addComment = 'Add Comment'; +$lang->repo->editComment = 'Edit Comment'; +$lang->repo->deleteComment = 'Delete Comment'; + +$lang->repo->submit = 'Submit'; +$lang->repo->cancel = 'Cancel'; +$lang->repo->addComment = 'Add Comment'; + +$lang->repo->product = $lang->productCommon; +$lang->repo->module = 'Module'; +$lang->repo->project = $lang->projectCommon; +$lang->repo->type = 'Type'; +$lang->repo->assign = 'AssignTo'; +$lang->repo->title = 'Title'; +$lang->repo->detile = 'Detail'; +$lang->repo->lines = 'Lines'; +$lang->repo->line = 'Line'; +$lang->repo->expand = 'Unfold'; +$lang->repo->collapse = 'Fold'; + +$lang->repo->id = 'ID'; +$lang->repo->SCM = 'Type'; +$lang->repo->name = 'Name'; +$lang->repo->path = 'Path'; +$lang->repo->prefix = 'Prefix'; +$lang->repo->config = 'Config'; +$lang->repo->account = 'Username'; +$lang->repo->password = 'Password'; +$lang->repo->encoding = 'Encoding'; +$lang->repo->client = 'Client Path'; +$lang->repo->size = 'Size'; +$lang->repo->revision = 'Revision'; +$lang->repo->revisionA = 'Revision'; +$lang->repo->revisions = 'Revision'; +$lang->repo->time = 'Date'; +$lang->repo->committer = 'Committer'; +$lang->repo->commits = 'Commits'; +$lang->repo->synced = 'Initialize Sync'; +$lang->repo->lastSync = 'Last Sync'; +$lang->repo->deleted = 'Deleted'; +$lang->repo->commit = 'Commit'; +$lang->repo->comment = 'Comment'; +$lang->repo->view = 'View File'; +$lang->repo->viewA = 'View'; +$lang->repo->log = 'Revision Log'; +$lang->repo->blame = 'Blame'; +$lang->repo->date = 'Date'; +$lang->repo->diff = 'Diff'; +$lang->repo->diffAB = 'Diff'; +$lang->repo->diffAll = 'Diff All'; +$lang->repo->viewDiff = 'View diff'; +$lang->repo->allLog = 'All Revisions'; +$lang->repo->location = 'Location'; +$lang->repo->file = 'File'; +$lang->repo->action = 'Action'; +$lang->repo->code = 'Code'; +$lang->repo->review = 'Repo Review'; +$lang->repo->acl = 'Privilege'; +$lang->repo->group = 'Group'; +$lang->repo->user = 'User'; +$lang->repo->info = 'Version Info'; + +$lang->repo->title = 'Title'; +$lang->repo->status = 'Status'; +$lang->repo->openedBy = 'CreatedBy'; +$lang->repo->assignedTo = 'AssignedTo'; +$lang->repo->openedDate = 'CreatedDate'; + +$lang->repo->latestRevision = 'Latest Revision'; +$lang->repo->actionInfo = "Add by %s in %s"; +$lang->repo->changes = "Change Log"; +$lang->repo->reviewLocation = "File: %s@%s, line:%s - %s"; +$lang->repo->commentEdit = ''; +$lang->repo->commentDelete = ''; +$lang->repo->allChanges = "Other Changes"; +$lang->repo->commitTitle = "The %sth Commit"; + +$lang->repo->viewDiffList['inline'] = 'Inline'; +$lang->repo->viewDiffList['appose'] = 'Parallel'; + +$lang->repo->encryptList['plain'] = 'No encryption'; +$lang->repo->encryptList['base64'] = 'BASE64'; + +$lang->repo->logStyles['A'] = 'Add'; +$lang->repo->logStyles['M'] = 'Modification'; +$lang->repo->logStyles['D'] = 'Delete'; + +$lang->repo->encodingList['utf_8'] = 'UTF-8'; +$lang->repo->encodingList['gbk'] = 'GBK'; + +$lang->repo->scmList['Subversion'] = 'Subversion'; +$lang->repo->scmList['Git'] = 'Git'; + +$lang->repo->notice = new stdclass(); +$lang->repo->notice->syncing = 'Synchronizing. Please wait ...'; +$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...'; +$lang->repo->notice->syncedCount = 'The number of records synchronized is '; +$lang->repo->notice->delete = 'Are you sure delete this repo?'; +$lang->repo->notice->successDelete = 'Repository is removed.'; +$lang->repo->notice->commentContent = 'Comment'; +$lang->repo->notice->deleteBug = 'Are you sure to delete this bug?'; +$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?'; +$lang->repo->notice->lastSyncTime = 'Last Sync:'; + +$lang->repo->error = new stdclass(); +$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.'; +$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!'; +$lang->repo->error->version = 'Version 1.8+ of https and svn protocol is required. Please update to latest version! Go to http://subversion.apache.org/'; +$lang->repo->error->path = 'Repo address is the file path, e.g. /home/test.'; +$lang->repo->error->cmd = 'Client Error!'; +$lang->repo->error->diff = 'Two versions must be selected.'; +$lang->repo->error->product = "Please select {$lang->productCommon}!"; +$lang->repo->error->commentText = 'Please enter content for review!'; +$lang->repo->error->comment = 'Please enter content!'; +$lang->repo->error->title = 'Please enter title!'; +$lang->repo->error->accessDenied = 'You do not have the privilege to access the repository.'; +$lang->repo->error->noFound = 'The repo is not found.'; +$lang->repo->error->noFile = '%s does not exist.'; +$lang->repo->error->noPriv = 'The program does not have the privilege to switch to %s'; +$lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n"; +$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client"; +$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again."; + +$lang->repo->example = new stdclass(); +$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git"; +$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test"; +$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir."; +$lang->repo->example->encoding = "input encoding of files"; + +$lang->repo->typeList['standard'] = 'Standard'; +$lang->repo->typeList['performance'] = 'Performance'; +$lang->repo->typeList['security'] = 'Security'; +$lang->repo->typeList['redundancy'] = 'Redundancy'; +$lang->repo->typeList['logicError'] = 'Logic Error'; diff --git a/module/repo/lang/en.php b/module/repo/lang/en.php new file mode 100644 index 0000000000..f666a0368d --- /dev/null +++ b/module/repo/lang/en.php @@ -0,0 +1,150 @@ +repo->common = 'Repo'; +$lang->repo->create = 'Create Repo'; +$lang->repo->settings = 'Settings'; +$lang->repo->browse = 'View Repo'; +$lang->repo->delete = 'Delete Repo'; +$lang->repo->showSyncComment = 'Display Synchronization'; +$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note'; +$lang->repo->download = 'Download File'; +$lang->repo->downloadDiff = 'Download Diff'; +$lang->repo->diffAction = 'Revision Diff'; +$lang->repo->revisionAction = 'Revision Detail'; +$lang->repo->blameAction = 'Repo Blame'; +$lang->repo->addBug = 'Add Review'; +$lang->repo->editBug = 'Edit Bug'; +$lang->repo->deleteBug = 'Delete Bug'; +$lang->repo->addComment = 'Add Comment'; +$lang->repo->editComment = 'Edit Comment'; +$lang->repo->deleteComment = 'Delete Comment'; + +$lang->repo->submit = 'Submit'; +$lang->repo->cancel = 'Cancel'; +$lang->repo->addComment = 'Add Comment'; + +$lang->repo->product = $lang->productCommon; +$lang->repo->module = 'Module'; +$lang->repo->project = $lang->projectCommon; +$lang->repo->type = 'Type'; +$lang->repo->assign = 'AssignTo'; +$lang->repo->title = 'Title'; +$lang->repo->detile = 'Detail'; +$lang->repo->lines = 'Lines'; +$lang->repo->line = 'Line'; +$lang->repo->expand = 'Unfold'; +$lang->repo->collapse = 'Fold'; + +$lang->repo->id = 'ID'; +$lang->repo->SCM = 'Type'; +$lang->repo->name = 'Name'; +$lang->repo->path = 'Path'; +$lang->repo->prefix = 'Prefix'; +$lang->repo->config = 'Config'; +$lang->repo->account = 'Username'; +$lang->repo->password = 'Password'; +$lang->repo->encoding = 'Encoding'; +$lang->repo->client = 'Client Path'; +$lang->repo->size = 'Size'; +$lang->repo->revision = 'Revision'; +$lang->repo->revisionA = 'Revision'; +$lang->repo->revisions = 'Revision'; +$lang->repo->time = 'Date'; +$lang->repo->committer = 'Committer'; +$lang->repo->commits = 'Commits'; +$lang->repo->synced = 'Initialize Sync'; +$lang->repo->lastSync = 'Last Sync'; +$lang->repo->deleted = 'Deleted'; +$lang->repo->commit = 'Commit'; +$lang->repo->comment = 'Comment'; +$lang->repo->view = 'View File'; +$lang->repo->viewA = 'View'; +$lang->repo->log = 'Revision Log'; +$lang->repo->blame = 'Blame'; +$lang->repo->date = 'Date'; +$lang->repo->diff = 'Diff'; +$lang->repo->diffAB = 'Diff'; +$lang->repo->diffAll = 'Diff All'; +$lang->repo->viewDiff = 'View diff'; +$lang->repo->allLog = 'All Revisions'; +$lang->repo->location = 'Location'; +$lang->repo->file = 'File'; +$lang->repo->action = 'Action'; +$lang->repo->code = 'Code'; +$lang->repo->review = 'Repo Review'; +$lang->repo->acl = 'Privilege'; +$lang->repo->group = 'Group'; +$lang->repo->user = 'User'; +$lang->repo->info = 'Version Info'; + +$lang->repo->title = 'Title'; +$lang->repo->status = 'Status'; +$lang->repo->openedBy = 'CreatedBy'; +$lang->repo->assignedTo = 'AssignedTo'; +$lang->repo->openedDate = 'CreatedDate'; + +$lang->repo->latestRevision = 'Latest Revision'; +$lang->repo->actionInfo = "Add by %s in %s"; +$lang->repo->changes = "Change Log"; +$lang->repo->reviewLocation = "File: %s@%s, line:%s - %s"; +$lang->repo->commentEdit = ''; +$lang->repo->commentDelete = ''; +$lang->repo->allChanges = "Other Changes"; +$lang->repo->commitTitle = "The %sth Commit"; + +$lang->repo->viewDiffList['inline'] = 'Inline'; +$lang->repo->viewDiffList['appose'] = 'Parallel'; + +$lang->repo->encryptList['plain'] = 'No encryption'; +$lang->repo->encryptList['base64'] = 'BASE64'; + +$lang->repo->logStyles['A'] = 'Add'; +$lang->repo->logStyles['M'] = 'Modification'; +$lang->repo->logStyles['D'] = 'Delete'; + +$lang->repo->encodingList['utf_8'] = 'UTF-8'; +$lang->repo->encodingList['gbk'] = 'GBK'; + +$lang->repo->scmList['Subversion'] = 'Subversion'; +$lang->repo->scmList['Git'] = 'Git'; + +$lang->repo->notice = new stdclass(); +$lang->repo->notice->syncing = 'Synchronizing. Please wait ...'; +$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...'; +$lang->repo->notice->syncedCount = 'The number of records synchronized is '; +$lang->repo->notice->delete = 'Are you sure delete this repo?'; +$lang->repo->notice->successDelete = 'Repository is removed.'; +$lang->repo->notice->commentContent = 'Comment'; +$lang->repo->notice->deleteBug = 'Are you sure to delete this bug?'; +$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?'; +$lang->repo->notice->lastSyncTime = 'Last Sync:'; + +$lang->repo->error = new stdclass(); +$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.'; +$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!'; +$lang->repo->error->version = 'Version 1.8+ of https and svn protocol is required. Please update to latest version! Go to http://subversion.apache.org/'; +$lang->repo->error->path = 'Repo address is the file path, e.g. /home/test.'; +$lang->repo->error->cmd = 'Client Error!'; +$lang->repo->error->diff = 'Two versions must be selected.'; +$lang->repo->error->product = "Please select {$lang->productCommon}!"; +$lang->repo->error->commentText = 'Please enter content for review!'; +$lang->repo->error->comment = 'Please enter content!'; +$lang->repo->error->title = 'Please enter title!'; +$lang->repo->error->accessDenied = 'You do not have the privilege to access the repository.'; +$lang->repo->error->noFound = 'The repo is not found.'; +$lang->repo->error->noFile = '%s does not exist.'; +$lang->repo->error->noPriv = 'The program does not have the privilege to switch to %s'; +$lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n"; +$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client"; +$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again."; + +$lang->repo->example = new stdclass(); +$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git"; +$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test"; +$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir."; +$lang->repo->example->encoding = "input encoding of files"; + +$lang->repo->typeList['standard'] = 'Standard'; +$lang->repo->typeList['performance'] = 'Performance'; +$lang->repo->typeList['security'] = 'Security'; +$lang->repo->typeList['redundancy'] = 'Redundancy'; +$lang->repo->typeList['logicError'] = 'Logic Error'; diff --git a/module/repo/lang/fr.php b/module/repo/lang/fr.php new file mode 100644 index 0000000000..f666a0368d --- /dev/null +++ b/module/repo/lang/fr.php @@ -0,0 +1,150 @@ +repo->common = 'Repo'; +$lang->repo->create = 'Create Repo'; +$lang->repo->settings = 'Settings'; +$lang->repo->browse = 'View Repo'; +$lang->repo->delete = 'Delete Repo'; +$lang->repo->showSyncComment = 'Display Synchronization'; +$lang->repo->ajaxSyncComment = 'Interface: Ajax Sync Note'; +$lang->repo->download = 'Download File'; +$lang->repo->downloadDiff = 'Download Diff'; +$lang->repo->diffAction = 'Revision Diff'; +$lang->repo->revisionAction = 'Revision Detail'; +$lang->repo->blameAction = 'Repo Blame'; +$lang->repo->addBug = 'Add Review'; +$lang->repo->editBug = 'Edit Bug'; +$lang->repo->deleteBug = 'Delete Bug'; +$lang->repo->addComment = 'Add Comment'; +$lang->repo->editComment = 'Edit Comment'; +$lang->repo->deleteComment = 'Delete Comment'; + +$lang->repo->submit = 'Submit'; +$lang->repo->cancel = 'Cancel'; +$lang->repo->addComment = 'Add Comment'; + +$lang->repo->product = $lang->productCommon; +$lang->repo->module = 'Module'; +$lang->repo->project = $lang->projectCommon; +$lang->repo->type = 'Type'; +$lang->repo->assign = 'AssignTo'; +$lang->repo->title = 'Title'; +$lang->repo->detile = 'Detail'; +$lang->repo->lines = 'Lines'; +$lang->repo->line = 'Line'; +$lang->repo->expand = 'Unfold'; +$lang->repo->collapse = 'Fold'; + +$lang->repo->id = 'ID'; +$lang->repo->SCM = 'Type'; +$lang->repo->name = 'Name'; +$lang->repo->path = 'Path'; +$lang->repo->prefix = 'Prefix'; +$lang->repo->config = 'Config'; +$lang->repo->account = 'Username'; +$lang->repo->password = 'Password'; +$lang->repo->encoding = 'Encoding'; +$lang->repo->client = 'Client Path'; +$lang->repo->size = 'Size'; +$lang->repo->revision = 'Revision'; +$lang->repo->revisionA = 'Revision'; +$lang->repo->revisions = 'Revision'; +$lang->repo->time = 'Date'; +$lang->repo->committer = 'Committer'; +$lang->repo->commits = 'Commits'; +$lang->repo->synced = 'Initialize Sync'; +$lang->repo->lastSync = 'Last Sync'; +$lang->repo->deleted = 'Deleted'; +$lang->repo->commit = 'Commit'; +$lang->repo->comment = 'Comment'; +$lang->repo->view = 'View File'; +$lang->repo->viewA = 'View'; +$lang->repo->log = 'Revision Log'; +$lang->repo->blame = 'Blame'; +$lang->repo->date = 'Date'; +$lang->repo->diff = 'Diff'; +$lang->repo->diffAB = 'Diff'; +$lang->repo->diffAll = 'Diff All'; +$lang->repo->viewDiff = 'View diff'; +$lang->repo->allLog = 'All Revisions'; +$lang->repo->location = 'Location'; +$lang->repo->file = 'File'; +$lang->repo->action = 'Action'; +$lang->repo->code = 'Code'; +$lang->repo->review = 'Repo Review'; +$lang->repo->acl = 'Privilege'; +$lang->repo->group = 'Group'; +$lang->repo->user = 'User'; +$lang->repo->info = 'Version Info'; + +$lang->repo->title = 'Title'; +$lang->repo->status = 'Status'; +$lang->repo->openedBy = 'CreatedBy'; +$lang->repo->assignedTo = 'AssignedTo'; +$lang->repo->openedDate = 'CreatedDate'; + +$lang->repo->latestRevision = 'Latest Revision'; +$lang->repo->actionInfo = "Add by %s in %s"; +$lang->repo->changes = "Change Log"; +$lang->repo->reviewLocation = "File: %s@%s, line:%s - %s"; +$lang->repo->commentEdit = ''; +$lang->repo->commentDelete = ''; +$lang->repo->allChanges = "Other Changes"; +$lang->repo->commitTitle = "The %sth Commit"; + +$lang->repo->viewDiffList['inline'] = 'Inline'; +$lang->repo->viewDiffList['appose'] = 'Parallel'; + +$lang->repo->encryptList['plain'] = 'No encryption'; +$lang->repo->encryptList['base64'] = 'BASE64'; + +$lang->repo->logStyles['A'] = 'Add'; +$lang->repo->logStyles['M'] = 'Modification'; +$lang->repo->logStyles['D'] = 'Delete'; + +$lang->repo->encodingList['utf_8'] = 'UTF-8'; +$lang->repo->encodingList['gbk'] = 'GBK'; + +$lang->repo->scmList['Subversion'] = 'Subversion'; +$lang->repo->scmList['Git'] = 'Git'; + +$lang->repo->notice = new stdclass(); +$lang->repo->notice->syncing = 'Synchronizing. Please wait ...'; +$lang->repo->notice->syncComplete = 'Synchronized. Now redirecting ...'; +$lang->repo->notice->syncedCount = 'The number of records synchronized is '; +$lang->repo->notice->delete = 'Are you sure delete this repo?'; +$lang->repo->notice->successDelete = 'Repository is removed.'; +$lang->repo->notice->commentContent = 'Comment'; +$lang->repo->notice->deleteBug = 'Are you sure to delete this bug?'; +$lang->repo->notice->deleteComment = 'Are you sure to delete this comment?'; +$lang->repo->notice->lastSyncTime = 'Last Sync:'; + +$lang->repo->error = new stdclass(); +$lang->repo->error->useless = 'Your server disabled exec and shell_exec, so it cannot be applied.'; +$lang->repo->error->connect = 'Connection to the repo failed. Please enter username, password and repo address correctly!'; +$lang->repo->error->version = 'Version 1.8+ of https and svn protocol is required. Please update to latest version! Go to http://subversion.apache.org/'; +$lang->repo->error->path = 'Repo address is the file path, e.g. /home/test.'; +$lang->repo->error->cmd = 'Client Error!'; +$lang->repo->error->diff = 'Two versions must be selected.'; +$lang->repo->error->product = "Please select {$lang->productCommon}!"; +$lang->repo->error->commentText = 'Please enter content for review!'; +$lang->repo->error->comment = 'Please enter content!'; +$lang->repo->error->title = 'Please enter title!'; +$lang->repo->error->accessDenied = 'You do not have the privilege to access the repository.'; +$lang->repo->error->noFound = 'The repo is not found.'; +$lang->repo->error->noFile = '%s does not exist.'; +$lang->repo->error->noPriv = 'The program does not have the privilege to switch to %s'; +$lang->repo->error->output = "The command is: %s\nThe error is(%s): %s\n"; +$lang->repo->error->clientVersion = "Client version is too low, please upgrade or change SVN client"; +$lang->repo->error->encoding = "The encoding maybe wrong. Please change the encoding and try again."; + +$lang->repo->example = new stdclass(); +$lang->repo->example->client = "For example, /usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git"; +$lang->repo->example->path = "For example, SVN: http://example.googlecode.com/svn/, GIT: /home/test"; +$lang->repo->example->config = "Config directory is required in https. Use '--config-dir' to generate config dir."; +$lang->repo->example->encoding = "input encoding of files"; + +$lang->repo->typeList['standard'] = 'Standard'; +$lang->repo->typeList['performance'] = 'Performance'; +$lang->repo->typeList['security'] = 'Security'; +$lang->repo->typeList['redundancy'] = 'Redundancy'; +$lang->repo->typeList['logicError'] = 'Logic Error'; diff --git a/module/repo/lang/zh-cn.php b/module/repo/lang/zh-cn.php new file mode 100644 index 0000000000..14c089439a --- /dev/null +++ b/module/repo/lang/zh-cn.php @@ -0,0 +1,150 @@ +repo->common = '代码'; +$lang->repo->create = '创建版本库'; +$lang->repo->settings = '版本库设置'; +$lang->repo->browse = '浏览'; +$lang->repo->delete = '删除版本库'; +$lang->repo->showSyncComment = '显示同步进度'; +$lang->repo->ajaxSyncComment = '接口:AJAX同步注释'; +$lang->repo->download = '下载'; +$lang->repo->downloadDiff = '下载Diff'; +$lang->repo->diffAction = '版本对比'; +$lang->repo->revisionAction = '版本详情'; +$lang->repo->blameAction = '版本追溯'; +$lang->repo->addBug = '添加评审'; +$lang->repo->editBug = '编辑评审'; +$lang->repo->deleteBug = '删除评审'; +$lang->repo->addComment = '添加备注'; +$lang->repo->editComment = '编辑备注'; +$lang->repo->deleteComment = '删除备注'; + +$lang->repo->submit = '提交'; +$lang->repo->cancel = '取消'; +$lang->repo->addComment = '添加评论'; + +$lang->repo->product = $lang->productCommon; +$lang->repo->module = '模块'; +$lang->repo->project = $lang->projectCommon; +$lang->repo->type = '类型'; +$lang->repo->assign = '指派'; +$lang->repo->title = '标题'; +$lang->repo->detile = '详情'; +$lang->repo->lines = '代码行'; +$lang->repo->line = '行'; +$lang->repo->expand = '点击展开'; +$lang->repo->collapse = '点击折叠'; + +$lang->repo->id = '编号'; +$lang->repo->SCM = '类型'; +$lang->repo->name = '名称'; +$lang->repo->path = '地址'; +$lang->repo->prefix = '地址扩展'; +$lang->repo->config = '配置目录'; +$lang->repo->account = '用户名'; +$lang->repo->password = '密码'; +$lang->repo->encoding = '编码'; +$lang->repo->client = '客户端'; +$lang->repo->size = '大小'; +$lang->repo->revision = '查看版本'; +$lang->repo->revisionA = '版本'; +$lang->repo->revisions = '版本'; +$lang->repo->time = '提交时间'; +$lang->repo->committer = '作者'; +$lang->repo->commits = '提交数'; +$lang->repo->synced = '初始化同步'; +$lang->repo->lastSync = '最后同步时间'; +$lang->repo->deleted = '已删除'; +$lang->repo->commit = '提交'; +$lang->repo->comment = '注释'; +$lang->repo->view = '查看文件'; +$lang->repo->viewA = '查看'; +$lang->repo->log = '版本历史'; +$lang->repo->blame = '追溯'; +$lang->repo->date = '日期'; +$lang->repo->diff = '比较差异'; +$lang->repo->diffAB = '比较'; +$lang->repo->diffAll = '全部比较'; +$lang->repo->viewDiff = '查看差异'; +$lang->repo->allLog = '所有版本'; +$lang->repo->location = '位置'; +$lang->repo->file = '文件'; +$lang->repo->action = '操作'; +$lang->repo->code = '代码'; +$lang->repo->review = '评审'; +$lang->repo->acl = '权限'; +$lang->repo->group = '分组'; +$lang->repo->user = '用户'; +$lang->repo->info = '版本信息'; + +$lang->repo->title = '标题'; +$lang->repo->status = '状态'; +$lang->repo->openedBy = '创建者'; +$lang->repo->assignedTo = '指派给'; +$lang->repo->openedDate = '创建日期'; + +$lang->repo->latestRevision = '最近修订版本'; +$lang->repo->actionInfo = "由%s在%s添加"; +$lang->repo->changes = "修改记录"; +$lang->repo->reviewLocation = "%s@%s,%s行 - %s行"; +$lang->repo->commentEdit = ''; +$lang->repo->commentDelete = ''; +$lang->repo->allChanges = "其他改动"; +$lang->repo->commitTitle = "第%s次提交"; + +$lang->repo->viewDiffList['inline'] = '直列'; +$lang->repo->viewDiffList['appose'] = '并排'; + +$lang->repo->encryptList['plain'] = '不加密'; +$lang->repo->encryptList['base64'] = 'BASE64'; + +$lang->repo->logStyles['A'] = '添加'; +$lang->repo->logStyles['M'] = '修改'; +$lang->repo->logStyles['D'] = '删除'; + +$lang->repo->encodingList['utf_8'] = 'UTF-8'; +$lang->repo->encodingList['gbk'] = 'GBK'; + +$lang->repo->scmList['Subversion'] = 'Subversion'; +$lang->repo->scmList['Git'] = 'Git'; + +$lang->repo->notice = new stdclass(); +$lang->repo->notice->syncing = '正在同步中, 请稍等...'; +$lang->repo->notice->syncComplete = '同步完成,正在跳转...'; +$lang->repo->notice->syncedCount = '已经同步记录条数'; +$lang->repo->notice->delete = '是否要删除该版本库?'; +$lang->repo->notice->successDelete = '已经成功删除版本库。'; +$lang->repo->notice->commentContent = '输入回复内容'; +$lang->repo->notice->deleteBug = '确认删除该Bug?'; +$lang->repo->notice->deleteComment = '确认删除该回复?'; +$lang->repo->notice->lastSyncTime = '最后更新于:'; + +$lang->repo->error = new stdclass(); +$lang->repo->error->useless = '你的服务器禁用了exec,shell_exec方法,无法使用该功能'; +$lang->repo->error->connect = '连接版本库失败,请填写正确的用户名、密码和版本库地址!'; +$lang->repo->error->version = "https和svn协议需要1.8及以上版本的客户端,请升级到最新版本!详情访问:http://subversion.apache.org/"; +$lang->repo->error->path = '版本库地址直接填写文件路径,如:/home/test。'; +$lang->repo->error->cmd = '客户端错误!'; +$lang->repo->error->diff = '必须选择两个版本'; +$lang->repo->error->product = "请选择{$lang->productCommon}!"; +$lang->repo->error->commentText = '请填写评审内容'; +$lang->repo->error->comment = '请填写内容'; +$lang->repo->error->title = '请填写标题'; +$lang->repo->error->accessDenied = '你没有权限访问该版本库'; +$lang->repo->error->noFound = '你访问的版本库不存在'; +$lang->repo->error->noFile = '目录 %s 不存在'; +$lang->repo->error->noPriv = '程序没有权限切换到目录 %s'; +$lang->repo->error->output = "执行命令:%s\n错误结果(%s): %s\n"; +$lang->repo->error->clientVersion = "客户端版本过低,请升级或更换SVN客户端"; +$lang->repo->error->encoding = "编码可能错误,请更换编码重试。"; + +$lang->repo->example = new stdclass(); +$lang->repo->example->client = "例如:/usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git"; +$lang->repo->example->path = "例如:SVN: http://example.googlecode.com/svn/, GIT: /homt/test"; +$lang->repo->example->config = "https需要填写配置目录的位置,通过config-dir选项生成配置目录"; +$lang->repo->example->encoding = "填写版本库中文件的编码"; + +$lang->repo->typeList['standard'] = '规范'; +$lang->repo->typeList['performance'] = '性能'; +$lang->repo->typeList['security'] = '安全'; +$lang->repo->typeList['redundancy'] = '冗余'; +$lang->repo->typeList['logicError'] = '逻辑错误'; diff --git a/module/repo/lang/zh-tw.php b/module/repo/lang/zh-tw.php new file mode 100644 index 0000000000..dac611fd5a --- /dev/null +++ b/module/repo/lang/zh-tw.php @@ -0,0 +1,150 @@ +repo->common = '代碼'; +$lang->repo->create = '創建版本庫'; +$lang->repo->settings = '版本庫設置'; +$lang->repo->browse = '瀏覽'; +$lang->repo->delete = '刪除版本庫'; +$lang->repo->showSyncComment = '顯示同步進度'; +$lang->repo->ajaxSyncComment = '介面:AJAX同步註釋'; +$lang->repo->download = '下載'; +$lang->repo->downloadDiff = '下載Diff'; +$lang->repo->diffAction = '版本對比'; +$lang->repo->revisionAction = '版本詳情'; +$lang->repo->blameAction = '版本追溯'; +$lang->repo->addBug = '添加評審'; +$lang->repo->editBug = '編輯評審'; +$lang->repo->deleteBug = '刪除評審'; +$lang->repo->addComment = '添加備註'; +$lang->repo->editComment = '編輯備註'; +$lang->repo->deleteComment = '刪除備註'; + +$lang->repo->submit = '提交'; +$lang->repo->cancel = '取消'; +$lang->repo->addComment = '添加評論'; + +$lang->repo->product = $lang->productCommon; +$lang->repo->module = '模組'; +$lang->repo->project = $lang->projectCommon; +$lang->repo->type = '類型'; +$lang->repo->assign = '指派'; +$lang->repo->title = '標題'; +$lang->repo->detile = '詳情'; +$lang->repo->lines = '代碼行'; +$lang->repo->line = '行'; +$lang->repo->expand = '點擊展開'; +$lang->repo->collapse = '點擊摺疊'; + +$lang->repo->id = '編號'; +$lang->repo->SCM = '類型'; +$lang->repo->name = '名稱'; +$lang->repo->path = '地址'; +$lang->repo->prefix = '地址擴展'; +$lang->repo->config = '配置目錄'; +$lang->repo->account = '用戶名'; +$lang->repo->password = '密碼'; +$lang->repo->encoding = '編碼'; +$lang->repo->client = '客戶端'; +$lang->repo->size = '大小'; +$lang->repo->revision = '查看版本'; +$lang->repo->revisionA = '版本'; +$lang->repo->revisions = '版本'; +$lang->repo->time = '提交時間'; +$lang->repo->committer = '作者'; +$lang->repo->commits = '提交數'; +$lang->repo->synced = '初始化同步'; +$lang->repo->lastSync = '最後同步時間'; +$lang->repo->deleted = '已刪除'; +$lang->repo->commit = '提交'; +$lang->repo->comment = '註釋'; +$lang->repo->view = '查看檔案'; +$lang->repo->viewA = '查看'; +$lang->repo->log = '版本歷史'; +$lang->repo->blame = '追溯'; +$lang->repo->date = '日期'; +$lang->repo->diff = '比較差異'; +$lang->repo->diffAB = '比較'; +$lang->repo->diffAll = '全部比較'; +$lang->repo->viewDiff = '查看差異'; +$lang->repo->allLog = '所有版本'; +$lang->repo->location = '位置'; +$lang->repo->file = '檔案'; +$lang->repo->action = '操作'; +$lang->repo->code = '代碼'; +$lang->repo->review = '評審'; +$lang->repo->acl = '權限'; +$lang->repo->group = '分組'; +$lang->repo->user = '用戶'; +$lang->repo->info = '版本信息'; + +$lang->repo->title = '標題'; +$lang->repo->status = '狀態'; +$lang->repo->openedBy = '創建者'; +$lang->repo->assignedTo = '指派給'; +$lang->repo->openedDate = '創建日期'; + +$lang->repo->latestRevision = '最近修訂版本'; +$lang->repo->actionInfo = "由%s在%s添加"; +$lang->repo->changes = "修改記錄"; +$lang->repo->reviewLocation = "%s@%s,%s行 - %s行"; +$lang->repo->commentEdit = ''; +$lang->repo->commentDelete = ''; +$lang->repo->allChanges = "其他改動"; +$lang->repo->commitTitle = "第%s次提交"; + +$lang->repo->viewDiffList['inline'] = '直列'; +$lang->repo->viewDiffList['appose'] = '並排'; + +$lang->repo->encryptList['plain'] = '不加密'; +$lang->repo->encryptList['base64'] = 'BASE64'; + +$lang->repo->logStyles['A'] = '添加'; +$lang->repo->logStyles['M'] = '修改'; +$lang->repo->logStyles['D'] = '刪除'; + +$lang->repo->encodingList['utf_8'] = 'UTF-8'; +$lang->repo->encodingList['gbk'] = 'GBK'; + +$lang->repo->scmList['Subversion'] = 'Subversion'; +$lang->repo->scmList['Git'] = 'Git'; + +$lang->repo->notice = new stdclass(); +$lang->repo->notice->syncing = '正在同步中, 請稍等...'; +$lang->repo->notice->syncComplete = '同步完成,正在跳轉...'; +$lang->repo->notice->syncedCount = '已經同步記錄條數'; +$lang->repo->notice->delete = '是否要刪除該版本庫?'; +$lang->repo->notice->successDelete = '已經成功刪除版本庫。'; +$lang->repo->notice->commentContent = '輸入回覆內容'; +$lang->repo->notice->deleteBug = '確認刪除該Bug?'; +$lang->repo->notice->deleteComment = '確認刪除該回覆?'; +$lang->repo->notice->lastSyncTime = '最後更新于:'; + +$lang->repo->error = new stdclass(); +$lang->repo->error->useless = '你的伺服器禁用了exec,shell_exec方法,無法使用該功能'; +$lang->repo->error->connect = '連接版本庫失敗,請填寫正確的用戶名、密碼和版本庫地址!'; +$lang->repo->error->version = "https和svn協議需要1.8及以上版本的客戶端,請升級到最新版本!詳情訪問:http://subversion.apache.org/"; +$lang->repo->error->path = '版本庫地址直接填寫檔案路徑,如:/home/test。'; +$lang->repo->error->cmd = '客戶端錯誤!'; +$lang->repo->error->diff = '必須選擇兩個版本'; +$lang->repo->error->product = "請選擇{$lang->productCommon}!"; +$lang->repo->error->commentText = '請填寫評審內容'; +$lang->repo->error->comment = '請填寫內容'; +$lang->repo->error->title = '請填寫標題'; +$lang->repo->error->accessDenied = '你沒有權限訪問該版本庫'; +$lang->repo->error->noFound = '你訪問的版本庫不存在'; +$lang->repo->error->noFile = '目錄 %s 不存在'; +$lang->repo->error->noPriv = '程序沒有權限切換到目錄 %s'; +$lang->repo->error->output = "執行命令:%s\n錯誤結果(%s): %s\n"; +$lang->repo->error->clientVersion = "客戶端版本過低,請升級或更換SVN客戶端"; +$lang->repo->error->encoding = "編碼可能錯誤,請更換編碼重試。"; + +$lang->repo->example = new stdclass(); +$lang->repo->example->client = "例如:/usr/bin/svn, C:\subversion\svn.exe, /usr/bin/git"; +$lang->repo->example->path = "例如:SVN: http://example.googlecode.com/svn/, GIT: /homt/test"; +$lang->repo->example->config = "https需要填寫配置目錄的位置,通過config-dir選項生成配置目錄"; +$lang->repo->example->encoding = "填寫版本庫中檔案的編碼"; + +$lang->repo->typeList['standard'] = '規範'; +$lang->repo->typeList['performance'] = '性能'; +$lang->repo->typeList['security'] = '安全'; +$lang->repo->typeList['redundancy'] = '冗餘'; +$lang->repo->typeList['logicError'] = '邏輯錯誤'; diff --git a/module/repo/model.php b/module/repo/model.php new file mode 100644 index 0000000000..aee4a9049c --- /dev/null +++ b/module/repo/model.php @@ -0,0 +1,964 @@ +app->user->account; + if(strpos(",{$this->app->company->admins},", ",$account,") !== false) return true; + if(empty($repo->acl->groups) and empty($repo->acl->users)) return true; + if(!empty($repo->acl->groups)) + { + foreach($this->app->user->groups as $group) + { + if(in_array($group, $repo->acl->groups)) return true; + } + } + if(!empty($repo->acl->users) and in_array($account, $repo->acl->users)) return true; + return false; + } + + /** + * Set menu. + * + * @param array $repos + * @param int $repoID + * @access public + * @return void + */ + public function setMenu($repos, $repoID = '') + { + if(empty($repoID)) $repoID = $this->session->repoID ? $this->session->repoID : key($repos); + if(!isset($repos[$repoID])) $repoID = key($repos); + + /* Check the privilege. */ + if($repoID) + { + $repo = $this->getRepoByID($repoID); + if(empty($repo)) + { + echo(js::alert($this->lang->repo->error->noFound)); + die(js::locate('back')); + } + + if(!$this->checkPriv($repo)) + { + echo(js::alert($this->lang->repo->error->accessDenied)); + die(js::locate('back')); + } + } + + if(!empty($repos)) + { + $repoIndex = '
'; + $repoIndex .= $this->select($repos, $repoID); + $repoIndex .= '
'; + + $branches = $this->getBranches($repo); + if(empty($branches)) + { + $this->setRepoBranch(''); + } + else + { + $branchID = 'master'; + if($this->cookie->repoBranch) $branchID = $this->cookie->repoBranch; + if(!isset($branches[$branchID])) $branchID = 'master'; + + $branch = zget($branches, $branchID); + if(empty($branch)) $branchID = $branch = current($branches); + + $this->setRepoBranch($branchID); + + $repoIndex .= '
'; + $repoIndex .= "
"; + } + + $this->lang->modulePageNav = $repoIndex; + } + + foreach($this->lang->repo->menu as $key => $menu) + { + common::setMenuVars($this->lang->repo->menu, $key, $repoID); + } + + session_start(); + $this->session->set('repoID', $repoID); + session_write_close(); + } + + /** + * Create the select code of repos. + * + * @param array $repos + * @param int $repoID + * @param string $currentModule + * @param string $currentMethod + * @access public + * @return string + */ + public function select($repos, $repoID) + { + $selectHtml = ""; + + return $selectHtml; + } + + /** + * Get all repos. + * + * @access public + * @return array + */ + public function getAllRepos() + { + $repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq(0)->fetchAll(); + foreach($repos as $i => $repo) + { + $repo->acl = json_decode($repo->acl); + if(!$this->checkPriv($repo)) unset($repos[$i]); + } + + return $repos; + } + + /** + * Get repo pairs. + * + * @access public + * @return array + */ + public function getRepoPairs() + { + $repos = $this->dao->select('*')->from(TABLE_REPO)->where('deleted')->eq(0)->fetchAll(); + $repoPairs = array(); + foreach($repos as $repo) + { + $repo->acl = json_decode($repo->acl); + $scm = $repo->SCM == 'Subversion' ? 'svn' : 'git'; + if($this->checkPriv($repo)) $repoPairs[$repo->id] = "[{$scm}] " . $repo->name; + } + + return $repoPairs; + } + + /** + * Get repo by id. + * + * @param int $repoID + * @access public + * @return object + */ + public function getRepoByID($repoID) + { + $repo = $this->dao->select('*')->from(TABLE_REPO)->where('id')->eq($repoID)->fetch(); + if(!$repo) return false; + + if($repo->encrypt == 'base64') $repo->password = base64_decode($repo->password); + $repo->acl = json_decode($repo->acl); + return $repo; + } + + /** + * Get git branches. + * + * @param object $repo + * @access public + * @return array + */ + public function getBranches($repo) + { + $this->scm = $this->app->loadClass('scm'); + $this->scm->setEngine($repo); + return $this->scm->branch(); + } + + /** + * Get logs. + * + * @param object $repo + * @param string $entry + * @param string $revision + * @param string $type + * @param object $pager + * @access public + * @return array + */ + public function getLogs($repo, $entry, $revision = 'HEAD', $type = 'dir', $pager = null) + { + $entry = ltrim($entry, '/'); + $entry = $repo->prefix . (empty($entry) ? '' : '/' . $entry); + if((time() - strtotime($repo->lastSync)) / 60 >= $this->config->repo->syncTime) $this->updateLatestCommit($repo); + + $repoID = $repo->id; + $revisionTime = $this->dao->select('time')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($revision != 'HEAD')->andWhere('t1.revision')->eq($revision)->fi() + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->orderBy('time desc') + ->limit(1) + ->fetch('time'); + + $historyIdList = array(); + if($entry != '/' and !empty($entry)) + { + $historyIdList = $this->dao->select('DISTINCT t2.id')->from(TABLE_REPOFILES)->alias('t1') + ->leftJoin(TABLE_REPOHISTORY)->alias('t2')->on('t1.revision=t2.id') + ->where('1=1') + ->andWhere('t1.repo')->eq($repo->id) + ->beginIF($type == 'dir') + ->andWhere('t1.parent', true)->like(rtrim($entry, '/') . "/%") + ->orWhere('t1.parent')->eq(rtrim($entry, '/')) + ->markRight(1) + ->fi() + ->beginIF($type == 'file')->andWhere('t1.path')->eq("$entry")->fi() + ->orderBy('t2.`time` desc') + ->page($pager) + ->fetchPairs('id', 'id'); + } + + $comments = $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->andWhere('t1.`time`')->le($revisionTime) + ->andWhere('left(t1.comment, 12)')->ne('Merge branch') + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->beginIF($entry != '/' and !empty($entry))->andWhere('t1.id')->in($historyIdList)->fi() + ->orderBy('time desc'); + if($entry == '/' or empty($entry))$comments->page($pager, 't1.id'); + $comments = $comments->fetchAll('revision'); + + foreach($comments as $repoComment) $repoComment->comment = $this->replaceCommentLink($repoComment->comment); + return $comments; + } + + /** + * Get latest comment. + * + * @param int $repoID + * @access public + * @return object + */ + public function getLatestComment($repoID) + { + $count = $this->dao->select('count(DISTINCT t1.id) as count')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->fetch('count'); + + $lastComment = $this->dao->select('t1.*')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->orderBy('t1.time desc') + ->limit(1) + ->fetch(); + if(empty($lastComment)) return null; + + $repo = $this->getRepoByID($repoID); + if($repo->SCM == 'Git' and $lastComment->commit != $count) + { + $this->fixCommit($repo->id); + $lastComment->commit = $count; + } + + return $lastComment; + } + + /** + * Get revisions from db. + * + * @param int $repoID + * @param string $limit + * @param string $maxRevision + * @param string $minRevision + * @access public + * @return array + */ + public function getRevisionsFromDB($repoID, $limit = '', $maxRevision = '', $minRevision = '') + { + $revisions = $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF(!empty($maxRevision))->andWhere('t1.revision')->le($maxRevision)->fi() + ->beginIF(!empty($minRevision))->andWhere('t1.revision')->ge($minRevision)->fi() + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->orderBy('t1.revision desc') + ->beginIF(!empty($limit))->limit($limit)->fi() + ->fetchAll('revision'); + $commiters = $this->loadModel('user')->getCommiters(); + foreach($revisions as $revision) + { + $revision->comment = $this->replaceCommentLink($revision->comment); + $revision->committer = isset($commiters[$revision->committer]) ? $commiters[$revision->committer] : $revision->committer; + } + return $revisions; + } + + /** + * Get history. + * + * @param int $repoID + * @param array $revisions + * @access public + * @return array + */ + public function getHistory($repoID, $revisions) + { + return $this->dao->select('DISTINCT t1.*')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->andWhere('t1.revision')->in($revisions) + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->fetchAll('revision'); + } + + /** + * Get review. + * + * @param int $repoID + * @param string $entry + * @param string $revision + * @access public + * @return array + */ + public function getReview($repoID, $entry, $revision) + { + $reviews = array(); + $bugs = $this->dao->select('t1.*, t2.realname')->from(TABLE_BUG)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2') + ->on('t1.openedBy = t2.account') + ->where('t1.repo')->eq($repoID) + ->andWhere('t1.entry')->eq($entry) + ->andWhere('t1.v2')->eq($revision) + ->andWhere('t1.deleted')->eq(0) + ->fetchAll('id'); + $comments = $this->dao->select('t1.*, t2.realname')->from(TABLE_ACTION)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2') + ->on('t1.actor = t2.account') + ->where('t1.objectType')->eq('bug') + ->andWhere('t1.objectID')->in(array_keys($bugs)) + ->andWhere('t1.action')->eq('commented') + ->fetchGroup('objectID', 'id'); + foreach($bugs as $bug) + { + if(common::hasPriv('bug', 'edit')) $bug->edit = true; + if(common::hasPriv('bug', 'delete')) $bug->delete = true; + $lines = explode(',', trim($bug->lines, ',')); + $line = $lines[0]; + $reviews[$line]['bugs'][$bug->id] = $bug; + + if(isset($comments[$bug->id])) + { + foreach($comments[$bug->id] as $key => $comment) + { + if($comment->actor == $this->app->user->account) $comment->edit = true; + } + $reviews[$line]['comments'] = $comments; + } + } + + return $reviews; + } + + /** + * Get git revisionName. + * + * @param string $revision + * @param int $commit + * @access public + * @return string + */ + public function getGitRevisionName($revision, $commit) + { + if(empty($commit)) return substr($revision, 0, 10); + return substr($revision, 0, 10) . ' (' . $commit . ') '; + } + + /** + * create + * + * @access public + * @return int + */ + public function create() + { + $this->checkConnection(); + $data = fixer::input('post')->skipSpecial('path,client,account,password')->get(); + $data->acl = empty($data->acl) ? '' : json_encode($data->acl); + if(empty($data->client)) $data->client = 'svn'; + + if($data->SCM == 'Subversion') + { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($data); + $info = $scm->info(''); + $data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/'); + if($data->prefix) $data->prefix = '/' . $data->prefix; + } + + if($data->encrypt == 'base64') $data->password = base64_encode($data->password); + $this->dao->insert(TABLE_REPO)->data($data)->exec(); + return $this->dao->lastInsertID(); + } + + /** + * Save settings. + * + * @param int $repoID + * @access public + * @return bool + */ + public function saveSettings($repoID) + { + $this->checkConnection(); + $data = fixer::input('post')->skipSpecial('path,client,account,password')->get(); + $data->acl = empty($data->acl) ? '' : json_encode($data->acl); + + if(empty($data->client)) $data->client = 'svn'; + $repo = $this->getRepoByID($repoID); + $data->prefix = $repo->prefix; + if($data->SCM == 'Subversion' and $data->path != $repo->path) + { + $scm = $this->app->loadClass('scm'); + $scm->setEngine($data); + $info = $scm->info(''); + $data->prefix = empty($info->root) ? '' : trim(str_ireplace($info->root, '', str_replace('\\', '/', $data->path)), '/'); + if($data->prefix) $data->prefix = '/' . $data->prefix; + } + elseif($data->SCM != $repo->SCM and $data->SCM == 'Git') + { + $data->prefix = ''; + } + + if($data->path != $repo->path) $data->synced = 0; + if($data->encrypt == 'base64') $data->password = base64_encode($data->password); + $this->dao->update(TABLE_REPO)->data($data)->where('id')->eq($repoID)->exec(); + if($repo->path != $data->path) + { + $this->dao->delete()->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->exec(); + $this->dao->delete()->from(TABLE_REPOFILES)->where('repo')->eq($repoID)->exec(); + return false; + } + return true; + } + + /** + * Save commit. + * + * @param int $repoID + * @param array $logs + * @param int $version + * @param string $branch + * @access public + * @return int + */ + public function saveCommit($repoID, $logs, $version, $branch = '') + { + $count = 0; + if(empty($logs)) return $count; + + foreach($logs['commits'] as $i => $commit) + { + $existsRevision = $this->dao->select('id,revision')->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->andWhere('revision')->eq($commit->revision)->fetch(); + if($existsRevision) + { + if($branch) $this->dao->replace(TABLE_REPOBRANCH)->set('repo')->eq($repoID)->set('revision')->eq($existsRevision->id)->set('branch')->eq($branch)->exec(); + continue; + } + + $commit->repo = $repoID; + $commit->commit = $version; + $commit->comment = htmlspecialchars($commit->comment); + $this->dao->insert(TABLE_REPOHISTORY)->data($commit)->exec(); + if(!dao::isError()) + { + $commitID = $this->dao->lastInsertID(); + if($branch) $this->dao->replace(TABLE_REPOBRANCH)->set('repo')->eq($repoID)->set('revision')->eq($commitID)->set('branch')->eq($branch)->exec(); + foreach($logs['files'][$i] as $file) + { + $parentPath = dirname($file->path); + + $file->parent = $parentPath == '\\' ? '/' : $parentPath; + $file->revision = $commitID; + $file->repo = $repoID; + $this->dao->insert(TABLE_REPOFILES)->data($file)->exec(); + } + $revisionPairs[$commit->revision] = $commit->revision; + $version++; + $count++; + } + else + { + dao::getError(); + } + } + return $count; + } + + /** + * Save exists log branch. + * + * @param int $repoID + * @param string $branch + * @access public + * @return void + */ + public function saveExistsLogBranch($repoID, $branch) + { + $lastBranchLog = $this->dao->select('t1.time')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->andWhere('t2.branch')->eq($branch) + ->orderBy('time') + ->limit(1) + ->fetch(); + $stmt = $this->dao->select('*')->from(TABLE_REPOHISTORY)->where('repo')->eq($repoID)->andWhere('time')->lt($lastBranchLog->time)->query(); + while($log = $stmt->fetch()) + { + $this->dao->REPLACE(TABLE_REPOBRANCH)->set('repo')->eq($repoID)->set('revision')->eq($log->id)->set('branch')->eq($branch)->exec(); + } + } + + /** + * Update commit count. + * + * @param int $repoID + * @param int $count + * @access public + * @return void + */ + public function updateCommitCount($repoID, $count) + { + return $this->dao->update(TABLE_REPO)->set('commits')->eq($count)->where('id')->eq($repoID)->exec(); + } + + /** + * Update latest commit. + * + * @param object $repo + * @access public + * @return void + */ + public function updateLatestCommit($repo) + { + $repoID = $repo->id; + $latestInDB = $this->getLatestComment($repoID); + $version = empty($latestInDB) ? 1 : $latestInDB->commit + 1; + + $scm = $this->app->loadClass('scm'); + $scm->setEngine($repo); + $commitCount = $scm->getCommitCount(empty($latestInDB) ? 0 : $latestInDB->commit, empty($latestInDB) ? 0 : $latestInDB->revision); + if($commitCount >= $version) + { + $revision = 'HEAD'; + $logs = $scm->getCommits($revision, $commitCount - $version + 1, $this->cookie->repoBranch); + $logs['commits'] = array_reverse($logs['commits'], true); + + $commitCount = $this->saveCommit($repoID, $logs, $version, $this->cookie->repoBranch); + if($repo->SCM == 'Git' and empty($latestInDB)) $this->fixCommit($repo->id); + $this->updateCommitCount($repoID, $commitCount); + } + $this->dao->update(TABLE_REPO)->set('lastSync')->eq(helper::now())->where('id')->eq($repoID)->exec(); + } + + /** + * Update comment. + * + * @param int $commentID + * @param string $comment + * @access public + * @return string + */ + public function updateComment($commentID, $comment) + { + $this->dao->update(TABLE_ACTION)->set('comment')->eq($comment)->where('id')->eq($commentID)->exec(); + return $comment; + } + + /** + * Delete comment. + * + * @param int $commentID + * @access public + * @return void + */ + public function deleteComment($commentID) + { + return $this->dao->delete()->from(TABLE_ACTION)->where('id')->eq($commentID)->exec(); + } + + /** + * Get pre and next revision. + * + * @param object $repo + * @param string $entry + * @param string $revision + * @param string $fileType + * @param string $method + * + * @access public + * @return object + */ + public function getPreAndNext($repo, $entry, $revision = 'HEAD', $fileType = 'dir', $method = 'view') + { + $entry = ltrim($entry, '/'); + $entry = $repo->prefix . '/' . $entry; + $repoID = $repo->id; + + if($method == 'view') + { + $revisions = $this->dao->select('DISTINCT t1.revision,t1.commit')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOFILES)->alias('t2')->on('t1.id=t2.revision') + ->leftJoin(TABLE_REPOBRANCH)->alias('t3')->on('t1.id=t3.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($this->cookie->repoBranch)->andWhere('t3.branch')->eq($this->cookie->repoBranch)->fi() + ->andWhere('t2.path')->eq("$entry") + ->orderBy('commit desc') + ->fetchPairs(); + } + else + { + $revisions = $this->dao->select('DISTINCT t1.revision,t1.commit')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOFILES)->alias('t2')->on('t1.id=t2.revision') + ->leftJoin(TABLE_REPOBRANCH)->alias('t3')->on('t1.id=t3.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($this->cookie->repoBranch)->andWhere('t3.branch')->eq($this->cookie->repoBranch)->fi() + ->beginIF($entry == '/')->andWhere('t2.revision = t1.id')->fi() + ->beginIF($fileType == 'dir' && $entry != '/') + ->andWhere('t2.parent', true)->like(rtrim($entry, '/') . "/%") + ->orWhere('t2.parent')->eq(rtrim($entry, '/')) + ->markRight(1) + ->fi() + ->beginIF($fileType == 'file' && $entry != '/')->andWhere('t2.path')->eq($entry)->fi() + ->orderBy('commit desc') + ->fetchPairs(); + } + + $preRevision = false; + $preAndNext = new stdclass(); + $preAndNext->pre = ''; + $preAndNext->next = ''; + foreach($revisions as $version => $commit) + { + /* Get next object. */ + if($preRevision === true) + { + $preAndNext->next = $version; + break; + } + + /* Get pre object. */ + if($revision == $version) + { + if($preRevision) $preAndNext->pre = $preRevision; + $preRevision = true; + } + if($preRevision !== true) $preRevision = $version; + } + return $preAndNext; + } + + /** + * Create link for repo + * + * @param string $method + * @param string $params + * @param string $pathParams + * @param string $viewType + * @param bool $onlybody + * @access public + * @return string + */ + public function createLink($method, $params = '', $pathParams = '', $viewType = '', $onlybody = false) + { + $link = helper::createLink('repo', $method, $params, $viewType, $onlybody); + if(empty($pathParams)) return $link; + + $link .= strpos($link, '?') === false ? '?' : '&'; + $link .= $pathParams; + return $link; + } + + /** + * Set back session/ + * + * @param string $type + * @param bool $withOtherModule + * @access public + * @return void + */ + public function setBackSession($type = 'list', $withOtherModule = false) + { + $backKey = 'repo' . ucfirst(strtolower($type)); + session_start(); + $uri = $this->app->getURI(true); + if(!empty($_GET) and $this->config->requestType == 'PATH_INFO') $uri .= "?" . http_build_query($_GET); + $_SESSION[$backKey] = $uri; + if($type == 'list') unset($_SESSION['repoView']); + if($withOtherModule) + { + $this->session->set('bugList', $uri); + $this->session->set('taskList', $uri); + } + session_write_close(); + } + + /** + * Set repo branch. + * + * @param string $branch + * @access public + * @return void + */ + public function setRepoBranch($branch) + { + setcookie("repoBranch", $branch, 0, $this->config->webRoot); + $_COOKIE['repoBranch'] = $branch; + } + + /** + * Mark synced status. + * + * @param int $repoID + * @access public + * @return void + */ + public function markSynced($repoID) + { + $this->fixCommit($repoID); + $this->dao->update(TABLE_REPO)->set('synced')->eq(1)->where('id')->eq($repoID)->exec(); + } + + /** + * Fix commit. + * + * @param int $repoID + * @access public + * @return void + */ + public function fixCommit($repoID) + { + $stmt = $this->dao->select('DISTINCT t1.id')->from(TABLE_REPOHISTORY)->alias('t1') + ->leftJoin(TABLE_REPOBRANCH)->alias('t2')->on('t1.id=t2.revision') + ->where('t1.repo')->eq($repoID) + ->beginIF($this->cookie->repoBranch)->andWhere('t2.branch')->eq($this->cookie->repoBranch)->fi() + ->orderBy('time') + ->query(); + + $i = 1; + while($repoHistory = $stmt->fetch()) + { + $this->dao->update(TABLE_REPOHISTORY)->set('`commit`')->eq($i)->where('id')->eq($repoHistory->id)->exec(); + $i++; + } + } + + /** + * Encode repo path. + * + * @param string $path + * @access public + * @return string + */ + public function encodePath($path = '') + { + return helper::safe64Encode(urlencode($path)); + } + + /** + * Decode repo path. + * + * @param string $path + * @access public + * @return string + */ + public function decodePath($path = '') + { + return trim(urldecode(helper::safe64Decode($path)), '/'); + } + + /** + * Check content is binary. + * + * @param string $content + * @param string $suffix + * @access public + * @return bool + */ + public function isBinary($content, $suffix = '') + { + if(strpos($this->config->repo->binary, "|$suffix|") !== false) return true; + + $blk = substr($content, 0, 512); + return ( + false || + substr_count($blk, "^\r\n")/512 > 0.3 || + substr_count($blk, "^ -~")/512 > 0.3 || + substr_count($blk, "\x00") > 0 + ); + } + + /** + * Check connection + * + * @access public + * @return void + */ + public function checkConnection() + { + if(empty($_POST)) return false; + $scm = $this->post->SCM; + $client = $this->post->client; + $account = $this->post->account; + $password = $this->post->password; + $encoding = strtoupper($this->post->encoding); + $path = $this->post->path; + if($encoding != 'UTF8' and $encoding != 'UTF-8') $path = helper::convertEncoding($path, 'utf-8', $encoding); + + if($scm == 'Subversion') + { + $path = '"' . $path . '"'; + if(stripos($path, 'https://') === 1 or stripos($path, 'svn://') === 1) + { + $ssh = true; + $remote = true; + $command = "$client info --username $account --password $password --non-interactive --trust-server-cert-failures=cn-mismatch --trust-server-cert --no-auth-cache $path 2>&1"; + } + else if(stripos($path, 'file://') === 1) + { + $ssh = false; + $remote = false; + $command = "$client info --non-interactive --no-auth-cache $path 2>&1"; + } + else + { + $ssh = false; + $remote = true; + $command = "$client info --username $account --password $password --non-interactive --no-auth-cache $path 2>&1"; + } + exec($command, $output, $result); + if($result) + { + $versionCommand = "$client --version --quiet 2>&1"; + exec($versionCommand, $versionOutput, $versionResult); + if($versionResult) + { + $message = sprintf($this->lang->repo->error->output, $versionCommand, $versionResult, join("\n", $versionOutput)); + echo $message; + die(js::alert($this->lang->repo->error->cmd . '\n' . str_replace(array("\n", "'"), array('\n', '"'), $message))); + } + if($ssh and version_compare(end($versionOutput), '1.6', '<')) die(js::alert($this->lang->repo->error->version)); + $message = sprintf($this->lang->repo->error->output, $command, $result, join("\n", $output)); + echo $message; + if(stripos($message, 'Expected FS format between') !== false and strpos($message, 'found format') !== false) die(js::alert($this->lang->repo->error->clientVersion)); + if(preg_match('/[^\:\/\\A-Za-z0-9_\-\'\"]/', $path)) die(js::alert($this->lang->repo->error->encoding . '\n' . str_replace(array("\n", "'"), array('\n', '"'), $message))); + die(js::alert($this->lang->repo->error->connect . '\n' . str_replace(array("\n", "'"), array('\n', '"'), $message))); + } + } + elseif($scm == 'Git') + { + if(!chdir($path)) + { + if(!is_dir($path)) die(js::alert(sprintf($this->lang->repo->error->noFile, $path))); + if(!is_executable($path)) die(js::alert(sprintf($this->lang->repo->error->noPriv, $path))); + die(js::alert($this->lang->repo->error->path)); + } + + $command = "$client tag 2>&1"; + exec($command, $output, $result); + if($result) + { + echo sprintf($this->lang->repo->error->output, $command, $result, join("\n", $output)); + die(js::alert($this->lang->repo->error->connect)); + } + } + return true; + } + + /** + * Replace comment link. + * + * @param string $comment + * @access public + * @return string + */ + public function replaceCommentLink($comment) + { + $stories = array(); + $tasks = array(); + $bugs = array(); + $commonReg = "(?:\s){0,}((?:#|:|:){0,})([0-9, ]{1,})"; + $taskReg = '/task' . $commonReg . '/i'; + $storyReg = '/story' . $commonReg . '/i'; + $bugReg = '/bug' . $commonReg . '/i'; + if(preg_match_all($storyReg, $comment, $result)) + { + $storyLinks = $this->addLink($result, 'story'); + foreach($storyLinks as $search => $replace) $comment = str_replace($search, $replace, $comment); + } + if(preg_match_all($taskReg, $comment, $result)) + { + $taskLinks = $this->addLink($result, 'task'); + foreach($taskLinks as $search => $replace) $comment = str_replace($search, $replace, $comment); + } + if(preg_match_all($bugReg, $comment, $result)) + { + $bugLinks = $this->addLink($result, 'bug'); + foreach($bugLinks as $search => $replace) $comment = str_replace($search, $replace, $comment); + } + return $comment; + } + + /** + * Add link. + * + * @param string $matches + * @param string $method + * @access public + * @return string + */ + public function addLink($matches, $method) + { + if(empty($matches)) return null; + $replaceLines = array(); + foreach($matches[2] as $key => $ids) + { + $spit = strpos($ids, ',') !== false ? ',' : ' '; + $ids = explode(' ', str_replace(',', ' ', $ids)); + $links = $method . " " . $matches[1][$key]; + foreach($ids as $id) + { + if($id) $links .= html::a(helper::createLink($method, 'view', "id=$id"), $id) . $spit; + } + $replaceLines[$matches[0][$key]] = rtrim($links, $spit); + } + return $replaceLines; + } +} diff --git a/module/repo/view/ajaxsidelogs.html.php b/module/repo/view/ajaxsidelogs.html.php new file mode 100644 index 0000000000..316ce92eba --- /dev/null +++ b/module/repo/view/ajaxsidelogs.html.php @@ -0,0 +1,88 @@ + + * @package repo + * @version $Id$ + * @link http://www.zentao.net + */ +?> +repo->encodePath(empty($path) ? '/' : $path); +if(isset($entry)) $pathInfo .= '&type=file'; +?> +
revision?>" /> + + + + repo->createLink('revision', "repoID=$repoID&revision={$log->revision}" . $pathInfo), $repo->SCM == 'Git' ? substr($log->revision, 0, 10) : $log->revision);?> + SCM == 'Git'):?> + commit?> + + time, 0, 10);?> + committer;?> + comment, ENT_QUOTES);?> + comment?> + + + + + +
+ diff --git a/module/repo/view/create.html.php b/module/repo/view/create.html.php new file mode 100644 index 0000000000..00f824f7fa --- /dev/null +++ b/module/repo/view/create.html.php @@ -0,0 +1,83 @@ + + + + + +
+
+
+

repo->create;?>

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
repo->SCM;?>repo->scmList, 'Subversion', "class='form-control'");?>
repo->name;?>
repo->path;?>repo->example->path;?>
repo->encoding;?>
repo->client;?>repo->example->client;?>
repo->account;?>
repo->password;?> +
+ + + repo->encryptList, 'base64', "class='form-control'");?> +
+
repo->acl;?> +
+ repo->group?> + +
+
+ repo->user?> + +
+
+ + +
+
+
+
+ diff --git a/module/repo/view/diff.html.php b/module/repo/view/diff.html.php new file mode 100644 index 0000000000..c158d36596 --- /dev/null +++ b/module/repo/view/diff.html.php @@ -0,0 +1,174 @@ + + + + + + + +
+
+
+
+ repo->viewDiffList['inline'], "id='inline'", $arrange == 'inline' ? 'active btn btn-sm' : 'btn btn-sm')?> + repo->viewDiffList['appose'], "id='appose'", $arrange == 'appose' ? 'active btn btn-sm' : 'btn btn-sm')?> +
+
+
+ repo->createLink('download', "repoID=$repoID&path=&fromRevison=$oldRevision&toRevision=$newRevision&type=path", "path=" . $this->repo->encodePath($entry)), $lang->repo->downloadDiff, 'hiddenwin', "class='btn btn-sm btn-download'");?> +
+ repo->encodingList, $encoding, $lang->repo->encoding) . "", "data-toggle='dropdown'", 'btn dropdown-toggle btn-sm')?> + +
+
+
+ +
+
+ +
+ + + contents)) continue;?> + contents as $content):?> + oldStartLine; + $newCurrentLine = $content->newStartLine; + ?> + + + + + + + + + + + + lines as $line):?> + + + + + + + + lines as $line):?> + + type == 'old') + { + $oldlc = $line->oldlc; + $newlc = ''; + if(isset($content->new[$oldlc])) + { + $newlc = $line->oldlc; + $line->type = 'custom'; + } + } + else + { + $oldlc = $line->oldlc; + $newlc = $line->newlc; + if(!isset($content->new[$newlc])) continue; + } + ?> + + + + + old[$oldlc])) unset($content->old[$oldlc]); + if(isset($content->new[$newlc])) unset($content->new[$newlc]); + ?> + + + + +
fileName;?>
......
type != 'new') echo $line->oldlc?>type != 'old') echo $line->newlc?>line = $repo->SCM == 'Subversion' ? htmlspecialchars($line->line) : $line->line; + echo $line->type == 'old' ? preg_replace('/^\-/', '–', $line->line) : ($line->type == 'new' ? $line->line : ' ' . $line->line); + ?>
type?> type == 'custom') echo "line-old"?> code'>old[$oldlc])) $content->old[$oldlc] = ''; + $content->old[$oldlc] = $repo->SCM == 'Subversion' ? htmlspecialchars($content->old[$oldlc]) : $content->old[$oldlc]; + if(!empty($oldlc)) echo $line->type != 'all' ? preg_replace('/^\-/', '–', $content->old[$oldlc]) : ' ' . $content->old[$oldlc]; + ?>type?> type == 'custom') echo "line-new"?> code'>new[$newlc])) $content->new[$newlc] = ''; + $content->new[$newlc] = $repo->SCM == 'Subversion' ? htmlspecialchars($content->new[$newlc]) : $content->new[$newlc]; + if(!empty($newlc)) echo $line->type != 'all' ? $content->new[$newlc] : ' ' . $content->new[$newlc]; + ?>
+
+ +
+ + + diff --git a/module/repo/view/log.html.php b/module/repo/view/log.html.php new file mode 100644 index 0000000000..ecdfaa78c9 --- /dev/null +++ b/module/repo/view/log.html.php @@ -0,0 +1,76 @@ + + + + +
+ +
revision?>" /> + +
+ + repo->createLink('revision', "repoID=$repoID&revision=" . $log->revision), substr($log->revision, 0, 10));?> + time;?> + committer;?> + comment;?> + + + + + + + + diff --git a/module/repo/view/revision.html.php b/module/repo/view/revision.html.php new file mode 100644 index 0000000000..96c97749bb --- /dev/null +++ b/module/repo/view/revision.html.php @@ -0,0 +1,91 @@ + +app->getURI(true); +session_write_close(); +$pathInfo = empty($path) ? '' : '&root=' . $this->repo->encodePath($path); +$preDir = empty($parentDir) ? $pathInfo : '&path=' . $this->repo->encodePath($parentDir); +$typeInfo = $type == 'file' ? '&type=file' : ''; +?> + + + +
+
+
+
+
+ repo->changes;?> +
repo->createLink('diff', "repoID=$repoID&entry=&fromRevision=$oldRevision&toRevision=$revision"), $lang->repo->diffAll);?>
+
+
+ + $change):?> + + + + + +
" . $change['action'] . ' ' . $path?>
+
+
+
+
+
+
+
+
repo->info?>
+
+ + + + + + + + + + SCM == 'Git'):?> + + + + + + + + + + + + + +
repo->committer?>committer?>
repo->revisionA?>revision?>
repo->commit?>commit?>
repo->comment?>comment?>
repo->time?>time?>
+
+
+
+
+
+
+ +
+ diff --git a/module/repo/view/settings.html.php b/module/repo/view/settings.html.php new file mode 100644 index 0000000000..be13609557 --- /dev/null +++ b/module/repo/view/settings.html.php @@ -0,0 +1,87 @@ + + + + + +
+
+
+

repo->settings;?>

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
repo->SCM;?>repo->scmList, $repo->SCM, "class='form-control'");?>
repo->name;?>name, "class='form-control'");?>
repo->path;?>path, "class='form-control'")?>repo->example->path;?>
repo->encoding;?>encoding, "class='form-control'")?>repo->example->encoding;?>
repo->client;?>client, "class='form-control'")?>repo->example->client;?>
repo->account;?> + account, "class='form-control' autocomplete='off'");?> + +
repo->password;?> +
+ password, "class='form-control'");?> + + repo->encryptList, $repo->encrypt, "class='form-control'");?> +
+
repo->acl;?> +
+ repo->group?> + acl->groups) ? '' : join(',', $repo->acl->groups), "class='form-control chosen' multiple")?> +
+
+ repo->user?> + acl->users) ? '' : join(',', $repo->acl->users), "class='form-control chosen' multiple")?> +
+
+ + +
+
+
+
+ diff --git a/module/repo/view/showsynccomment.html.php b/module/repo/view/showsynccomment.html.php new file mode 100644 index 0000000000..18a959cabe --- /dev/null +++ b/module/repo/view/showsynccomment.html.php @@ -0,0 +1,45 @@ + + * @package repo + * @version $Id$ + * @link http://www.zentao.net + */ +?> + +
+
+
+ +
+

repo->notice->syncing;?>

+
+

repo->notice->syncedCount?>

+
+
+
+
+ + diff --git a/module/repo/view/view.html.php b/module/repo/view/view.html.php new file mode 100644 index 0000000000..0e011db65a --- /dev/null +++ b/module/repo/view/view.html.php @@ -0,0 +1,98 @@ + +repo->encodePath($entry); +$version = " $revisionName"; +?> + + + + + +
+ +
+
+
+
+
+ repo->images, "|$suffix|") === false):?> + repo->createLink('download', "repoID=$repoID&path=&fromRevision=$revision", "path=$encodePath"), html::icon('download-alt') . $lang->repo->download, 'hiddenwin', "class='btn btn-sm btn-primary'"); + ?> + +
+ repo->encodingList, $encoding, $lang->repo->encoding) . "", "id='encoding' data-toggle='dropdown'", 'btn btn-sm btn-primary dropdown-toggle')?> + +
+
+
+ repo->images, "|$suffix|") !== false):?> +
+ +
repo->createLink('download', "repoID=$repoID&path=&fromRevision=$revision", "path=" . $this->repo->encodePath($entry)), "", 'hiddenwin', "title='{$lang->repo->download}'"); ?>
+ +
+ +
+
+ + + + +
+ + +
+ +
+ + diff --git a/www/js/misc/highlight/export.html b/www/js/misc/highlight/export.html new file mode 100755 index 0000000000..86ac892848 --- /dev/null +++ b/www/js/misc/highlight/export.html @@ -0,0 +1,87 @@ + + + + + + + + Highlited code export + + + + + + + + + + + + + + + + + + +
Write a code snippetGet HTML to paste anywhere (for actual styles and colors see sample.css)
+ + + + + +
+
+
+ Export script: Vladimir Gubarkov
+ Highlighting: highlight.js +
+ + diff --git a/www/js/misc/highlight/highlight.pack.js b/www/js/misc/highlight/highlight.pack.js new file mode 100644 index 0000000000..796caed43c --- /dev/null +++ b/www/js/misc/highlight/highlight.pack.js @@ -0,0 +1 @@ +/*highlight v8.4 https://highlightjs.org*/!function(e){"undefined"!=typeof exports?e(exports):(window.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return window.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(//gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){var n=(e.className+" "+(e.parentNode?e.parentNode.className:"")).split(/\s+/);return n=n.map(function(e){return e.replace(/^lang(uage)?-/,"")}),n.filter(function(e){return N(e)||/no(-?)highlight/.test(e)})[0]}function o(e,n){var t={};for(var r in e)t[r]=e[r];if(n)for(var r in n)t[r]=n[r];return t}function i(e){var n=[];return function r(e,a){for(var o=e.firstChild;o;o=o.nextSibling)3==o.nodeType?a+=o.nodeValue.length:1==o.nodeType&&(n.push({event:"start",offset:a,node:o}),a=r(o,a),t(o).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:o}));return a}(e,0),n}function c(e,r,a){function o(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset"}function c(e){l+=""}function u(e){("start"==e.event?i:c)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=o();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(c);do u(g.splice(0,1)[0]),g=o();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(i)}else"start"==g[0].event?f.push(g[0].node):f.pop(),u(g.splice(0,1)[0])}return l+n(a.substr(s))}function u(e){function n(e){return e&&e.source||e}function t(t,r){return RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var c={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");c[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):Object.keys(a.k).forEach(function(e){u(e,a.k[e])}),a.k=c}a.lR=t(a.l||/\b[A-Za-z0-9_]+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function s(e,t,a,o){function i(e,n){for(var t=0;t";return o+=e+'">',o+n+i}function d(){if(!w.k)return n(y);var e="",t=0;w.lR.lastIndex=0;for(var r=w.lR.exec(y);r;){e+=n(y.substr(t,r.index-t));var a=g(w,r);a?(B+=a[1],e+=p(a[0],n(r[0]))):e+=n(r[0]),t=w.lR.lastIndex,r=w.lR.exec(y)}return e+n(y.substr(t))}function h(){if(w.sL&&!R[w.sL])return n(y);var e=w.sL?s(w.sL,y,!0,L[w.sL]):l(y);return w.r>0&&(B+=e.r),"continuous"==w.subLanguageMode&&(L[w.sL]=e.top),p(e.language,e.value,!1,!0)}function v(){return void 0!==w.sL?h():d()}function b(e,t){var r=e.cN?p(e.cN,"",!0):"";e.rB?(M+=r,y=""):e.eB?(M+=n(t)+r,y=""):(M+=r,y=t),w=Object.create(e,{parent:{value:w}})}function m(e,t){if(y+=e,void 0===t)return M+=v(),0;var r=i(t,w);if(r)return M+=v(),b(r,t),r.rB?0:t.length;var a=c(w,t);if(a){var o=w;o.rE||o.eE||(y+=t),M+=v();do w.cN&&(M+=""),B+=w.r,w=w.parent;while(w!=a.parent);return o.eE&&(M+=n(t)),y="",a.starts&&b(a.starts,""),o.rE?0:t.length}if(f(t,w))throw new Error('Illegal lexeme "'+t+'" for mode "'+(w.cN||"")+'"');return y+=t,t.length||1}var x=N(e);if(!x)throw new Error('Unknown language: "'+e+'"');u(x);for(var w=o||x,L={},M="",k=w;k!=x;k=k.parent)k.cN&&(M=p(k.cN,"",!0)+M);var y="",B=0;try{for(var C,j,I=0;;){if(w.t.lastIndex=I,C=w.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}m(t.substr(I));for(var k=w;k.parent;k=k.parent)k.cN&&(M+="");return{r:B,value:M,language:e,top:w}}catch(A){if(-1!=A.message.indexOf("Illegal"))return{r:0,value:n(t)};throw A}}function l(e,t){t=t||E.languages||Object.keys(R);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(N(n)){var t=s(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function f(e){return E.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,E.tabReplace)})),E.useBR&&(e=e.replace(/\n/g,"
")),e}function g(e,n,t){var r=n?x[n]:t,a=[e.trim()];return e.match(/(\s|^)hljs(\s|$)/)||a.push("hljs"),r&&a.push(r),a.join(" ").trim()}function p(e){var n=a(e);if(!/no(-?)highlight/.test(n)){var t;E.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e;var r=t.textContent,o=n?s(n,r,!0):l(r),u=i(t);if(u.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(u,i(p),r)}o.value=f(o.value),e.innerHTML=o.value,e.className=g(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){E=o(E,e)}function h(){if(!h.called){h.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",h,!1),addEventListener("load",h,!1)}function b(n,t){var r=R[n]=t(e);r.aliases&&r.aliases.forEach(function(e){x[e]=n})}function m(){return Object.keys(R)}function N(e){return R[e]||R[x[e]]}var E={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},R={},x={};return e.highlight=s,e.highlightAuto=l,e.fixMarkup=f,e.highlightBlock=p,e.configure=d,e.initHighlighting=h,e.initHighlightingOnLoad=v,e.registerLanguage=b,e.listLanguages=m,e.getLanguage=N,e.inherit=o,e.IR="[a-zA-Z][a-zA-Z0-9_]*",e.UIR="[a-zA-Z_][a-zA-Z0-9_]*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/},e.CLCM={cN:"comment",b:"//",e:"$",c:[e.PWM]},e.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[e.PWM]},e.HCM={cN:"comment",b:"#",e:"$",c:[e.PWM]},e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("xml",function(){var t="[A-Za-z0-9\\._:-]+",e={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"},c={eW:!0,i:/]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[c],starts:{e:"",rE:!0,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[c],starts:{e:"",rE:!0,sL:"javascript"}},e,{cN:"pi",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"",c:[{cN:"title",b:/[^ \/><\n\t]+/,r:0},c]}]}});hljs.registerLanguage("cpp",function(t){var i={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex _Complex _Imaginaryintmax_t uintmax_t int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_tint_least8_t uint_least8_t int_least16_t uint_least16_t int_least32_t uint_least32_tint_least64_t uint_least64_t int_fast8_t uint_fast8_t int_fast16_t uint_fast16_t int_fast32_tuint_fast32_t int_fast64_t uint_fast64_t intptr_t uintptr_t atomic_bool atomic_char atomic_scharatomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llongatomic_ullong atomic_wchar_t atomic_char16_t atomic_char32_t atomic_intmax_t atomic_uintmax_tatomic_intptr_t atomic_uintptr_t atomic_size_t atomic_ptrdiff_t atomic_int_least8_t atomic_int_least16_tatomic_int_least32_t atomic_int_least64_t atomic_uint_least8_t atomic_uint_least16_t atomic_uint_least32_tatomic_uint_least64_t atomic_int_fast8_t atomic_int_fast16_t atomic_int_fast32_t atomic_int_fast64_tatomic_uint_fast8_t atomic_uint_fast16_t atomic_uint_fast32_t atomic_uint_fast64_t",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf"};return{aliases:["c","h","c++","h++"],k:i,i:""]',k:"include",i:"\\n"},t.CLCM]},{cN:"stl_container",b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:i,c:["self"]},{b:t.IR+"::"},{bK:"new throw return",r:0},{cN:"function",b:"("+t.IR+"\\s+)+"+t.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:t.IR+"\\s*\\(",rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:i,r:0,c:[t.CBCM]},t.CLCM,t.CBCM]}]}});hljs.registerLanguage("haskell",function(e){var i={cN:"comment",v:[{b:"--",e:"$"},{b:"{-",e:"-}",c:["self"]}]},c={cN:"pragma",b:"{-#",e:"#-}"},a={cN:"preprocessor",b:"^#",e:"$"},n={cN:"type",b:"\\b[A-Z][\\w']*",r:0},l={cN:"container",b:"\\(",e:"\\)",i:'"',c:[c,i,a,{cN:"type",b:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TM,{b:"[_a-z][\\w']*"})]},t={cN:"container",b:"{",e:"}",c:l.c};return{aliases:["hs"],k:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",c:[{cN:"module",b:"\\bmodule\\b",e:"where",k:"module where",c:[l,i],i:"\\W\\.|;"},{cN:"import",b:"\\bimport\\b",e:"$",k:"import|0 qualified as hiding",c:[l,i],i:"\\W\\.|;"},{cN:"class",b:"^(\\s*)?(class|instance)\\b",e:"where",k:"class family instance where",c:[n,l,i]},{cN:"typedef",b:"\\b(data|(new)?type)\\b",e:"$",k:"data family type newtype deriving",c:[c,i,n,l,t]},{cN:"default",bK:"default",e:"$",c:[n,l,i]},{cN:"infix",bK:"infix infixl infixr",e:"$",c:[e.CNM,i]},{cN:"foreign",b:"\\bforeign\\b",e:"$",k:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",c:[n,e.QSM,i]},{cN:"shebang",b:"#!\\/usr\\/bin\\/env runhaskell",e:"$"},c,i,a,e.QSM,e.CNM,n,e.inherit(e.TM,{b:"^[_a-z][\\w']*"}),{b:"->|<-"}]}});hljs.registerLanguage("rsl",function(e){return{k:{keyword:"float color point normal vector matrix while for if do return else break extern continue",built_in:"abs acos ambient area asin atan atmosphere attribute calculatenormal ceil cellnoise clamp comp concat cos degrees depth Deriv diffuse distance Du Dv environment exp faceforward filterstep floor format fresnel incident length lightsource log match max min mod noise normalize ntransform opposite option phong pnoise pow printf ptlined radians random reflect refract renderinfo round setcomp setxcomp setycomp setzcomp shadow sign sin smoothstep specular specularbrdf spline sqrt step tan texture textureinfo trace transform vtransform xcomp ycomp zcomp"},i:" ",r:10},{cN:"comment",b:"%",e:"$"},{cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},e.ASM,e.QSM,{cN:"constant",b:"\\?(::)?([A-Z]\\w*(::)?)+"},{cN:"arrow",b:"->"},{cN:"ok",b:"ok"},{cN:"exclamation_mark",b:"!"},{cN:"function_or_atom",b:"(\\b[a-z'][a-zA-Z0-9_']*:[a-z'][a-zA-Z0-9_']*)|(\\b[a-z'][a-zA-Z0-9_']*)",r:0},{cN:"variable",b:"[A-Z][a-zA-Z0-9_']*",r:0}]}});hljs.registerLanguage("avrasm",function(r){return{cI:!0,l:"\\.?"+r.IR,k:{keyword:"adc add adiw and andi asr bclr bld brbc brbs brcc brcs break breq brge brhc brhs brid brie brlo brlt brmi brne brpl brsh brtc brts brvc brvs bset bst call cbi cbr clc clh cli cln clr cls clt clv clz com cp cpc cpi cpse dec eicall eijmp elpm eor fmul fmuls fmulsu icall ijmp in inc jmp ld ldd ldi lds lpm lsl lsr mov movw mul muls mulsu neg nop or ori out pop push rcall ret reti rjmp rol ror sbc sbr sbrc sbrs sec seh sbi sbci sbic sbis sbiw sei sen ser ses set sev sez sleep spm st std sts sub subi swap tst wdr",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r30 r31 x|0 xh xl y|0 yh yl z|0 zh zl ucsr1c udr1 ucsr1a ucsr1b ubrr1l ubrr1h ucsr0c ubrr0h tccr3c tccr3a tccr3b tcnt3h tcnt3l ocr3ah ocr3al ocr3bh ocr3bl ocr3ch ocr3cl icr3h icr3l etimsk etifr tccr1c ocr1ch ocr1cl twcr twdr twar twsr twbr osccal xmcra xmcrb eicra spmcsr spmcr portg ddrg ping portf ddrf sreg sph spl xdiv rampz eicrb eimsk gimsk gicr eifr gifr timsk tifr mcucr mcucsr tccr0 tcnt0 ocr0 assr tccr1a tccr1b tcnt1h tcnt1l ocr1ah ocr1al ocr1bh ocr1bl icr1h icr1l tccr2 tcnt2 ocr2 ocdr wdtcr sfior eearh eearl eedr eecr porta ddra pina portb ddrb pinb portc ddrc pinc portd ddrd pind spdr spsr spcr udr0 ucsr0a ucsr0b ubrr0l acsr admux adcsr adch adcl porte ddre pine pinf",preprocessor:".byte .cseg .db .def .device .dseg .dw .endmacro .equ .eseg .exit .include .list .listmac .macro .nolist .org .set"},c:[r.CBCM,{cN:"comment",b:";",e:"$",r:0},r.CNM,r.BNM,{cN:"number",b:"\\b(\\$[a-zA-Z0-9]+|0o[0-7]+)"},r.QSM,{cN:"string",b:"'",e:"[^\\\\]'",i:"[^\\\\][^']"},{cN:"label",b:"^[A-Za-z0-9_.$]+:"},{cN:"preprocessor",b:"#",e:"$"},{cN:"localvars",b:"@[0-9]+"}]}});hljs.registerLanguage("delphi",function(e){var r="exports register file shl array record property for mod while set ally label uses raise not stored class safecall var interface or private static exit index inherited to else stdcall override shr asm far resourcestring finalization packed virtual out and protected library do xorwrite goto near function end div overload object unit begin string on inline repeat until destructor write message program with read initialization except default nil if case cdecl in downto threadvar of try pascal const external constructor type public then implementation finally published procedure",t={cN:"comment",v:[{b:/\{/,e:/\}/,r:0},{b:/\(\*/,e:/\*\)/,r:10}]},i={cN:"string",b:/'/,e:/'/,c:[{b:/''/}]},c={cN:"string",b:/(#\d+)+/},o={b:e.IR+"\\s*=\\s*class\\s*\\(",rB:!0,c:[e.TM]},n={cN:"function",bK:"function constructor destructor procedure",e:/[:;]/,k:"function constructor|10 destructor|10 procedure|10",c:[e.TM,{cN:"params",b:/\(/,e:/\)/,k:r,c:[i,c]},t]};return{cI:!0,k:r,i:/"|\$[G-Zg-z]|\/\*|<\/|\|/,c:[t,e.CLCM,i,c,e.NM,o,n]}});hljs.registerLanguage("less",function(e){var r="[\\w-]+",t="("+r+"|@{"+r+"})+",a=[],c=[],n=function(e){return{cN:"string",b:"~?"+e+".*?"+e}},i=function(e,r,t){return{cN:e,b:r,r:t}},s=function(r,t,a){return e.inherit({cN:r,b:t+"\\(",e:"\\(",rB:!0,eE:!0,r:0},a)},b={b:"\\(",e:"\\)",c:c,r:0};c.push(e.CLCM,e.CBCM,n("'"),n('"'),e.CSSNM,i("hexcolor","#[0-9A-Fa-f]+\\b"),s("function","(url|data-uri)",{starts:{cN:"string",e:"[\\)\\n]",eE:!0}}),s("function",r),b,i("variable","@@?"+r,10),i("variable","@{"+r+"}"),i("built_in","~?`[^`]*?`"),{cN:"attribute",b:r+"\\s*:",e:":",rB:!0,eE:!0});var o=c.concat({b:"{",e:"}",c:a}),u={bK:"when",eW:!0,c:[{bK:"and not"}].concat(c)},C={cN:"attribute",b:t,e:":",eE:!0,c:[e.CLCM,e.CBCM],i:/\S/,starts:{e:"[;}]",rE:!0,c:c,i:"[<=$]"}},l={cN:"at_rule",b:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{e:"[;{}]",rE:!0,c:c,r:0}},d={cN:"variable",v:[{b:"@"+r+"\\s*:",r:15},{b:"@"+r}],starts:{e:"[;}]",rE:!0,c:o}},p={v:[{b:"[\\.#:&\\[]",e:"[;{}]"},{b:t+"[^;]*{",e:"{"}],rB:!0,rE:!0,i:"[<='$\"]",c:[e.CLCM,e.CBCM,u,i("keyword","all\\b"),i("variable","@{"+r+"}"),i("tag",t+"%?",0),i("id","#"+t),i("class","\\."+t,0),i("keyword","&",0),s("pseudo",":not"),s("keyword",":extend"),i("pseudo","::?"+t),{cN:"attr_selector",b:"\\[",e:"\\]"},{b:"\\(",e:"\\)",c:o},{b:"!important"}]};return a.push(e.CLCM,e.CBCM,l,d,p,C),{cI:!0,i:"[=>'/<($\"]",c:a}});hljs.registerLanguage("scala",function(e){var t={cN:"annotation",b:"@[A-Za-z]+"},a={cN:"string",b:'u?r?"""',e:'"""',r:10},r={cN:"symbol",b:"'\\w[\\w\\d_]*(?!')"},c={cN:"type",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},i={cN:"title",b:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,r:0},l={cN:"class",bK:"class object trait type",e:/[:={\[(\n;]/,c:[{cN:"keyword",bK:"extends with",r:10},i]},n={cN:"function",bK:"def val",e:/[:={\[(\n;]/,c:[i]};return{k:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},c:[e.CLCM,e.CBCM,a,e.QSM,r,c,n,l,e.CNM,t]}});hljs.registerLanguage("java",function(e){var a=e.UIR+"(<"+e.UIR+">)?",t="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private",c="(\\b(0b[01_]+)|\\b0[xX][a-fA-F0-9_]+|(\\b[\\d_]+(\\.[\\d_]*)?|\\.[\\d_]+)([eE][-+]?\\d+)?)[lLfF]?",r={cN:"number",b:c,r:0};return{aliases:["jsp"],k:t,i:/<\//,c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",r:0,c:[{cN:"javadoctag",b:"(^|\\s)@[A-Za-z]+"}]},e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return",r:0},{cN:"function",b:"("+a+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:t,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:t,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},r,{cN:"annotation",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("profile",function(e){return{c:[e.CNM,{cN:"built_in",b:"{",e:"}$",eB:!0,eE:!0,c:[e.ASM,e.QSM],r:0},{cN:"filename",b:"[a-zA-Z_][\\da-zA-Z_]+\\.[\\da-zA-Z_]{1,3}",e:":",eE:!0},{cN:"header",b:"(ncalls|tottime|cumtime)",e:"$",k:"ncalls tottime|10 cumtime|10 filename",r:10},{cN:"summary",b:"function calls",e:"$",c:[e.CNM],r:10},e.ASM,e.QSM,{cN:"function",b:"\\(",e:"\\)$",c:[e.UTM],r:0}]}});hljs.registerLanguage("objectivec",function(e){var t={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"NSString NSData NSDictionary CGRect CGPoint UIButton UILabel UITextView UIWebView MKMapView NSView NSViewController NSWindow NSWindowController NSSet NSUUID NSIndexSet UISegmentedControl NSObject UITableViewDelegate UITableViewDataSource NSThread UIActivityIndicator UITabbar UIToolBar UIBarButtonItem UIImageView NSAutoreleasePool UITableView BOOL NSInteger CGFloat NSException NSLog NSMutableString NSMutableArray NSMutableDictionary NSURL NSIndexPath CGSize UITableViewCell UIView UIViewController UINavigationBar UINavigationController UITabBarController UIPopoverController UIPopoverControllerDelegate UIImage NSNumber UISearchBar NSFetchedResultsController NSFetchedResultsChangeType UIScrollView UIScrollViewDelegate UIEdgeInsets UIColor UIFont UIApplication NSNotFound NSNotificationCenter NSNotification UILocalNotification NSBundle NSFileManager NSTimeInterval NSDate NSCalendar NSUserDefaults UIWindow NSRange NSArray NSError NSURLRequest NSURLConnection NSURLSession NSURLSessionDataTask NSURLSessionDownloadTask NSURLSessionUploadTask NSURLResponseUIInterfaceOrientation MPMoviePlayerController dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},o=/[a-zA-Z@][a-zA-Z0-9_]*/,a="@interface @class @protocol @implementation";return{aliases:["m","mm","objc","obj-c"],k:t,l:o,i:""}]}]},{cN:"class",b:"("+a.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:a,l:o,c:[e.UTM]},{cN:"variable",b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("ini",function(e){return{cI:!0,i:/\S/,c:[{cN:"comment",b:";",e:"$"},{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9\\[\\]_-]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:!0,k:"on off true false yes no",c:[e.QSM,e.NM],r:0}]}]}});hljs.registerLanguage("php",function(e){var c={cN:"variable",b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"preprocessor",b:/<\?(php)?|\?>/},a={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},n={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.CLCM,e.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"},i]},{cN:"comment",b:"__halt_compiler.+?;",eW:!0,k:"__halt_compiler",l:e.UIR},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[e.BE]},i,c,{b:/->+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,a,n]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},a,n]}});hljs.registerLanguage("matlab",function(e){var a=[e.CNM,{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]}],s={r:0,c:[{cN:"operator",b:/'['\.]*/}]};return{k:{keyword:"break case catch classdef continue else elseif end enumerated events for function global if methods otherwise parfor persistent properties return spmd switch try while",built_in:"sin sind sinh asin asind asinh cos cosd cosh acos acosd acosh tan tand tanh atan atand atan2 atanh sec secd sech asec asecd asech csc cscd csch acsc acscd acsch cot cotd coth acot acotd acoth hypot exp expm1 log log1p log10 log2 pow2 realpow reallog realsqrt sqrt nthroot nextpow2 abs angle complex conj imag real unwrap isreal cplxpair fix floor ceil round mod rem sign airy besselj bessely besselh besseli besselk beta betainc betaln ellipj ellipke erf erfc erfcx erfinv expint gamma gammainc gammaln psi legendre cross dot factor isprime primes gcd lcm rat rats perms nchoosek factorial cart2sph cart2pol pol2cart sph2cart hsv2rgb rgb2hsv zeros ones eye repmat rand randn linspace logspace freqspace meshgrid accumarray size length ndims numel disp isempty isequal isequalwithequalnans cat reshape diag blkdiag tril triu fliplr flipud flipdim rot90 find sub2ind ind2sub bsxfun ndgrid permute ipermute shiftdim circshift squeeze isscalar isvector ans eps realmax realmin pi i inf nan isnan isinf isfinite j why compan gallery hadamard hankel hilb invhilb magic pascal rosser toeplitz vander wilkinson"},i:'(//|"|#|/\\*|\\s+/\\w+)',c:[{cN:"function",bK:"function",e:"$",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)"},{cN:"params",b:"\\[",e:"\\]"}]},{b:/[a-zA-Z_][a-zA-Z_0-9]*'['\.]*/,rB:!0,r:0,c:[{b:/[a-zA-Z_][a-zA-Z_0-9]*/,r:0},s.c[0]]},{cN:"matrix",b:"\\[",e:"\\]",c:a,r:0,starts:s},{cN:"cell",b:"\\{",e:/\}/,c:a,r:0,i:/:/,starts:s},{b:/\)/,r:0,starts:s},{cN:"comment",b:"\\%",e:"$"}].concat(a)}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",reserved:"case default function var void with const let enum export import native __hasProp __extends __slice __bind __indexOf",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",t={cN:"subst",b:/#\{/,e:/}/,k:c},r=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,t]},{b:/"/,e:/"/,c:[e.BE,t]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[t,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{cN:"property",b:"@"+n},{b:"`",e:"`",eB:!0,eE:!0,sL:"javascript"}];t.c=r;var i=e.inherit(e.TM,{b:n}),s="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(r)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:r.concat([{cN:"comment",b:"###",e:"###",c:[e.PWM]},e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+s,e:"[-=]>",rB:!0,c:[i,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:s,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[i]},i]},{cN:"attribute",b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("xl",function(e){var t="ObjectLoader Animate MovieCredits Slides Filters Shading Materials LensFlare Mapping VLCAudioVideo StereoDecoder PointCloud NetworkAccess RemoteControl RegExp ChromaKey Snowfall NodeJS Speech Charts",o={keyword:"if then else do while until for loop import with is as where when by data constant",literal:"true false nil",type:"integer real text name boolean symbol infix prefix postfix block tree",built_in:"in mod rem and or xor not abs sign floor ceil sqrt sin cos tan asin acos atan exp expm1 log log2 log10 log1p pi at",module:t,id:"text_length text_range text_find text_replace contains page slide basic_slide title_slide title subtitle fade_in fade_out fade_at clear_color color line_color line_width texture_wrap texture_transform texture scale_?x scale_?y scale_?z? translate_?x translate_?y translate_?z? rotate_?x rotate_?y rotate_?z? rectangle circle ellipse sphere path line_to move_to quad_to curve_to theme background contents locally time mouse_?x mouse_?y mouse_buttons"},a={cN:"constant",b:"[A-Z][A-Z_0-9]+",r:0},r={cN:"variable",b:"([A-Z][a-z_0-9]+)+",r:0},i={cN:"id",b:"[a-z][a-z_0-9]+",r:0},l={cN:"string",b:'"',e:'"',i:"\\n"},n={cN:"string",b:"'",e:"'",i:"\\n"},s={cN:"string",b:"<<",e:">>"},c={cN:"number",b:"[0-9]+#[0-9A-Z_]+(\\.[0-9-A-Z_]+)?#?([Ee][+-]?[0-9]+)?",r:10},_={cN:"import",bK:"import",e:"$",k:{keyword:"import",module:t},r:0,c:[l]},d={cN:"function",b:"[a-z].*->"};return{aliases:["tao"],l:/[a-zA-Z][a-zA-Z0-9_?]*/,k:o,c:[e.CLCM,e.CBCM,l,n,s,d,_,a,r,i,c,e.NM]}});hljs.registerLanguage("actionscript",function(e){var a="[a-zA-Z_$][a-zA-Z0-9_$]*",c="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)",t={cN:"rest_arg",b:"[.]{3}",e:a,r:10};return{aliases:["as"],k:{keyword:"as break case catch class const continue default delete do dynamic each else extends final finally for function get if implements import in include instanceof interface internal is namespace native new override package private protected public return set static super switch this throw try typeof use var void while with",literal:"true false null undefined"},c:[e.ASM,e.QSM,e.CLCM,e.CBCM,e.CNM,{cN:"package",bK:"package",e:"{",c:[e.TM]},{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.TM]},{cN:"preprocessor",bK:"import include",e:";"},{cN:"function",bK:"function",e:"[{;]",eE:!0,i:"\\S",c:[e.TM,{cN:"params",b:"\\(",e:"\\)",c:[e.ASM,e.QSM,e.CLCM,e.CBCM,t]},{cN:"type",b:":",e:c,r:10}]}]}});hljs.registerLanguage("go",function(e){var t={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer",constant:"true false iota nil",typename:"bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{aliases:["golang"],k:t,i:"]/,c:[{cN:"operator",bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate savepoint release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup",e:/;/,eW:!0,k:{keyword:"abs absolute acos action add adddate addtime aes_decrypt aes_encrypt after aggregate all allocate alter analyze and any are as asc ascii asin assertion at atan atan2 atn2 authorization authors avg backup before begin benchmark between bin binlog bit_and bit_count bit_length bit_or bit_xor both by cache call cascade cascaded case cast catalog ceil ceiling chain change changed char_length character_length charindex charset check checksum checksum_agg choose close coalesce coercibility collate collation collationproperty column columns columns_updated commit compress concat concat_ws concurrent connect connection connection_id consistent constraint constraints continue contributors conv convert convert_tz corresponding cos cot count count_big crc32 create cross cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime data database databases datalength date_add date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts datetimeoffsetfromparts day dayname dayofmonth dayofweek dayofyear deallocate declare decode default deferrable deferred degrees delayed delete des_decrypt des_encrypt des_key_file desc describe descriptor diagnostics difference disconnect distinct distinctrow div do domain double drop dumpfile each else elt enclosed encode encrypt end end-exec engine engines eomonth errors escape escaped event eventdata events except exception exec execute exists exp explain export_set extended external extract fast fetch field fields find_in_set first first_value floor flush for force foreign format found found_rows from from_base64 from_days from_unixtime full function get get_format get_lock getdate getutcdate global go goto grant grants greatest group group_concat grouping grouping_id gtid_subset gtid_subtract handler having help hex high_priority hosts hour ident_current ident_incr ident_seed identified identity if ifnull ignore iif ilike immediate in index indicator inet6_aton inet6_ntoa inet_aton inet_ntoa infile initially inner innodb input insert install instr intersect into is is_free_lock is_ipv4 is_ipv4_compat is_ipv4_mapped is_not is_not_null is_used_lock isdate isnull isolation join key kill language last last_day last_insert_id last_value lcase lead leading least leaves left len lenght level like limit lines ln load load_file local localtime localtimestamp locate lock log log10 log2 logfile logs low_priority lower lpad ltrim make_set makedate maketime master master_pos_wait match matched max md5 medium merge microsecond mid min minute mod mode module month monthname mutex name_const names national natural nchar next no no_write_to_binlog not now nullif nvarchar oct octet_length of old_password on only open optimize option optionally or ord order outer outfile output pad parse partial partition password patindex percent_rank percentile_cont percentile_disc period_add period_diff pi plugin position pow power pragma precision prepare preserve primary prior privileges procedure procedure_analyze processlist profile profiles public publishingservername purge quarter query quick quote quotename radians rand read references regexp relative relaylog release release_lock rename repair repeat replace replicate reset restore restrict return returns reverse revoke right rlike rollback rollup round row row_count rows rpad rtrim savepoint schema scroll sec_to_time second section select serializable server session session_user set sha sha1 sha2 share show sign sin size slave sleep smalldatetimefromparts snapshot some soname soundex sounds_like space sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sql_variant_property sqlstate sqrt square start starting status std stddev stddev_pop stddev_samp stdev stdevp stop str str_to_date straight_join strcmp string stuff subdate substr substring subtime subtring_index sum switchoffset sysdate sysdatetime sysdatetimeoffset system_user sysutcdatetime table tables tablespace tan temporary terminated tertiary_weights then time time_format time_to_sec timediff timefromparts timestamp timestampadd timestampdiff timezone_hour timezone_minute to to_base64 to_days to_seconds todatetimeoffset trailing transaction translation trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse ucase uncompress uncompressed_length unhex unicode uninstall union unique unix_timestamp unknown unlock update upgrade upped upper usage use user user_resources using utc_date utc_time utc_timestamp uuid uuid_short validate_password_strength value values var var_pop var_samp variables variance varp version view warnings week weekday weekofyear weight_string when whenever where with work write xml xor year yearweek zon",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int integer interval number numeric real serial smallint varchar varying int8 serial8 text"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("tex",function(){var c={cN:"command",b:"\\\\[a-zA-Zа-яА-я]+[\\*]?"},e={cN:"command",b:"\\\\[^a-zA-Zа-яА-я0-9]"},m={cN:"special",b:"[{}\\[\\]\\&#~]",r:0};return{c:[{b:"\\\\[a-zA-Zа-яА-я]+[\\*]? *= *-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",rB:!0,c:[c,e,{cN:"number",b:" *=",e:"-?\\d*\\.?\\d+(pt|pc|mm|cm|in|dd|cc|ex|em)?",eB:!0}],r:10},c,e,m,{cN:"formula",b:"\\$\\$",e:"\\$\\$",c:[c,e,m],r:0},{cN:"formula",b:"\\$",e:"\\$",c:[c,e,m],r:0},{cN:"comment",b:"%",e:"$",r:0}]}});hljs.registerLanguage("dos",function(e){var r={cN:"comment",b:/@?rem\b/,e:/$/,r:10},t={cN:"label",b:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)",r:0};return{aliases:["bat","cmd"],cI:!0,k:{flow:"if else goto for in do call exit not exist errorlevel defined",operator:"equ neq lss leq gtr geq",keyword:"shift cd dir echo setlocal endlocal set pause copy",stream:"prn nul lpt3 lpt2 lpt1 con com4 com3 com2 com1 aux",winutils:"ping net ipconfig taskkill xcopy ren del",built_in:"append assoc at attrib break cacls cd chcp chdir chkdsk chkntfs cls cmd color comp compact convert date dir diskcomp diskcopy doskey erase fs find findstr format ftype graftabl help keyb label md mkdir mode more move path pause print popd pushd promt rd recover rem rename replace restore rmdir shiftsort start subst time title tree type ver verify vol"},c:[{cN:"envvar",b:/%%[^ ]|%[^ ]+?%|![^ ]+?!/},{cN:"function",b:t.b,e:"goto:eof",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),r]},{cN:"number",b:"\\b\\d+",r:0},r]}});hljs.registerLanguage("vbscript",function(e){return{aliases:["vbs"],cI:!0,k:{keyword:"call class const dim do loop erase execute executeglobal exit for each next function if then else on error option explicit new private property let get public randomize redim rem select case set stop sub while wend with end to elseif is or xor and not class_initialize class_terminate default preserve in me byval byref step resume goto",built_in:"lcase month vartype instrrev ubound setlocale getobject rgb getref string weekdayname rnd dateadd monthname now day minute isarray cbool round formatcurrency conversions csng timevalue second year space abs clng timeserial fixs len asc isempty maths dateserial atn timer isobject filter weekday datevalue ccur isdate instr datediff formatdatetime replace isnull right sgn array snumeric log cdbl hex chr lbound msgbox ucase getlocale cos cdate cbyte rtrim join hour oct typename trim strcomp int createobject loadpicture tan formatnumber mid scriptenginebuildversion scriptengine split scriptengineminorversion cint sin datepart ltrim sqr scriptenginemajorversion time derived eval date formatpercent exp inputbox left ascw chrw regexp server response request cstr err",literal:"true false null nothing empty"},i:"//",c:[e.inherit(e.QSM,{c:[{b:'""'}]}),{cN:"comment",b:/'/,e:/$/,r:0},e.CNM]}});hljs.registerLanguage("vhdl",function(e){return{cI:!0,k:{keyword:"abs access after alias all and architecture array assert attribute begin block body buffer bus case component configuration constant context cover disconnect downto default else elsif end entity exit fairness file for force function generate generic group guarded if impure in inertial inout is label library linkage literal loop map mod nand new next nor not null of on open or others out package port postponed procedure process property protected pure range record register reject release rem report restrict restrict_guarantee return rol ror select sequence severity shared signal sla sll sra srl strong subtype then to transport type unaffected units until use variable vmode vprop vunit wait when while with xnor xor",typename:"boolean bit character severity_level integer time delay_length natural positive string bit_vector file_open_kind file_open_status std_ulogic std_ulogic_vector std_logic std_logic_vector unsigned signed boolean_vector integer_vector real_vector time_vector"},i:"{",c:[e.CBCM,{cN:"comment",b:"--",e:"$"},e.QSM,e.CNM,{cN:"literal",b:"'(U|X|0|1|Z|W|L|H|-)'",c:[e.BE]},{cN:"attribute",b:"'[A-Za-z](_?[A-Za-z0-9])*",c:[e.BE]}]}});hljs.registerLanguage("http",function(){return{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:!0}}]}});hljs.registerLanguage("lisp",function(e){var b="[a-zA-Z_\\-\\+\\*\\/\\<\\=\\>\\&\\#][a-zA-Z0-9_\\-\\+\\*\\/\\<\\=\\>\\&\\#!]*",c="\\|[^]*?\\|",r="(\\-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s)(\\+|\\-)?\\d+)?",t={cN:"shebang",b:"^#!",e:"$"},a={cN:"literal",b:"\\b(t{1}|nil)\\b"},i={cN:"number",v:[{b:r,r:0},{b:"#b[0-1]+(/[0-1]+)?"},{b:"#o[0-7]+(/[0-7]+)?"},{b:"#x[0-9a-f]+(/[0-9a-f]+)?"},{b:"#c\\("+r+" +"+r,e:"\\)"}]},l=e.inherit(e.QSM,{i:null}),n={cN:"comment",b:";",e:"$",r:0},N={cN:"variable",b:"\\*",e:"\\*"},d={cN:"keyword",b:"[:&]"+b},o={b:c},u={b:"\\(",e:"\\)",c:["self",a,l,i]},s={cN:"quoted",c:[i,l,N,d,u],v:[{b:"['`]\\(",e:"\\)"},{b:"\\(quote ",e:"\\)",k:"quote"},{b:"'"+c}]},f={cN:"quoted",b:"'"+b},v={cN:"list",b:"\\(",e:"\\)"},g={eW:!0,r:0};return v.c=[{cN:"keyword",v:[{b:b},{b:c}]},g],g.c=[s,f,v,a,i,l,n,N,d,o],{i:/\S/,c:[i,t,a,l,n,s,f,v]}});hljs.registerLanguage("erlang",function(e){var r="[a-z'][a-zA-Z0-9_']*",c="("+r+":"+r+"|"+r+")",a={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"},n={cN:"comment",b:"%",e:"$"},b={cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0},i={b:"fun\\s+"+r+"/\\d+"},o={b:c+"\\(",e:"\\)",rB:!0,r:0,c:[{cN:"function_name",b:c,r:0},{b:"\\(",e:"\\)",eW:!0,rE:!0,r:0}]},d={cN:"tuple",b:"{",e:"}",r:0},t={cN:"variable",b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0},l={cN:"variable",b:"[A-Z][a-zA-Z0-9_]*",r:0},f={b:"#"+e.UIR,r:0,rB:!0,c:[{cN:"record_name",b:"#"+e.UIR,r:0},{b:"{",e:"}",r:0}]},s={bK:"fun receive if try case",e:"end",k:a};s.c=[n,i,e.inherit(e.ASM,{cN:""}),s,o,e.QSM,b,d,t,l,f];var u=[n,i,s,o,e.QSM,b,d,t,l,f];o.c[1].c=u,d.c=u,f.c[1].c=u;var v={cN:"params",b:"\\(",e:"\\)",c:u};return{aliases:["erl"],k:a,i:"(",rB:!0,i:"\\(|#|//|/\\*|\\\\|:|;",c:[v,e.inherit(e.TM,{b:r})],starts:{e:";|\\.",k:a,c:u}},n,{cN:"pp",b:"^-",e:"\\.",r:0,eE:!0,rB:!0,l:"-"+e.IR,k:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec",c:[v]},b,e.QSM,f,t,l,d,{b:/\.$/}]}});hljs.registerLanguage("makefile",function(e){var a={cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]};return{aliases:["mk","mak"],c:[e.HCM,{b:/^\w+\s*\W*=/,rB:!0,r:0,starts:{cN:"constant",e:/\s*\W*=/,eE:!0,starts:{e:/$/,r:0,c:[a]}}},{cN:"title",b:/^[\w]+:\s*$/},{cN:"phony",b:/^\.PHONY:/,e:/$/,k:".PHONY",l:/[\.\w]+/},{b:/^\t+/,e:/$/,r:0,c:[e.QSM,a]}]}});hljs.registerLanguage("d",function(e){var r={keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},t="(0|[1-9][\\d_]*)",a="(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)",i="0[bB][01_]+",n="([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)",c="0[xX]"+n,_="([eE][+-]?"+a+")",d="("+a+"(\\.\\d*|"+_+")|\\d+\\."+a+a+"|\\."+t+_+"?)",o="(0[xX]("+n+"\\."+n+"|\\.?"+n+")[pP][+-]?"+a+")",s="("+t+"|"+i+"|"+c+")",l="("+o+"|"+d+")",u="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",b={cN:"number",b:"\\b"+s+"(L|u|U|Lu|LU|uL|UL)?",r:0},f={cN:"number",b:"\\b("+l+"([fF]|L|i|[fF]i|Li)?|"+s+"(i|[fF]i|Li))",r:0},g={cN:"string",b:"'("+u+"|.)",e:"'",i:"."},h={b:u,r:0},p={cN:"string",b:'"',c:[h],e:'"[cwd]?'},N={cN:"string",b:'[rq]"',e:'"[cwd]?',r:5},m={cN:"string",b:"`",e:"`[cwd]?"},w={cN:"string",b:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',r:10},A={cN:"string",b:'q"\\{',e:'\\}"'},F={cN:"shebang",b:"^#!",e:"$",r:5},y={cN:"preprocessor",b:"#(line)",e:"$",r:5},L={cN:"keyword",b:"@[a-zA-Z_][a-zA-Z_\\d]*"},v={cN:"comment",b:"\\/\\+",c:["self"],e:"\\+\\/",r:10};return{l:e.UIR,k:r,c:[e.CLCM,e.CBCM,v,w,p,N,m,A,f,b,g,F,y,L]}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={cN:"variable",v:[{b:/\$\d/},{b:/[\$\%\@](\^\w\b|#\w+(\:\:\w+)*|{\w+}|\w+(\:\:\w*)*)/},{b:/[\$\%\@][^\s\w{]/,r:0}]},o={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5},i=[e.BE,r,n],c=[n,e.HCM,o,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:!0},s,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,o,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",r:5},{cN:"operator",b:"-\\w\\b",r:0}];return r.c=c,s.c=c,{aliases:["pl"],k:t,c:c}});hljs.registerLanguage("mel",function(e){return{k:"int float string vector matrix if else switch case default while do for in break continue global proc return about abs addAttr addAttributeEditorNodeHelp addDynamic addNewShelfTab addPP addPanelCategory addPrefixToName advanceToNextDrivenKey affectedNet affects aimConstraint air alias aliasAttr align alignCtx alignCurve alignSurface allViewFit ambientLight angle angleBetween animCone animCurveEditor animDisplay animView annotate appendStringArray applicationName applyAttrPreset applyTake arcLenDimContext arcLengthDimension arclen arrayMapper art3dPaintCtx artAttrCtx artAttrPaintVertexCtx artAttrSkinPaintCtx artAttrTool artBuildPaintMenu artFluidAttrCtx artPuttyCtx artSelectCtx artSetPaintCtx artUserPaintCtx assignCommand assignInputDevice assignViewportFactories attachCurve attachDeviceAttr attachSurface attrColorSliderGrp attrCompatibility attrControlGrp attrEnumOptionMenu attrEnumOptionMenuGrp attrFieldGrp attrFieldSliderGrp attrNavigationControlGrp attrPresetEditWin attributeExists attributeInfo attributeMenu attributeQuery autoKeyframe autoPlace bakeClip bakeFluidShading bakePartialHistory bakeResults bakeSimulation basename basenameEx batchRender bessel bevel bevelPlus binMembership bindSkin blend2 blendShape blendShapeEditor blendShapePanel blendTwoAttr blindDataType boneLattice boundary boxDollyCtx boxZoomCtx bufferCurve buildBookmarkMenu buildKeyframeMenu button buttonManip CBG cacheFile cacheFileCombine cacheFileMerge cacheFileTrack camera cameraView canCreateManip canvas capitalizeString catch catchQuiet ceil changeSubdivComponentDisplayLevel changeSubdivRegion channelBox character characterMap characterOutlineEditor characterize chdir checkBox checkBoxGrp checkDefaultRenderGlobals choice circle circularFillet clamp clear clearCache clip clipEditor clipEditorCurrentTimeCtx clipSchedule clipSchedulerOutliner clipTrimBefore closeCurve closeSurface cluster cmdFileOutput cmdScrollFieldExecuter cmdScrollFieldReporter cmdShell coarsenSubdivSelectionList collision color colorAtPoint colorEditor colorIndex colorIndexSliderGrp colorSliderButtonGrp colorSliderGrp columnLayout commandEcho commandLine commandPort compactHairSystem componentEditor compositingInterop computePolysetVolume condition cone confirmDialog connectAttr connectControl connectDynamic connectJoint connectionInfo constrain constrainValue constructionHistory container containsMultibyte contextInfo control convertFromOldLayers convertIffToPsd convertLightmap convertSolidTx convertTessellation convertUnit copyArray copyFlexor copyKey copySkinWeights cos cpButton cpCache cpClothSet cpCollision cpConstraint cpConvClothToMesh cpForces cpGetSolverAttr cpPanel cpProperty cpRigidCollisionFilter cpSeam cpSetEdit cpSetSolverAttr cpSolver cpSolverTypes cpTool cpUpdateClothUVs createDisplayLayer createDrawCtx createEditor createLayeredPsdFile createMotionField createNewShelf createNode createRenderLayer createSubdivRegion cross crossProduct ctxAbort ctxCompletion ctxEditMode ctxTraverse currentCtx currentTime currentTimeCtx currentUnit curve curveAddPtCtx curveCVCtx curveEPCtx curveEditorCtx curveIntersect curveMoveEPCtx curveOnSurface curveSketchCtx cutKey cycleCheck cylinder dagPose date defaultLightListCheckBox defaultNavigation defineDataServer defineVirtualDevice deformer deg_to_rad delete deleteAttr deleteShadingGroupsAndMaterials deleteShelfTab deleteUI deleteUnusedBrushes delrandstr detachCurve detachDeviceAttr detachSurface deviceEditor devicePanel dgInfo dgdirty dgeval dgtimer dimWhen directKeyCtx directionalLight dirmap dirname disable disconnectAttr disconnectJoint diskCache displacementToPoly displayAffected displayColor displayCull displayLevelOfDetail displayPref displayRGBColor displaySmoothness displayStats displayString displaySurface distanceDimContext distanceDimension doBlur dolly dollyCtx dopeSheetEditor dot dotProduct doubleProfileBirailSurface drag dragAttrContext draggerContext dropoffLocator duplicate duplicateCurve duplicateSurface dynCache dynControl dynExport dynExpression dynGlobals dynPaintEditor dynParticleCtx dynPref dynRelEdPanel dynRelEditor dynamicLoad editAttrLimits editDisplayLayerGlobals editDisplayLayerMembers editRenderLayerAdjustment editRenderLayerGlobals editRenderLayerMembers editor editorTemplate effector emit emitter enableDevice encodeString endString endsWith env equivalent equivalentTol erf error eval evalDeferred evalEcho event exactWorldBoundingBox exclusiveLightCheckBox exec executeForEachObject exists exp expression expressionEditorListen extendCurve extendSurface extrude fcheck fclose feof fflush fgetline fgetword file fileBrowserDialog fileDialog fileExtension fileInfo filetest filletCurve filter filterCurve filterExpand filterStudioImport findAllIntersections findAnimCurves findKeyframe findMenuItem findRelatedSkinCluster finder firstParentOf fitBspline flexor floatEq floatField floatFieldGrp floatScrollBar floatSlider floatSlider2 floatSliderButtonGrp floatSliderGrp floor flow fluidCacheInfo fluidEmitter fluidVoxelInfo flushUndo fmod fontDialog fopen formLayout format fprint frameLayout fread freeFormFillet frewind fromNativePath fwrite gamma gauss geometryConstraint getApplicationVersionAsFloat getAttr getClassification getDefaultBrush getFileList getFluidAttr getInputDeviceRange getMayaPanelTypes getModifiers getPanel getParticleAttr getPluginResource getenv getpid glRender glRenderEditor globalStitch gmatch goal gotoBindPose grabColor gradientControl gradientControlNoAttr graphDollyCtx graphSelectContext graphTrackCtx gravity grid gridLayout group groupObjectsByName HfAddAttractorToAS HfAssignAS HfBuildEqualMap HfBuildFurFiles HfBuildFurImages HfCancelAFR HfConnectASToHF HfCreateAttractor HfDeleteAS HfEditAS HfPerformCreateAS HfRemoveAttractorFromAS HfSelectAttached HfSelectAttractors HfUnAssignAS hardenPointCurve hardware hardwareRenderPanel headsUpDisplay headsUpMessage help helpLine hermite hide hilite hitTest hotBox hotkey hotkeyCheck hsv_to_rgb hudButton hudSlider hudSliderButton hwReflectionMap hwRender hwRenderLoad hyperGraph hyperPanel hyperShade hypot iconTextButton iconTextCheckBox iconTextRadioButton iconTextRadioCollection iconTextScrollList iconTextStaticLabel ikHandle ikHandleCtx ikHandleDisplayScale ikSolver ikSplineHandleCtx ikSystem ikSystemInfo ikfkDisplayMethod illustratorCurves image imfPlugins inheritTransform insertJoint insertJointCtx insertKeyCtx insertKnotCurve insertKnotSurface instance instanceable instancer intField intFieldGrp intScrollBar intSlider intSliderGrp interToUI internalVar intersect iprEngine isAnimCurve isConnected isDirty isParentOf isSameObject isTrue isValidObjectName isValidString isValidUiName isolateSelect itemFilter itemFilterAttr itemFilterRender itemFilterType joint jointCluster jointCtx jointDisplayScale jointLattice keyTangent keyframe keyframeOutliner keyframeRegionCurrentTimeCtx keyframeRegionDirectKeyCtx keyframeRegionDollyCtx keyframeRegionInsertKeyCtx keyframeRegionMoveKeyCtx keyframeRegionScaleKeyCtx keyframeRegionSelectKeyCtx keyframeRegionSetKeyCtx keyframeRegionTrackCtx keyframeStats lassoContext lattice latticeDeformKeyCtx launch launchImageEditor layerButton layeredShaderPort layeredTexturePort layout layoutDialog lightList lightListEditor lightListPanel lightlink lineIntersection linearPrecision linstep listAnimatable listAttr listCameras listConnections listDeviceAttachments listHistory listInputDeviceAxes listInputDeviceButtons listInputDevices listMenuAnnotation listNodeTypes listPanelCategories listRelatives listSets listTransforms listUnselected listerEditor loadFluid loadNewShelf loadPlugin loadPluginLanguageResources loadPrefObjects localizedPanelLabel lockNode loft log longNameOf lookThru ls lsThroughFilter lsType lsUI Mayatomr mag makeIdentity makeLive makePaintable makeRoll makeSingleSurface makeTubeOn makebot manipMoveContext manipMoveLimitsCtx manipOptions manipRotateContext manipRotateLimitsCtx manipScaleContext manipScaleLimitsCtx marker match max memory menu menuBarLayout menuEditor menuItem menuItemToShelf menuSet menuSetPref messageLine min minimizeApp mirrorJoint modelCurrentTimeCtx modelEditor modelPanel mouse movIn movOut move moveIKtoFK moveKeyCtx moveVertexAlongDirection multiProfileBirailSurface mute nParticle nameCommand nameField namespace namespaceInfo newPanelItems newton nodeCast nodeIconButton nodeOutliner nodePreset nodeType noise nonLinear normalConstraint normalize nurbsBoolean nurbsCopyUVSet nurbsCube nurbsEditUV nurbsPlane nurbsSelect nurbsSquare nurbsToPoly nurbsToPolygonsPref nurbsToSubdiv nurbsToSubdivPref nurbsUVSet nurbsViewDirectionVector objExists objectCenter objectLayer objectType objectTypeUI obsoleteProc oceanNurbsPreviewPlane offsetCurve offsetCurveOnSurface offsetSurface openGLExtension openMayaPref optionMenu optionMenuGrp optionVar orbit orbitCtx orientConstraint outlinerEditor outlinerPanel overrideModifier paintEffectsDisplay pairBlend palettePort paneLayout panel panelConfiguration panelHistory paramDimContext paramDimension paramLocator parent parentConstraint particle particleExists particleInstancer particleRenderInfo partition pasteKey pathAnimation pause pclose percent performanceOptions pfxstrokes pickWalk picture pixelMove planarSrf plane play playbackOptions playblast plugAttr plugNode pluginInfo pluginResourceUtil pointConstraint pointCurveConstraint pointLight pointMatrixMult pointOnCurve pointOnSurface pointPosition poleVectorConstraint polyAppend polyAppendFacetCtx polyAppendVertex polyAutoProjection polyAverageNormal polyAverageVertex polyBevel polyBlendColor polyBlindData polyBoolOp polyBridgeEdge polyCacheMonitor polyCheck polyChipOff polyClipboard polyCloseBorder polyCollapseEdge polyCollapseFacet polyColorBlindData polyColorDel polyColorPerVertex polyColorSet polyCompare polyCone polyCopyUV polyCrease polyCreaseCtx polyCreateFacet polyCreateFacetCtx polyCube polyCut polyCutCtx polyCylinder polyCylindricalProjection polyDelEdge polyDelFacet polyDelVertex polyDuplicateAndConnect polyDuplicateEdge polyEditUV polyEditUVShell polyEvaluate polyExtrudeEdge polyExtrudeFacet polyExtrudeVertex polyFlipEdge polyFlipUV polyForceUV polyGeoSampler polyHelix polyInfo polyInstallAction polyLayoutUV polyListComponentConversion polyMapCut polyMapDel polyMapSew polyMapSewMove polyMergeEdge polyMergeEdgeCtx polyMergeFacet polyMergeFacetCtx polyMergeUV polyMergeVertex polyMirrorFace polyMoveEdge polyMoveFacet polyMoveFacetUV polyMoveUV polyMoveVertex polyNormal polyNormalPerVertex polyNormalizeUV polyOptUvs polyOptions polyOutput polyPipe polyPlanarProjection polyPlane polyPlatonicSolid polyPoke polyPrimitive polyPrism polyProjection polyPyramid polyQuad polyQueryBlindData polyReduce polySelect polySelectConstraint polySelectConstraintMonitor polySelectCtx polySelectEditCtx polySeparate polySetToFaceNormal polySewEdge polyShortestPathCtx polySmooth polySoftEdge polySphere polySphericalProjection polySplit polySplitCtx polySplitEdge polySplitRing polySplitVertex polyStraightenUVBorder polySubdivideEdge polySubdivideFacet polyToSubdiv polyTorus polyTransfer polyTriangulate polyUVSet polyUnite polyWedgeFace popen popupMenu pose pow preloadRefEd print progressBar progressWindow projFileViewer projectCurve projectTangent projectionContext projectionManip promptDialog propModCtx propMove psdChannelOutliner psdEditTextureFile psdExport psdTextureFile putenv pwd python querySubdiv quit rad_to_deg radial radioButton radioButtonGrp radioCollection radioMenuItemCollection rampColorPort rand randomizeFollicles randstate rangeControl readTake rebuildCurve rebuildSurface recordAttr recordDevice redo reference referenceEdit referenceQuery refineSubdivSelectionList refresh refreshAE registerPluginResource rehash reloadImage removeJoint removeMultiInstance removePanelCategory rename renameAttr renameSelectionList renameUI render renderGlobalsNode renderInfo renderLayerButton renderLayerParent renderLayerPostProcess renderLayerUnparent renderManip renderPartition renderQualityNode renderSettings renderThumbnailUpdate renderWindowEditor renderWindowSelectContext renderer reorder reorderDeformers requires reroot resampleFluid resetAE resetPfxToPolyCamera resetTool resolutionNode retarget reverseCurve reverseSurface revolve rgb_to_hsv rigidBody rigidSolver roll rollCtx rootOf rot rotate rotationInterpolation roundConstantRadius rowColumnLayout rowLayout runTimeCommand runup sampleImage saveAllShelves saveAttrPreset saveFluid saveImage saveInitialState saveMenu savePrefObjects savePrefs saveShelf saveToolSettings scale scaleBrushBrightness scaleComponents scaleConstraint scaleKey scaleKeyCtx sceneEditor sceneUIReplacement scmh scriptCtx scriptEditorInfo scriptJob scriptNode scriptTable scriptToShelf scriptedPanel scriptedPanelType scrollField scrollLayout sculpt searchPathArray seed selLoadSettings select selectContext selectCurveCV selectKey selectKeyCtx selectKeyframeRegionCtx selectMode selectPref selectPriority selectType selectedNodes selectionConnection separator setAttr setAttrEnumResource setAttrMapping setAttrNiceNameResource setConstraintRestPosition setDefaultShadingGroup setDrivenKeyframe setDynamic setEditCtx setEditor setFluidAttr setFocus setInfinity setInputDeviceMapping setKeyCtx setKeyPath setKeyframe setKeyframeBlendshapeTargetWts setMenuMode setNodeNiceNameResource setNodeTypeFlag setParent setParticleAttr setPfxToPolyCamera setPluginResource setProject setStampDensity setStartupMessage setState setToolTo setUITemplate setXformManip sets shadingConnection shadingGeometryRelCtx shadingLightRelCtx shadingNetworkCompare shadingNode shapeCompare shelfButton shelfLayout shelfTabLayout shellField shortNameOf showHelp showHidden showManipCtx showSelectionInTitle showShadingGroupAttrEditor showWindow sign simplify sin singleProfileBirailSurface size sizeBytes skinCluster skinPercent smoothCurve smoothTangentSurface smoothstep snap2to2 snapKey snapMode snapTogetherCtx snapshot soft softMod softModCtx sort sound soundControl source spaceLocator sphere sphrand spotLight spotLightPreviewPort spreadSheetEditor spring sqrt squareSurface srtContext stackTrace startString startsWith stitchAndExplodeShell stitchSurface stitchSurfacePoints strcmp stringArrayCatenate stringArrayContains stringArrayCount stringArrayInsertAtIndex stringArrayIntersector stringArrayRemove stringArrayRemoveAtIndex stringArrayRemoveDuplicates stringArrayRemoveExact stringArrayToString stringToStringArray strip stripPrefixFromName stroke subdAutoProjection subdCleanTopology subdCollapse subdDuplicateAndConnect subdEditUV subdListComponentConversion subdMapCut subdMapSewMove subdMatchTopology subdMirror subdToBlind subdToPoly subdTransferUVsToCache subdiv subdivCrease subdivDisplaySmoothness substitute substituteAllString substituteGeometry substring surface surfaceSampler surfaceShaderList swatchDisplayPort switchTable symbolButton symbolCheckBox sysFile system tabLayout tan tangentConstraint texLatticeDeformContext texManipContext texMoveContext texMoveUVShellContext texRotateContext texScaleContext texSelectContext texSelectShortestPathCtx texSmudgeUVContext texWinToolCtx text textCurves textField textFieldButtonGrp textFieldGrp textManip textScrollList textToShelf textureDisplacePlane textureHairColor texturePlacementContext textureWindow threadCount threePointArcCtx timeControl timePort timerX toNativePath toggle toggleAxis toggleWindowVisibility tokenize tokenizeList tolerance tolower toolButton toolCollection toolDropped toolHasOptions toolPropertyWindow torus toupper trace track trackCtx transferAttributes transformCompare transformLimits translator trim trunc truncateFluidCache truncateHairCache tumble tumbleCtx turbulence twoPointArcCtx uiRes uiTemplate unassignInputDevice undo undoInfo ungroup uniform unit unloadPlugin untangleUV untitledFileName untrim upAxis updateAE userCtx uvLink uvSnapshot validateShelfName vectorize view2dToolCtx viewCamera viewClipPlane viewFit viewHeadOn viewLookAt viewManip viewPlace viewSet visor volumeAxis vortex waitCursor warning webBrowser webBrowserPrefs whatIs window windowPref wire wireContext workspace wrinkle wrinkleContext writeTake xbmLangPathList xform",i:">|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",c={cN:"yardoctag",b:"@[A-Za-z]+"},a={cN:"value",b:"#<",e:">"},s={cN:"comment",v:[{b:"#",e:"$",c:[c]},{b:"^\\=begin",e:"^\\=end",c:[c],r:10},{b:"^__END__",e:"\\n$"}]},n={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,n],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]},i={cN:"params",b:"\\(",e:"\\)",k:r},d=[t,a,s,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]},s]},{cN:"function",bK:"def",e:" |$|;",r:0,c:[e.inherit(e.TM,{b:b}),i,s]},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[t,{b:b}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:[a,s,{cN:"regexp",c:[e.BE,n],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];n.c=d,i.c=d;var l="[>?]>",u="[\\w#]+\\(\\w+\\):\\d+:\\d+>",N="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",o=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:d}},{cN:"prompt",b:"^("+l+"|"+u+"|"+N+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,c:[s].concat(o).concat(d)}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"tag",b:""},{cN:"keyword",b:/\w+/,r:0,k:{common:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"sqbracket",b:"\\s\\[",e:"\\]$"},{cN:"cbracket",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],l={cN:"value",e:",",eW:!0,eE:!0,c:i,k:t},c={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:l}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(l,{cN:null})],i:"\\S"};return i.splice(i.length,0,c,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",a={cN:"function",b:c+"\\(",rB:!0,eE:!0,e:"\\("};return{cI:!0,i:"[=/|']",c:[e.CBCM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[a,e.ASM,e.QSM,e.CSSNM]}]},{cN:"tag",b:c,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[e.CBCM,{cN:"rule",b:"[^\\s]",rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:!0,i:"[^\\s]",starts:{cN:"value",eW:!0,eE:!0,c:[a,e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"hexcolor",b:"#[0-9A-Fa-f]+"},{cN:"important",b:"!important"}]}}]}]}]}});hljs.registerLanguage("rust",function(e){var t=e.inherit(e.CBCM);return t.c.push("self"),{aliases:["rs"],k:{keyword:"alignof as be box break const continue crate do else enum extern false fn for if impl in let loop match mod mut offsetof once priv proc pub pure ref return self sizeof static struct super trait true type typeof unsafe unsized use virtual while yield int i8 i16 i32 i64 uint u8 u32 u64 float f32 f64 str char bool",built_in:"assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln!"},l:e.IR+"!?",i:""}]}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{built_in:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{cN:"url",b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"title",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("smalltalk",function(a){var c="[a-z][a-zA-Z0-9_]*",e={cN:"char",b:"\\$.{1}"},r={cN:"symbol",b:"#"+a.UIR};return{aliases:["st"],k:"self super nil true false thisContext",c:[{cN:"comment",b:'"',e:'"'},a.ASM,{cN:"class",b:"\\b[A-Z][A-Za-z0-9_]*",r:0},{cN:"method",b:c+":",r:0},a.CNM,r,e,{cN:"localvars",b:"\\|[ ]*"+c+"([ ]+"+c+")*[ ]*\\|",rB:!0,e:/\|/,i:/\S/,c:[{b:"(\\|[ ]*)?"+c}]},{cN:"array",b:"\\#\\(",e:"\\)",c:[a.ASM,e,a.CNM,r]}]}});hljs.registerLanguage("cs",function(e){var r="abstract as base bool break byte case catch char checked const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long null object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async protected public private internal ascending descending from get group into join let orderby partial select set value var where yield",t=e.IR+"(<"+e.IR+">)?";return{aliases:["csharp"],k:r,i:/::/,c:[{cN:"comment",b:"///",e:"$",rB:!0,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:""},{b:""}]}]},e.CLCM,e.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},e.ASM,e.QSM,e.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"new return throw await",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("markdown",function(){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:"^\\[.+\\]:",rB:!0,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:!0,eE:!0,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("diff",function(){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("1c",function(c){var e="[a-zA-Zа-яА-Я][a-zA-Z0-9_а-яА-Я]*",r="возврат дата для если и или иначе иначеесли исключение конецесли конецпопытки конецпроцедуры конецфункции конеццикла константа не перейти перем перечисление по пока попытка прервать продолжить процедура строка тогда фс функция цикл число экспорт",t="ansitooem oemtoansi ввестивидсубконто ввестидату ввестизначение ввестиперечисление ввестипериод ввестиплансчетов ввестистроку ввестичисло вопрос восстановитьзначение врег выбранныйплансчетов вызватьисключение датагод датамесяц датачисло добавитьмесяц завершитьработусистемы заголовоксистемы записьжурналарегистрации запуститьприложение зафиксироватьтранзакцию значениевстроку значениевстрокувнутр значениевфайл значениеизстроки значениеизстрокивнутр значениеизфайла имякомпьютера имяпользователя каталогвременныхфайлов каталогиб каталогпользователя каталогпрограммы кодсимв командасистемы конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лев лог лог10 макс максимальноеколичествосубконто мин монопольныйрежим названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найти найтипомеченныенаудаление найтиссылки началопериодаби началостандартногоинтервала начатьтранзакцию начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода нрег обработкаожидания окр описаниеошибки основнойжурналрасчетов основнойплансчетов основнойязык открытьформу открытьформумодально отменитьтранзакцию очиститьокносообщений периодстр полноеимяпользователя получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта прав праводоступа предупреждение префиксавтонумерации пустаястрока пустоезначение рабочаядаттьпустоезначение рабочаядата разделительстраниц разделительстрок разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо сигнал симв символтабуляции создатьобъект сокрл сокрлп сокрп сообщить состояние сохранитьзначение сред статусвозврата стрдлина стрзаменить стрколичествострок стрполучитьстроку стрчисловхождений сформироватьпозициюдокумента счетпокоду текущаядата текущеевремя типзначения типзначениястр удалитьобъекты установитьтана установитьтапо фиксшаблон формат цел шаблон",i={cN:"dquote",b:'""'},n={cN:"string",b:'"',e:'"|$',c:[i]},a={cN:"string",b:"\\|",e:'"|$',c:[i]};return{cI:!0,l:e,k:{keyword:r,built_in:t},c:[c.CLCM,c.NM,n,a,{cN:"function",b:"(процедура|функция)",e:"$",l:e,k:"процедура функция",c:[c.inherit(c.TM,{b:e}),{cN:"tail",eW:!0,c:[{cN:"params",b:"\\(",e:"\\)",l:e,k:"знач",c:[n,a]},{cN:"export",b:"экспорт",eW:!0,l:e,k:"экспорт",c:[c.CLCM]}]},c.CLCM]},{cN:"preprocessor",b:"#",e:"$"},{cN:"date",b:"'\\d{2}\\.\\d{2}\\.(\\d{2}|\\d{4})'"}]}});hljs.registerLanguage("javascript",function(r){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document"},c:[{cN:"pi",r:10,v:[{b:/^\s*('|")use strict('|")/},{b:/^\s*('|")use asm('|")/}]},r.ASM,r.QSM,r.CLCM,r.CBCM,r.CNM,{b:"("+r.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[r.CLCM,r.CBCM,r.RM,{b:/;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[r.inherit(r.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[r.CLCM,r.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+r.IR,r:0}]}});hljs.registerLanguage("lua",function(e){var t="\\[=*\\[",a="\\]=*\\]",r={b:t,e:a,c:["self"]},n=[{cN:"comment",b:"--(?!"+t+")",e:"$"},{cN:"comment",b:"--"+t,e:a,c:[r],r:10}];return{l:e.UIR,k:{keyword:"and break do else elseif end false for if in local nil not or repeat return then true until while",built_in:"_G _VERSION assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall coroutine debug io math os package string table"},c:n.concat([{cN:"function",bK:"function",e:"\\)",c:[e.inherit(e.TM,{b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{cN:"params",b:"\\(",eW:!0,c:n}].concat(n)},e.CNM,e.ASM,e.QSM,{cN:"string",b:t,e:a,c:[r],r:5}])}});hljs.registerLanguage("django",function(){var e={cN:"filter",b:/\|[A-Za-z]+\:?/,k:"truncatewords removetags linebreaksbr yesno get_digit timesince random striptags filesizeformat escape linebreaks length_is ljust rjust cut urlize fix_ampersands title floatformat capfirst pprint divisibleby add make_list unordered_list urlencode timeuntil urlizetrunc wordcount stringformat linenumbers slice date dictsort dictsortreversed default_if_none pluralize lower join center default truncatewords_html upper length phone2numeric wordwrap time addslashes slugify first escapejs force_escape iriencode last safe safeseq truncatechars localize unlocalize localtime utc timezone",c:[{cN:"argument",b:/"/,e:/"/},{cN:"argument",b:/'/,e:/'/}]};return{aliases:["jinja"],cI:!0,sL:"xml",subLanguageMode:"continuous",c:[{cN:"comment",b:/\{%\s*comment\s*%}/,e:/\{%\s*endcomment\s*%}/},{cN:"comment",b:/\{#/,e:/#}/},{cN:"template_tag",b:/\{%/,e:/%}/,k:"comment endcomment load templatetag ifchanged endifchanged if endif firstof for endfor in ifnotequal endifnotequal widthratio extends include spaceless endspaceless regroup by as ifequal endifequal ssi now with cycle url filter endfilter debug block endblock else autoescape endautoescape csrf_token empty elif endwith static trans blocktrans endblocktrans get_static_prefix get_media_prefix plural get_current_language language get_available_languages get_current_language_bidi get_language_info get_language_info_list localize endlocalize localtime endlocaltime timezone endtimezone get_current_timezone verbatim",c:[e]},{cN:"variable",b:/\{\{/,e:/}}/,c:[e]}]}});hljs.registerLanguage("vala",function(e){return{k:{keyword:"char uchar unichar int uint long ulong short ushort int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double bool struct enum string void weak unowned owned async signal static abstract interface override while do for foreach else switch case break default return try catch public private protected internal using new this get set const stdout stdin stderr var",built_in:"DBus GLib CCode Gee Object",literal:"false true null"},c:[{cN:"class",bK:"class interface delegate namespace",e:"{",eE:!0,i:"[^,:\\n\\s\\.]",c:[e.UTM]},e.CLCM,e.CBCM,{cN:"string",b:'"""',e:'"""',r:5},e.ASM,e.QSM,e.CNM,{cN:"preprocessor",b:"^#",e:"$",r:2},{cN:"constant",b:" [A-Z_]+ ",r:0}]}});hljs.registerLanguage("python",function(e){var r={cN:"prompt",b:/^(>>>|\.\.\.) /},b={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[r],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[r],r:10},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},e.ASM,e.QSM]},l={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},c={cN:"params",b:/\(/,e:/\)/,c:["self",r,l,b]};return{aliases:["py","gyp"],k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},i:/(<\/|->|\?)/,c:[r,l,b,e.HCM,{v:[{cN:"function",bK:"def",r:10},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n]/,c:[e.UTM,c]},{cN:"decorator",b:/@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("cmake",function(e){return{aliases:["cmake.in"],cI:!0,k:{keyword:"add_custom_command add_custom_target add_definitions add_dependencies add_executable add_library add_subdirectory add_test aux_source_directory break build_command cmake_minimum_required cmake_policy configure_file create_test_sourcelist define_property else elseif enable_language enable_testing endforeach endfunction endif endmacro endwhile execute_process export find_file find_library find_package find_path find_program fltk_wrap_ui foreach function get_cmake_property get_directory_property get_filename_component get_property get_source_file_property get_target_property get_test_property if include include_directories include_external_msproject include_regular_expression install link_directories load_cache load_command macro mark_as_advanced message option output_required_files project qt_wrap_cpp qt_wrap_ui remove_definitions return separate_arguments set set_directory_properties set_property set_source_files_properties set_target_properties set_tests_properties site_name source_group string target_link_libraries try_compile try_run unset variable_watch while build_name exec_program export_library_dependencies install_files install_programs install_targets link_libraries make_directory remove subdir_depends subdirs use_mangled_mesa utility_source variable_requires write_file qt5_use_modules qt5_use_package qt5_wrap_cpp on off true false and or",operator:"equal less greater strless strgreater strequal matches"},c:[{cN:"envvar",b:"\\${",e:"}"},e.HCM,e.QSM,e.NM]}}); diff --git a/www/js/misc/highlight/styles/github.css b/www/js/misc/highlight/styles/github.css new file mode 100644 index 0000000000..9b4f3aa17f --- /dev/null +++ b/www/js/misc/highlight/styles/github.css @@ -0,0 +1,124 @@ +/* + +github.com style (c) Vasily Polovnyov + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #333; + background: #f8f8f8; + -webkit-text-size-adjust: none; +} + +.hljs-comment, +.diff .hljs-header, +.hljs-javadoc { + color: #998; + font-style: italic; +} + +.hljs-keyword, +.css .rule .hljs-keyword, +.hljs-winutils, +.nginx .hljs-title, +.hljs-subst, +.hljs-request, +.hljs-status { + color: #333; + font-weight: bold; +} + +.hljs-number, +.hljs-hexcolor, +.ruby .hljs-constant { + color: #008080; +} + +.hljs-string, +.hljs-tag .hljs-value, +.hljs-phpdoc, +.hljs-dartdoc, +.tex .hljs-formula { + color: #d14; +} + +.hljs-title, +.hljs-id, +.scss .hljs-preprocessor { + color: #900; + font-weight: bold; +} + +.hljs-list .hljs-keyword, +.hljs-subst { + font-weight: normal; +} + +.hljs-class .hljs-title, +.hljs-type, +.vhdl .hljs-literal, +.tex .hljs-command { + color: #458; + font-weight: bold; +} + +.hljs-tag, +.hljs-tag .hljs-title, +.hljs-rules .hljs-property, +.django .hljs-tag .hljs-keyword { + color: #000080; + font-weight: normal; +} + +.hljs-attribute, +.hljs-variable, +.lisp .hljs-body { + color: #008080; +} + +.hljs-regexp { + color: #009926; +} + +.hljs-symbol, +.ruby .hljs-symbol .hljs-string, +.lisp .hljs-keyword, +.clojure .hljs-keyword, +.scheme .hljs-keyword, +.tex .hljs-special, +.hljs-prompt { + color: #990073; +} + +.hljs-built_in { + color: #0086b3; +} + +.hljs-preprocessor, +.hljs-pragma, +.hljs-pi, +.hljs-doctype, +.hljs-shebang, +.hljs-cdata { + color: #999; + font-weight: bold; +} + +.hljs-deletion { + background: #fdd; +} + +.hljs-addition { + background: #dfd; +} + +.diff .hljs-change { + background: #0086b3; +} + +.hljs-chunk { + color: #aaa; +} diff --git a/www/js/misc/highlight/styles/googlecode.css b/www/js/misc/highlight/styles/googlecode.css new file mode 100644 index 0000000000..84be5f26a3 --- /dev/null +++ b/www/js/misc/highlight/styles/googlecode.css @@ -0,0 +1,147 @@ +/* + +Google Code style (c) Aahan Krish + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: white; + color: black; + -webkit-text-size-adjust: none; +} + +.hljs-comment, +.hljs-javadoc { + color: #800; +} + +.hljs-keyword, +.method, +.hljs-list .hljs-keyword, +.nginx .hljs-title, +.hljs-tag .hljs-title, +.setting .hljs-value, +.hljs-winutils, +.tex .hljs-command, +.http .hljs-title, +.hljs-request, +.hljs-status { + color: #008; +} + +.hljs-envvar, +.tex .hljs-special { + color: #660; +} + +.hljs-string, +.hljs-tag .hljs-value, +.hljs-cdata, +.hljs-filter .hljs-argument, +.hljs-attr_selector, +.apache .hljs-cbracket, +.hljs-date, +.hljs-regexp, +.coffeescript .hljs-attribute { + color: #080; +} + +.hljs-sub .hljs-identifier, +.hljs-pi, +.hljs-tag, +.hljs-tag .hljs-keyword, +.hljs-decorator, +.ini .hljs-title, +.hljs-shebang, +.hljs-prompt, +.hljs-hexcolor, +.hljs-rules .hljs-value, +.hljs-literal, +.hljs-symbol, +.ruby .hljs-symbol .hljs-string, +.hljs-number, +.css .hljs-function, +.clojure .hljs-attribute { + color: #066; +} + +.hljs-class .hljs-title, +.smalltalk .hljs-class, +.hljs-javadoctag, +.hljs-yardoctag, +.hljs-phpdoc, +.hljs-dartdoc, +.hljs-type, +.hljs-typename, +.hljs-tag .hljs-attribute, +.hljs-doctype, +.hljs-class .hljs-id, +.hljs-built_in, +.setting, +.hljs-params, +.hljs-variable { + color: #606; +} + +.css .hljs-tag, +.hljs-rules .hljs-property, +.hljs-pseudo, +.hljs-subst { + color: #000; +} + +.css .hljs-class, +.css .hljs-id { + color: #9b703f; +} + +.hljs-value .hljs-important { + color: #ff7700; + font-weight: bold; +} + +.hljs-rules .hljs-keyword { + color: #c5af75; +} + +.hljs-annotation, +.apache .hljs-sqbracket, +.nginx .hljs-built_in { + color: #9b859d; +} + +.hljs-preprocessor, +.hljs-preprocessor *, +.hljs-pragma { + color: #444; +} + +.tex .hljs-formula { + background-color: #eee; + font-style: italic; +} + +.diff .hljs-header, +.hljs-chunk { + color: #808080; + font-weight: bold; +} + +.diff .hljs-change { + background-color: #bccff9; +} + +.hljs-addition { + background-color: #baeeba; +} + +.hljs-deletion { + background-color: #ffc8bd; +} + +.hljs-comment .hljs-yardoctag { + font-weight: bold; +}