Page MenuHomeIn-Portal Phabricator

in-portal
No OneTemporary

File Metadata

Created
Sat, Feb 22, 12:08 AM

in-portal

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.3.x/core/units/admin/admin_tag_processor.php
===================================================================
--- branches/5.3.x/core/units/admin/admin_tag_processor.php (revision 15913)
+++ branches/5.3.x/core/units/admin/admin_tag_processor.php (revision 15914)
@@ -1,1195 +1,1194 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class AdminTagProcessor extends kDBTagProcessor {
/**
* Allows to execute js script after the page is fully loaded
*
* @param Array $params
* @return string
*/
function AfterScript($params)
{
$after_script = $this->Application->GetVar('after_script');
if ($after_script) {
return '<script type="text/javascript">'.$after_script.'</script>';
}
return '';
}
/**
* Returns section title with #section# keyword replaced with current section
*
* @param Array $params
* @return string
*/
function GetSectionTitle($params)
{
if (array_key_exists('default', $params)) {
return $params['default'];
}
return $this->Application->Phrase( kUtil::replaceModuleSection($params['phrase']) );
}
/**
* Returns section icon with #section# keyword replaced with current section
*
* @param Array $params
* @return string
*/
function GetSectionIcon($params)
{
return kUtil::replaceModuleSection($params['icon']);
}
/**
* Returns version of module by name
*
* @param Array $params
* @return string
*/
function ModuleVersion($params)
{
return $this->Application->findModule('Name', $params['module'], 'Version');
}
/**
* Used in table form section drawing
*
* @param Array $params
* @return string
*/
function DrawTree($params)
{
static $deep_level = 0;
// when processings, then sort children by priority (key of children array)
$ret = '';
$section_name = $params['section_name'];
$params['name'] = $this->SelectParam($params, 'name,render_as,block');
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($section_name);
$params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0;
$params['deep_level'] = $deep_level++;
$template = $section_data['url']['t'];
unset($section_data['url']['t']);
$section_data['url']['__URLENCODE__'] = 1;
$section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']);
$ret .= $this->Application->ParseBlock( array_merge($params, $section_data) );
if (!isset($section_data['children'])) {
return $ret;
}
ksort($section_data['children'], SORT_NUMERIC);
foreach ($section_data['children'] as $section_name) {
if (!$sections_helper->sectionVisible($section_name)) {
continue;
}
$params['section_name'] = $section_name;
$ret .= $this->DrawTree($params);
$deep_level--;
}
return $ret;
}
function SectionInfo($params)
{
$section = $params['section'];
if ($section == '#session#') {
$section = $this->Application->RecallVar('section');
}
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($section);
if (!$section_data) {
throw new Exception('Use of undefined section "<strong>' . $section . '</strong>" in "<strong>' . __METHOD__ . '</strong>"');
}
if (array_key_exists('parent', $params) && $params['parent']) {
do {
$section = $section_data['parent'];
$section_data =& $sections_helper->getSectionData($section);
} while (array_key_exists('use_parent_header', $section_data) && $section_data['use_parent_header']);
}
$info = $params['info'];
switch ($info) {
case 'module_path':
if (isset($params['module']) && $params['module']) {
$module = $params['module'];
}
elseif (isset($section_data['icon_module'])) {
$module = $section_data['icon_module'];
}
else {
$module = '#session#';
}
$res = $this->ModulePath(array('module' => $module));
break;
case 'perm_section':
$res = $sections_helper->getPermSection($section);
break;
case 'label':
$res = '';
if ( $section ) {
if ( $section == 'in-portal:root' ) {
// don't translate label for top section, because it's already translated
$res = $section_data['label'];
}
else {
$no_editing = array_key_exists('no_editing', $params) ? $params['no_editing'] : false;
$res = $this->Application->Phrase($section_data['label'], !$no_editing);
}
}
break;
default:
$res = $section_data[$info];
break;
}
if (array_key_exists('as_label', $params) && $params['as_label']) {
$res = $this->Application->Phrase($res);
}
return $res;
}
function PrintSection($params)
{
$section_name = $params['section_name'];
if ($section_name == '#session#') {
$section_name = $this->Application->RecallVar('section');
}
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
if (isset($params['use_first_child']) && $params['use_first_child']) {
$section_name = $sections_helper->getFirstChild($section_name, true);
}
$section_data =& $sections_helper->getSectionData($section_name);
$params['name'] = $this->SelectParam($params, 'name,render_as,block');
$params['section_name'] = $section_name;
$url_params = $section_data['url'];
unset($url_params['t']);
$url_params['__URLENCODE__'] = 1;
$section_data['section_url'] = $this->Application->HREF($section_data['url']['t'], '', $url_params);
$ret = $this->Application->ParseBlock( array_merge($params, $section_data) );
return $ret;
}
/**
* Used in XML drawing for tree
*
* @param Array $params
* @return string
*/
function PrintSections($params)
{
// when processings, then sort children by priority (key of children array)
$ret = '';
$section_name = $params['section_name'];
if ($section_name == '#session#') {
$section_name = $this->Application->RecallVar('section');
}
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($section_name);
$params['name'] = $this->SelectParam($params, 'name,render_as,block');
if (!isset($section_data['children'])) {
return '';
}
ksort($section_data['children'], SORT_NUMERIC);
foreach ($section_data['children'] as $section_name) {
$params['section_name'] = $section_name;
$section_data =& $sections_helper->getSectionData($section_name);
if (!$sections_helper->sectionVisible($section_name)) {
continue;
}
else {
$show_mode = isset($section_data['show_mode']) ? $section_data['show_mode'] : smNORMAL;
$section_data['debug_only'] = ($show_mode == smDEBUG) || ($show_mode == smSUPER_ADMIN) ? 1 : 0;
}
if (isset($section_data['tabs_only']) && $section_data['tabs_only']) {
$perm_status = false;
$folder_label = $section_data['label'];
ksort($section_data['children'], SORT_NUMERIC);
foreach ($section_data['children'] as $priority => $section_name) {
// if only tabs in this section & none of them have permission, then skip section too
$section_name = $sections_helper->getPermSection($section_name);
$perm_status = $this->Application->CheckPermission($section_name.'.view', 1);
if ($perm_status) {
break;
}
}
if (!$perm_status) {
// no permission for all tabs -> don't display tree node either
continue;
}
$params['section_name'] = $section_name;
$section_data =& $sections_helper->getSectionData($section_name);
$section_data['label'] = $folder_label; // use folder label in tree
$section_data['is_tab'] = 1;
}
else {
$section_name = $sections_helper->getPermSection($section_name);
if (!$this->Application->CheckPermission($section_name.'.view', 1)) continue;
}
$params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0;
// remove template, so it doesn't appear as additional parameter in url
$template = $section_data['url']['t'];
unset($section_data['url']['t']);
$section_data['url']['__URLENCODE__'] = 1;
$section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']);
$late_load = getArrayValue($section_data, 'late_load');
if ($late_load) {
$t = $late_load['t'];
unset($late_load['t']);
$section_data['late_load'] = $this->Application->HREF($t, '', $late_load);
$params['children_count'] = 99;
}
else {
$section_data['late_load'] = '';
}
// restore template
$section_data['url']['t'] = $template;
$ret .= $this->Application->ParseBlock( array_merge($params, $section_data) );
$params['section_name'] = $section_name;
}
return preg_replace("/\r\n|\n/", '', $ret);
}
function ListSectionPermissions($params)
{
$section_name = isset($params['section_name']) ? $params['section_name'] : $this->Application->GetVar('section_name');
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($section_name);
$block_params = array_merge($section_data, Array('name' => $params['render_as'], 'section_name' => $section_name));
$ret = '';
foreach ($section_data['permissions'] as $perm_name) {
if (preg_match('/^advanced:(.*)/', $perm_name) != $params['type']) continue;
$block_params['perm_name'] = $perm_name;
$ret .= $this->Application->ParseBlock($block_params);
}
return $ret;
}
function ModuleInclude($params)
{
foreach ($params as $param_name => $param_value) {
$params[$param_name] = kUtil::replaceModuleSection($param_value);
}
return $this->Application->ProcessParsedTag('m', 'ModuleInclude', $params);
}
function TodayDate($params)
{
return date($params['format']);
}
/**
* Draws section tabs using block name passed
*
* @param Array $params
*/
function ListTabs($params)
{
$sections_helper = $this->Application->recallObject('SectionsHelper');
/* @var $sections_helper kSectionsHelper */
$section_data =& $sections_helper->getSectionData($params['section_name']);
$ret = '';
$block_params = Array('name' => $params['render_as']);
ksort($section_data['children'], SORT_NUMERIC);
foreach ($section_data['children'] as $priority => $section_name) {
$perm_section = $sections_helper->getPermSection($section_name);
if ( !$this->Application->CheckPermission($perm_section.'.view') ) {
continue;
}
$tab_data =& $sections_helper->getSectionData($section_name);
$block_params['t'] = $tab_data['url']['t'];
$block_params['pass'] = $tab_data['url']['pass'];
$block_params['title'] = $tab_data['label'];
$block_params['main_prefix'] = $section_data['SectionPrefix'];
$ret .= $this->Application->ParseBlock($block_params);
}
return $ret;
}
/**
* Returns list of module item tabs that have view permission in current category
*
* @param Array $params
*/
function ListCatalogTabs($params)
{
$ret = '';
$special = isset($params['special']) ? $params['special'] : '';
$replace_main = isset($params['replace_m']) && $params['replace_m'];
$skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array ();
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
$prefix = $module_info['Var'];
if ( $prefix == 'm' && $replace_main ) {
$prefix = 'c';
}
if ( $this->Application->prefixRegistred($prefix) ) {
$config = $this->Application->getUnitConfig($prefix);
}
else {
$config = null;
}
if ( in_array($prefix, $skip_prefixes) || !is_object($config) || !$config->getCatalogItem() ) {
continue;
}
$icon = $config->getCatalogTabIcon();
if ( strpos($icon, ':') !== false ) {
list ($icon_module, $icon) = explode(':', $icon, 2);
}
else {
$icon_module = 'core';
}
$label = $config->getSetting($params['title_property']);
$block_params['title'] = $label;
$block_params['prefix'] = $prefix;
$block_params['icon_module'] = $icon_module;
$block_params['icon'] = $icon;
$ret .= $this->Application->ParseBlock($block_params);
}
return $ret;
}
/**
* Renders inividual catalog tab based on prefix and title_property given
*
* @param Array $params
* @return string
*/
function CatalogTab($params)
{
$config = $this->Application->getUnitConfig($params['prefix']);
$icon = $config->getCatalogTabIcon();
if ( strpos($icon, ':') !== false ) {
list ($icon_module, $icon) = explode(':', $icon, 2);
}
else {
$icon_module = 'core';
}
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
$block_params['icon_module'] = $icon_module;
$block_params['icon'] = $icon;
$block_params['title'] = $config->getSetting($params['title_property']);
return $this->Application->ParseBlock($block_params);
}
/**
* Allows to construct link for opening any type of catalog item selector
*
* @param Array $params
* @return string
*/
function SelectorLink($params)
{
$mode = 'catalog';
if (isset($params['mode'])) { // {catalog, advanced_view}
$mode = $params['mode'];
unset($params['mode']);
}
$params['t'] = 'catalog/item_selector/item_selector_'.$mode;
$params['m_cat_id'] = $this->Application->getBaseCategory();
$default_params = Array('no_amp' => 1, 'pass' => 'all,'.$params['prefix']);
unset($params['prefix']);
$pass_through = Array();
if (isset($params['tabs_dependant'])) { // {yes, no}
$pass_through['td'] = $params['tabs_dependant'];
unset($params['tabs_dependant']);
}
if (isset($params['selection_mode'])) { // {single, multi}
$pass_through['tm'] = $params['selection_mode'];
unset($params['selection_mode']);
}
if (isset($params['tab_prefixes'])) { // {all, none, <comma separated prefix list>}
$pass_through['tp'] = $params['tab_prefixes'];
unset($params['tab_prefixes']);
}
if ($pass_through) {
// add pass_through to selector url if any
$params['pass_through'] = implode(',', array_keys($pass_through));
$params = array_merge($params, $pass_through);
}
// user can override default parameters (except pass_through of course)
$params = array_merge($default_params, $params);
return $this->Application->ProcessParsedTag('m', 'T', $params);
}
function TimeFrame($params)
{
$w = adodb_date('w');
$m = adodb_date('m');
$y = adodb_date('Y');
//FirstDayOfWeek is 0 for Sunday and 1 for Monday
$fdow = $this->Application->ConfigValue('FirstDayOfWeek');
if ( $fdow && $w == 0 ) {
$w = 7;
}
$today_start = adodb_mktime(0, 0, 0, adodb_date('m'), adodb_date('d'), $y);
$first_day_of_this_week = $today_start - ($w - $fdow) * 86400;
$first_day_of_this_month = adodb_mktime(0, 0, 0, $m, 1, $y);
$this_quater = ceil($m / 3);
$this_quater_start = adodb_mktime(0, 0, 0, $this_quater * 3 - 2, 1, $y);
switch ( $params['type'] ) {
case 'last_week_start':
$timestamp = $first_day_of_this_week - 86400 * 7;
break;
case 'last_week_end':
$timestamp = $first_day_of_this_week - 1;
break;
case 'last_month_start':
$timestamp = $m == 1 ? adodb_mktime(0, 0, 0, 12, 1, $y - 1) : adodb_mktime(0, 0, 0, $m - 1, 1, $y);
break;
case 'last_month_end':
$timestamp = $first_day_of_this_month = adodb_mktime(0, 0, 0, $m, 1, $y) - 1;
break;
case 'last_quater_start':
$timestamp = $this_quater == 1 ? adodb_mktime(0, 0, 0, 10, 1, $y - 1) : adodb_mktime(0, 0, 0, ($this_quater - 1) * 3 - 2, 1, $y);
break;
case 'last_quater_end':
$timestamp = $this_quater_start - 1;
break;
case 'last_6_months_start':
$timestamp = $m <= 6 ? adodb_mktime(0, 0, 0, $m + 6, 1, $y - 1) : adodb_mktime(0, 0, 0, $m - 6, 1, $y);
break;
case 'last_year_start':
$timestamp = adodb_mktime(0, 0, 0, 1, 1, $y - 1);
break;
case 'last_year_end':
$timestamp = adodb_mktime(23, 59, 59, 12, 31, $y - 1);
break;
default:
$timestamp = 0;
break;
}
if ( isset($params['format']) ) {
$format = $params['format'];
if ( preg_match("/_regional_(.*)/", $format, $regs) ) {
$lang = $this->Application->recallObject('lang.current');
/* @var $lang LanguagesItem */
$format = $lang->GetDBField($regs[1]);
}
return adodb_date($format, $timestamp);
}
return $timestamp;
}
/**
* Redirect to cache rebuild template, when required by installator
*
* @param Array $params
*/
function CheckPermCache($params)
{
// we have separate session between install wizard and admin console, so store in cache
$global_mark = $this->Application->getDBCache('ForcePermCacheUpdate');
$local_mark = $this->Application->RecallVar('PermCache_UpdateRequired');
if ( $global_mark || $local_mark ) {
$this->Application->RemoveVar('PermCache_UpdateRequired');
$rebuild_mode = $this->Application->ConfigValue('CategoryPermissionRebuildMode');
if ( $rebuild_mode == CategoryPermissionRebuild::SILENT ) {
$updater = $this->Application->makeClass('kPermCacheUpdater');
/* @var $updater kPermCacheUpdater */
$updater->OneStepRun();
$this->Application->HandleEvent(new kEvent('c:OnResetCMSMenuCache'));
}
elseif ( $rebuild_mode == CategoryPermissionRebuild::AUTOMATIC ) {
// update with progress bar
return true;
}
}
return false;
}
/**
* Checks if current protocol is SSL
*
* @param Array $params
* @return int
*/
function IsSSL($params)
{
return (PROTOCOL == 'https://')? 1 : 0;
}
function PrintColumns($params)
{
/* @var $picker_helper kColumnPickerHelper */
$picker_helper = $this->Application->recallObject('ColumnPickerHelper');
$picker_helper->SetGridName($this->Application->GetLinkedVar('grid_name'));
$main_prefix = $this->Application->RecallVar('main_prefix');
$cols = $picker_helper->LoadColumns($main_prefix);
$this->Application->Phrases->AddCachedPhrase('__FREEZER__', '-------------');
$o = '';
if (isset($params['hidden']) && $params['hidden']) {
foreach ($cols['hidden_fields'] as $col) {
$title = $this->Application->Phrase($cols['titles'][$col]);
$o .= "<option value='$col'>".$title;
}
}
else {
foreach ($cols['order'] as $col) {
if (in_array($col, $cols['hidden_fields'])) continue;
$title = $this->Application->Phrase($cols['titles'][$col]);
$o .= "<option value='$col'>".$title;
}
}
return $o;
}
/**
* Allows to set popup size (key - current template name)
*
* @param Array $params
* @return string
* @access protected
*/
protected function SetPopupSize($params)
{
$width = $params['width'];
$height = $params['height'];
if ( $this->Application->GetVar('ajax') == 'yes' ) {
// during AJAX request just output size
die($width . 'x' . $height);
}
if ( !$this->UsePopups($params) ) {
return;
}
$t = $this->Application->GetVar('t');
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'PopupSizes
WHERE TemplateName = ' . $this->Conn->qstr($t);
$popup_info = $this->Conn->GetRow($sql);
if ( !$popup_info ) {
// create new popup size record
$fields_hash = Array (
'TemplateName' => $t,
'PopupWidth' => $width,
'PopupHeight' => $height,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PopupSizes');
}
elseif ( $popup_info['PopupWidth'] != $width || $popup_info['PopupHeight'] != $height ) {
// popup found and size in tag differs from one in db -> update in db
$fields_hash = Array (
'PopupWidth' => $width,
'PopupHeight' => $height,
);
$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PopupSizes', 'PopupId = ' . $popup_info['PopupId']);
}
}
/**
* Allows to check if popups are generally enabled OR to check for "popup" or "modal" mode is enabled
*
* @param Array $params
* @return bool
*/
function UsePopups($params)
{
if ($this->Application->GetVar('_force_popup')) {
return true;
}
$use_popups = (int)$this->Application->ConfigValue('UsePopups');
if (array_key_exists('mode', $params)) {
$mode_mapping = Array ('popup' => 1, 'modal' => 2);
return $use_popups == $mode_mapping[ $params['mode'] ];
}
return $use_popups;
}
function UseToolbarLabels($params)
{
return (int)$this->Application->ConfigValue('UseToolbarLabels');
}
/**
* Checks if debug mode enabled (optionally) and specified constant is on
*
* @param Array $params
* @return bool
* @todo Could be a duplicate of kMainTagProcessor::ConstOn
*/
function ConstOn($params)
{
$constant_name = $this->SelectParam($params, 'name,const');
$debug_mode = isset($params['debug_mode']) && $params['debug_mode'] ? $this->Application->isDebugMode() : true;
return $debug_mode && kUtil::constOn($constant_name);
}
/**
* Builds link to last template in main frame of admin
*
* @param Array $params
* @return string
*/
function MainFrameLink($params)
{
$persistent = isset($params['persistent']) && $params['persistent'];
if ($persistent && $this->Application->ConfigValue('RememberLastAdminTemplate')) {
// check last_template in persistent session
$last_template = $this->Application->RecallPersistentVar('last_template_popup');
}
else {
// check last_template in session
$last_template = $this->Application->RecallVar('last_template_popup'); // because of m_opener=s there
}
if (!$last_template) {
$params['persistent'] = 1;
return $persistent ? false : $this->MainFrameLink($params);
}
list($index_file, $env) = explode('|', $last_template);
$vars = $this->Application->processQueryString($env, 'pass');
$recursion_templates = Array ('login', 'index', 'no_permission');
if (isset($vars['admin']) && $vars['admin'] == 1) {
// index template doesn't begin recursion on front-end (in admin frame)
$vars['m_theme'] = '';
if (isset($params['m_opener']) && $params['m_opener'] == 'r') {
// front-end link for highlighting purposes
$vars['t'] = 'index';
$vars['m_cat_id'] = $this->Application->getBaseCategory();
}
unset($recursion_templates[ array_search('index', $recursion_templates)]);
}
if (in_array($vars['t'], $recursion_templates)) {
// prevents redirect recursion OR old in-portal pages
$params['persistent'] = 1;
return $persistent ? false : $this->MainFrameLink($params);
}
$vars = array_merge($vars, $params);
$t = $vars['t'];
unset($vars['t'], $vars['persistent']);
// substitute language in link to current (link will work, even when language will be changed)
$vars['m_lang'] = $this->Application->GetVar('m_lang');
return $this->Application->HREF($t, '', $vars, $index_file);
}
/**
* Returns menu frame width or 200 in case, when invalid width specified in config
*
* @param Array $params
* @return string
*/
function MenuFrameWidth($params)
{
$width = (int)$this->Application->ConfigValue('MenuFrameWidth');
return $width > 0 ? $width : 200;
}
function AdminSkin($params)
{
$skin_helper = $this->Application->recallObject('SkinHelper');
/* @var $skin_helper SkinHelper */
return $skin_helper->AdminSkinTag($params);
}
/**
* Prints errors, discovered during mass template compilation
*
* @param $params
* @return string
* @access protected
*/
protected function PrintCompileErrors($params)
{
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
$errors = $this->Application->RecallVar('compile_errors');
if ( !$errors ) {
return '';
}
$ret = '';
$errors = unserialize($errors);
$path_regexp = '/^' . preg_quote(FULL_PATH, '/') . '/';
foreach ($errors as $an_error) {
$block_params = array_merge($block_params, $an_error);
$block_params['file'] = preg_replace($path_regexp, '', $an_error['file'], 1);
$ret .= $this->Application->ParseBlock($block_params);
}
$this->Application->RemoveVar('compile_errors');
return $ret;
}
function CompileErrorCount($params)
{
$errors = $this->Application->RecallVar('compile_errors');
if (!$errors) {
return 0;
}
return count( unserialize($errors) );
}
/**
* Detects if given exception isn't one caused by tag error
*
* @param Array $params
* @return string
* @access protected
*/
protected function IsParserException($params)
{
return mb_strtolower($params['class']) == 'parserexception';
}
function ExportData($params)
{
$export_helper = $this->Application->recallObject('CSVHelper');
/* @var $export_helper kCSVHelper */
$result = $export_helper->ExportData( $this->SelectParam($params, 'var,name,field') );
return ($result === false) ? '' : $result;
}
function ImportData($params)
{
$import_helper = $this->Application->recallObject('CSVHelper');
/* @var $import_helper kCSVHelper */
$result = $import_helper->ImportData( $this->SelectParam($params, 'var,name,field') );
return ($result === false) ? '' : $result;
}
function PrintCSVNotImportedLines($params)
{
$import_helper = $this->Application->recallObject('CSVHelper');
/* @var $import_helper kCSVHelper */
return $import_helper->GetNotImportedLines();
}
/**
* Returns input field name to
* be placed on form (for correct
* event processing)
*
* @param Array $params
* @return string
* @access public
*/
function InputName($params)
{
list($id, $field) = $this->prepareInputName($params);
$ret = $this->getPrefixSpecial().'[0]['.$field.']'; // 0 always, as has no idfield
if( getArrayValue($params, 'as_preg') ) $ret = preg_quote($ret, '/');
return $ret;
}
/**
* Returns list of all backup file dates formatted
* in passed block
*
* @param Array $params
* @return string
* @access public
*/
function PrintBackupDates($params)
{
$backup_helper = $this->Application->recallObject('BackupHelper');
/* @var $backup_helper BackupHelper */
$ret = '';
$dates = $backup_helper->getBackupFiles();
foreach ($dates as $date) {
$params['backuptimestamp'] = $date['filedate'];
$params['backuptime'] = date('F j, Y, g:i a', $date['filedate']);
$params['backupsize'] = round($date['filesize'] / 1024 / 1024, 2); // MBytes
$ret .= $this->Application->ParseBlock($params);
}
return $ret;
}
/**
* Returns phpinfo() output
*
* @param Array $params
* @return string
*/
function PrintPHPinfo($params)
{
ob_start();
phpinfo();
return ob_get_clean();
}
function PrintSqlCols($params)
{
$ret = '';
$block = $params['render_as'];
$a_data = unserialize($this->Application->GetVar('sql_rows'));
$a_row = current($a_data);
foreach ($a_row AS $col => $value) {
$ret .= $this->Application->ParseBlock(Array ('name' => $block, 'value' => $col));
}
return $ret;
}
function PrintSqlRows($params)
{
$ret = '';
$block = $params['render_as'];
$a_data = unserialize($this->Application->GetVar('sql_rows'));
foreach ($a_data as $a_row) {
$cells = '';
foreach ($a_row as $value) {
$cells .= '<td>' . kUtil::escape($value, kUtil::ESCAPE_HTML) . '</td>';
}
$ret .= $this->Application->ParseBlock(Array ('name' => $block, 'cells' => $cells));
}
return $ret;
}
/**
* Prints available and enabled import sources using given block
*
* @param Array $params
* @return string
*/
function PrintImportSources($params)
{
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'ImportScripts
WHERE (Status = ' . STATUS_ACTIVE . ') AND (Type = "CSV")';
$import_sources = $this->Conn->Query($sql);
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
$ret = '';
foreach ($import_sources as $import_source) {
$block_params['script_id'] = $import_source['ImportId'];
$block_params['script_module'] = mb_strtolower($import_source['Module']);
$block_params['script_name'] = $import_source['Name'];
$block_params['script_prefix'] = $import_source['Prefix'];
$block_params['module_path'] = $this->Application->findModule('Name', $import_source['Module'], 'Path');
$ret .= $this->Application->ParseBlock($block_params);
}
return $ret;
}
/**
* Checks, that new window should be opened in "incs/close_popup" template instead of refreshing parent window
*
* @param Array $params
* @return bool
*/
function OpenNewWindow($params)
{
if (!$this->UsePopups($params)) {
return false;
}
$diff = array_key_exists('diff', $params) ? $params['diff'] : 0;
$wid = $this->Application->GetVar('m_wid');
$stack_name = rtrim('opener_stack_' . $wid, '_');
$opener_stack = $this->Application->RecallVar($stack_name);
$opener_stack = $opener_stack ? unserialize($opener_stack) : Array ();
return count($opener_stack) >= 2 - $diff;
}
/**
* Allows to dynamically change current language in template
*
* @param Array $params
*/
function SetLanguage($params)
{
$this->Application->SetVar('m_lang', $params['language_id']);
$this->Application->Phrases->Init('phrases', '', $params['language_id']);
}
/**
* Performs HTTP Authentification for administrative console
*
* @param Array $params
* @return bool
*/
function HTTPAuth($params)
{
if ( !$this->Application->ConfigValue('UseHTTPAuth') ) {
// http authentification not required
return true;
}
$super_admin_ips = defined('SA_IP') ? SA_IP : false;
$auth_bypass_ips = $this->Application->ConfigValue('HTTPAuthBypassIPs');
if ( ($auth_bypass_ips && kUtil::ipMatch($auth_bypass_ips)) || ($super_admin_ips && kUtil::ipMatch($super_admin_ips)) ) {
// user ip is in ip bypass list
return true;
}
if ( !array_key_exists('PHP_AUTH_USER', $_SERVER) ) {
// ask user to authentificate, when not authentificated before
return $this->_httpAuthentificate();
}
else {
// validate user credentials (browsers remembers user/password
// and sends them each time page is visited, so no need to save
// authentification result in session)
if ( $this->Application->ConfigValue('HTTPAuthUsername') != $_SERVER['PHP_AUTH_USER'] ) {
// incorrect username
return $this->_httpAuthentificate();
}
$password_formatter = $this->Application->recallObject('kPasswordFormatter');
/* @var $password_formatter kPasswordFormatter */
if ( !$password_formatter->checkPasswordFromSetting('HTTPAuthPassword', $_SERVER['PHP_AUTH_PW']) ) {
// incorrect password
return $this->_httpAuthentificate();
}
}
return true;
}
/**
* Ask user to authentificate
*
* @return bool
*/
function _httpAuthentificate()
{
$realm = strip_tags( $this->Application->ConfigValue('Site_Name') );
header('WWW-Authenticate: Basic realm="' . $realm . '"');
header('HTTP/1.0 401 Unauthorized');
return false;
}
/**
* Checks, that we are using memory cache
*
* @param Array $params
* @return bool
*/
function MemoryCacheEnabled($params)
{
return $this->Application->isCachingType(CACHING_TYPE_MEMORY);
}
/**
* Generates HTML for additional js and css files inclusion in accordance to selected editor language
*
* @param Array $params
* @return string
* @access protected
*/
protected function IncludeCodeMirrorFilesByLanguage($params)
{
$ret = '';
$language = $params['language'];
$language_map = Array (
- 'application/x-httpd-php' => Array (
- 'htmlmixed.js', 'xml.js', 'javascript.js', 'css.js', 'clike.js', 'php.js'
- ),
- 'text/html' => Array (
- 'xml.js', 'javascript.js', 'css.js', 'htmlmixed.js'
- )
+ 'application/x-httpd-php' => array('htmlmixed.js', 'xml.js', 'javascript.js', 'css.js', 'clike.js', 'php.js'),
+ 'text/css' => array('css.js'),
+ 'text/html' => array('xml.js', 'javascript.js', 'css.js', 'htmlmixed.js'),
+ 'text/x-sql' => array('sql.js'),
+ 'text/x-mysql' => array('sql.js'),
);
if ( !isset($language_map[$language]) ) {
$language_map[$language] = Array ($language . '.js');
}
foreach ($language_map[$language] as $filename) {
$ret .= $this->_includeCodeMirrorFile($filename, $params);
}
return $ret;
}
/**
* Generates code for one CodeMirror additional file inclusion
*
* @param string $filename
* @param Array $params
* @return string
* @access protected
* @throws InvalidArgumentException
*/
protected function _includeCodeMirrorFile($filename, $params)
{
static $included = Array ();
$name = pathinfo($filename, PATHINFO_FILENAME);
$extension = pathinfo($filename, PATHINFO_EXTENSION);
if ( $extension != 'js' && $extension != 'css' ) {
throw new InvalidArgumentException('Extension "' . $extension . '" not supported');
}
if ( isset($included[$filename]) ) {
return '';
}
$included[$filename] = 1;
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
$block_params['resource_extension'] = $extension;
$block_params['resource_file'] = $name . '/' . $filename;
return $this->Application->ParseBlock($block_params);
}
}
Index: branches/5.3.x/core/admin_templates/skins/skin_edit.tpl
===================================================================
--- branches/5.3.x/core/admin_templates/skins/skin_edit.tpl (revision 15913)
+++ branches/5.3.x/core/admin_templates/skins/skin_edit.tpl (revision 15914)
@@ -1,276 +1,276 @@
<inp2:adm_SetPopupSize width="750" height="570"/>
<inp2:m_include t="incs/header"/>
<inp2:m_RenderElement name="combined_header" section="in-portal:skins" prefix="skin" title_preset="skin_edit"/>
<!-- ToolBar -->
<table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td>
<script type="text/javascript">
a_toolbar = new ToolBar();
a_toolbar.AddButton( new ToolBarButton('select', '<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>', function() {
submit_event('skin','<inp2:skin_SaveEvent/>');
}
));
a_toolbar.AddButton( new ToolBarButton('cancel', '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>', function() {
cancel_edit('skin','OnCancelEdit','<inp2:skin_SaveEvent/>','<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>');
}
));
a_toolbar.AddButton( new ToolBarButton('reset_edit', '<inp2:m_phrase label="la_ToolTip_Reset" escape="1"/>', function() {
reset_form('skin', 'OnReset', '<inp2:m_Phrase label="la_FormResetConfirmation" escape="1"/>');
}
));
a_toolbar.AddButton( new ToolBarSeparator('sep1') );
a_toolbar.AddButton( new ToolBarButton('prev', '<inp2:m_phrase label="la_ToolTip_Prev" escape="1"/>', function() {
go_to_id('skin', '<inp2:skin_PrevId/>');
}
));
a_toolbar.AddButton( new ToolBarButton('next', '<inp2:m_phrase label="la_ToolTip_Next" escape="1"/>', function() {
go_to_id('skin', '<inp2:skin_NextId/>');
}
));
a_toolbar.Render();
<inp2:m_if check="skin_IsSingle" >
a_toolbar.HideButton('prev');
a_toolbar.HideButton('next');
a_toolbar.HideButton('sep1');
<inp2:m_else/>
<inp2:m_if check="skin_IsLast" >
a_toolbar.DisableButton('next');
</inp2:m_if>
<inp2:m_if check="skin_IsFirst" >
a_toolbar.DisableButton('prev');
</inp2:m_if>
</inp2:m_if>
</script>
<script src="js/swfobject.js" type="text/javascript"></script>
<script type="text/javascript" src="<inp2:m_Compress files='js/uploader/upload_manager.js|js/uploader/uploader.js'/>"></script>
</td>
</tr>
</tbody>
</table>
<inp2:m_DefineElement name="inp_edit_serialized" subfield="" class="" is_last="" maxlength="" onblur="" size="" onkeyup="" style="width: 100%">
<inp2:PrintSerializedFields pass_params="1" field="$field" render_as="$item_render_as"/>
</inp2:m_DefineElement>
<inp2:skin_SaveWarning name="grid_save_warning"/>
<inp2:skin_ErrorWarning name="form_error_warning"/>
<style type="text/css">
.skin-table td {width: 90px};
</style>
<div id="scroll_container">
<table class="edit-form">
<inp2:m_RenderElement name="inp_id_label" prefix="skin" field="SkinId" title="la_fld_Id"/>
<inp2:m_RenderElement name="inp_edit_box" prefix="skin" field="Name" title="la_fld_SkinName" style="width: 100px"/>
<inp2:m_RenderElement name="inp_edit_swf_upload" prefix="skin" field="Logo" title="la_fld_Logo"/>
<inp2:m_RenderElement name="inp_edit_swf_upload" prefix="skin" field="LogoBottom" title="la_fld_LogoBottom"/>
<inp2:m_RenderElement name="inp_edit_swf_upload" prefix="skin" field="LogoLogin" title="la_fld_LogoLogin"/>
<inp2:m_RenderElement design="form_row" prefix="skin" field="Options" title="la_HeadFrame">
<td class="control-cell">
<table class="skin-table">
<tr>
<td>Font Color (HeadColor)</td>
<td>Background (HeadBgColor)</td>
<td>Bar Text Color (HeadBarColor)</td>
<td>Bar Background (HeadBarBgColor)</td>
</tr>
<tr>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[HeadColor][Value]"
value="<inp2:skin_Field name="Options" format="HeadColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[HeadBgColor][Value]"
value="<inp2:skin_Field name="Options" format="HeadBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[HeadBarColor][Value]"
value="<inp2:skin_Field name="Options" format="HeadBarColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[HeadBarBgColor][Value]"
value="<inp2:skin_Field name="Options" format="HeadBarBgColor.Value"/>">
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
<inp2:m_RenderElement design="form_row" prefix="skin" field="Options" title="la_GeneralSections">
<td class="control-cell">
<table class="skin-table">
<tr>
<td>Section Title Color (SectionColor)</td>
<td>Section Background (SectionBgColor)</td>
<td>Titlebar Font Color (TitleBarColor)</td>
<td>Titlebar Background (TitleBarBgColor)</td>
</tr>
<tr>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[SectionColor][Value]"
value="<inp2:skin_Field name="Options" format="SectionColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[SectionBgColor][Value]"
value="<inp2:skin_Field name="Options" format="SectionBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TitleBarColor][Value]"
value="<inp2:skin_Field name="Options" format="TitleBarColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TitleBarBgColor][Value]"
value="<inp2:skin_Field name="Options" format="TitleBarBgColor.Value"/>">
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
<inp2:m_RenderElement design="form_row" prefix="skin" field="Options" title="la_DataGrid1">
<td class="control-cell">
<table class="skin-table">
<tr>
<td>Toolbar Backgroun (ToolbarBgColor)</td>
<td>Filter Row Background (FiltersBgColor)</td>
<td>Column Titles Color (ColumnTitlesColor)</td>
<td>Column Titles Background (ColumnTitlesBgColor)</td>
</tr>
<tr>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[ToolbarBgColor][Value]"
value="<inp2:skin_Field name="Options" format="ToolbarBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[FiltersBgColor][Value]"
value="<inp2:skin_Field name="Options" format="FiltersBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[ColumnTitlesColor][Value]"
value="<inp2:skin_Field name="Options" format="ColumnTitlesColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[ColumnTitlesBgColor][Value]"
value="<inp2:skin_Field name="Options" format="ColumnTitlesBgColor.Value"/>">
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
<inp2:m_RenderElement design="form_row" prefix="skin" field="Options" title="la_DataGrid2">
<td class="control-cell">
<table class="skin-table">
<tr>
<td>Grid Odd Row Color (OddColor)</td>
<td>Grid Odd Row Background Color (OddBgColor)</td>
<td>Grid Even Row Color (EvenColor)</td>
<td>Grid Even Row Background Color (EvenBgColor)</td>
</tr>
<tr>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[OddColor][Value]"
value="<inp2:skin_Field name="Options" format="OddColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[OddBgColor][Value]"
value="<inp2:skin_Field name="Options" format="OddBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[EvenColor][Value]"
value="<inp2:skin_Field name="Options" format="EvenColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[EvenBgColor][Value]"
value="<inp2:skin_Field name="Options" format="EvenBgColor.Value"/>">
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
<inp2:m_RenderElement design="form_row" prefix="skin" field="Options" title="la_Trees">
<td class="control-cell">
<table class="skin-table">
<tr>
<td>Tree Item Color (TreeColor)</td>
<td>Tree Item Hover Color (TreeHoverColor)</td>
<td>Tree Highlighted Item Color (TreeHighColor)</td>
<td>Tree Highlighted Item Hover Color (TreeHighHoverColor)</td>
<td>Tree Highlighted Item Background Color (TreeHighBgColor)</td>
<td>Tree Background Color (TreeBgColor)</td>
</tr>
<tr>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeHoverColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeHoverColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeHighColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeHighColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeHighHoverColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeHighHoverColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeHighBgColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeHighBgColor.Value"/>">
</td>
<td>
<input style="width: 80px" type="text"
name="<inp2:skin_InputName field="Options"/>[TreeBgColor][Value]"
value="<inp2:skin_Field name="Options" format="TreeBgColor.Value"/>">
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
- <inp2:m_RenderElement name="inp_edit_textarea" prefix="skin" field="CSS" title="la_fld_CSS" control_options="{min_height: 300}"/>
+ <inp2:m_RenderElement name="inp_edit_codemirror" prefix="skin" field="CSS" title="la_fld_CSS" control_options="{min_height: 300}" language="text/css"/>
<inp2:m_RenderElement name="inp_edit_radio" prefix="skin" field="DisplaySiteNameInHeader"/>
</table>
</div>
-<inp2:m_include t="incs/footer"/>
\ No newline at end of file
+<inp2:m_include t="incs/footer"/>
Index: branches/5.3.x/core/admin_templates/tools/sql_query.tpl
===================================================================
--- branches/5.3.x/core/admin_templates/tools/sql_query.tpl (revision 15913)
+++ branches/5.3.x/core/admin_templates/tools/sql_query.tpl (revision 15914)
@@ -1,114 +1,114 @@
<inp2:m_include t="incs/header"/>
<inp2:m_RenderElement name="combined_header" prefix="adm" section="in-portal:service" title_preset="sql_query"/>
<!-- ToolBar -->
<table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
<tbody>
<tr>
<td>
<script type="text/javascript">
a_toolbar = new ToolBar();
a_toolbar.AddButton(
new ToolBarButton(
'query_database',
'<inp2:m_phrase label="la_ToolTip_RunSQL" escape="1"/>',
function() {
submit_event('adm','OnSqlQuery');
}
)
);
a_toolbar.Render();
</script>
</td>
</tr>
</tbody>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="4" class="bordered">
<inp2:m_RenderElement name="subsection" title="la_prompt_sqlquery_header"/>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td colspan="3">
<inp2:m_inc param="tab_index" by="1"/>
<span class="hint"><img src="img/smicon7.gif" align="absmiddle" height="14" width="14">
<inp2:m_Phrase label="la_text_db_warning"/>
</span>
</td>
</tr>
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<inp2:m_inc param="tab_index" by="1"/>
<td valign="top" style="width:100px;">
<span class="text" ID="prompt_sql_query"><inp2:m_Phrase label="la_prompt_sqlquery"/>
</td>
<td valign="top" width="400" style="width:auto">
<style type="text/css">
.CodeMirror {
background-color: white;
}
</style>
<textarea name="sql" cols="100" rows="10"><inp2:m_Get var="sql"/></textarea>
<script src="<inp2:m_TemplatesBase/>/incs/code_mirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="<inp2:m_TemplatesBase/>/incs/code_mirror/lib/codemirror.css">
- <inp2:adm_IncludeCodeMirrorFilesByLanguage language="mysql" render_as="code_mirror_resource"/>
+ <inp2:adm_IncludeCodeMirrorFilesByLanguage language="text/x-mysql" render_as="code_mirror_resource"/>
<script type="text/javascript">
var $editor = CodeMirror.fromTextArea(document.kernel_form.sql, {mode: "mysql", tabindex: <inp2:m_Get name="tab_index"/>, indentWithTabs: true, indentUnit: 4, lineWrapping: true, lineNumbers: true});
$editor.setSize(null, 155);
</script>
</td>
<td align="left" valign="top" style="width:250px">
<span class="text">
<inp2:m_if check="m_Get" var="query_status">
<inp2:m_Phrase label="la_SQLRuntime"/>:&nbsp;<inp2:m_Get var="sql_time"/> sec.<br />
<inp2:m_if check="m_Get" var="sql_has_affected">
<br /><inp2:m_Phrase label="la_SQLAffectedRows"/>: <inp2:m_Get var="sql_affected"/>
</inp2:m_if>
</inp2:m_if>
</span>
</td>
</tr>
</table>
<inp2:m_if check="m_Get" var="query_status">
<inp2:m_if check="m_Get" var="sql_has_rows">
<br/>
<table width="100%" cellspacing="0" cellpadding="0" class="bordered">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<inp2:m_inc param="tab_index" by="1"/>
<td colspan="3">
<table width="100%" border="0" cellspacing="0" cellpadding="4">
<tr class="grid-header-row-0">
<inp2:m_DefineElement name="sql_column_title">
<td width="100%" nowrap="nowrap">
<span class="columntitle_small">
<img title="" src="../../admin/images/list_arrow_rt.gif" align="absmiddle" border="0">
<inp2:m_param name="value"/>
</span>
</td>
</inp2:m_DefineElement>
<inp2:adm_PrintSqlCols render_as="sql_column_title"/>
</tr>
<inp2:m_DefineElement name="sql_row">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<inp2:m_param name="cells"/>
</tr>
</inp2:m_DefineElement>
<inp2:adm_PrintSqlRows render_as="sql_row"/>
</table>
</td>
</tr>
</table>
</inp2:m_if>
</inp2:m_if>
<inp2:m_include t="incs/footer"/>
\ No newline at end of file
Index: branches/5.3.x/core/admin_templates/incs/style_template.css
===================================================================
--- branches/5.3.x/core/admin_templates/incs/style_template.css (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/style_template.css (revision 15914)
@@ -1,816 +1,819 @@
/*
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
THIS IS STYLESHEET TEMPLATES USED FOR SKINS IN ADMIN
IT'S NOT BEING USED DIRECTLY
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
/* General elements */
html {
height: 100%;
}
body {
font-family: verdana,arial,helvetica,sans-serif;
color: #000000;
overflow-x: auto; overflow-y: auto;
margin: 0px 0px 0px 0px;
text-decoration: none;
}
body, td {
/* fix for Firefox, when font-size was not inherited in table cells */
font-size: 9pt;
}
a {
color: #006699;
text-decoration: none;
}
a:hover {
color: #009ff0;
text-decoration: none;
}
form {
display: inline;
}
img { border: 0px; }
body.height-100 {
height: 100%;
}
body.regular-body {
margin: 0px 10px 5px 10px;
color: #000000;
background-color: @@SectionBgColor@@;
}
body.edit-popup {
margin: 0px 0px 0px 0px;
}
table.collapsed {
border-collapse: collapse;
}
.bordered, table.bordered, .bordered-no-bottom {
border: 1px solid #000000 !important;
border-top-width: 0px;
border-collapse: collapse;
}
.bordered-no-bottom {
border-top-width: 1px;
border-bottom: none;
}
.login-table td {
padding: 1px;
}
.disabled {
background-color: #ebebeb;
}
/* Head frame */
table.head-table {
background: url('@@base_url@@/core/admin_templates/img/top_frame/right_background.png') top right @@HeadBgColor@@ no-repeat;
}
.head-table tr td, .head-table tr td a {
color: @@HeadColor@@
}
div#extra_toolbar td.button-active {
background: url('@@base_url@@/core/admin_templates/img/top_frame/toolbar_button_background.gif') bottom left repeat-x;
height: 22px;
}
div#extra_toolbar td.button-active a {
color: black;
text-decoration: none;
}
td.kx-block-header, .head-table tr td.kx-block-header{
color: @@HeadBarColor@@;
background: url('@@base_url@@/core/admin_templates/img/top_frame/toolbar_background.gif') repeat-x top left;
/*background-color: @@HeadBarBgColor@@;*/
padding-left: 7px;
padding-right: 7px;
}
a.kx-header-link {
text-decoration: underline;
font-weight: bold;
color: #0080C8;
}
a.kx-header-link:hover {
color: #FFCB05;
text-decoration: none;
}
.kx-secondary-foreground {
color: #FFFFFF;
/*background-color: @@HeadBarBgColor@@;*/
}
.kx-login-button {
background-color: #2D79D6;
color: #FFFFFF;
}
/* General form button (yellow) */
.button {
font-size: 12px;
font-weight: normal;
color: #000000;
background: url('@@base_url@@/core/admin_templates/img/button_back.gif') #f9eeae repeat-x;
text-decoration: none;
}
/* Disabled (grayed-out) form button */
.button-disabled {
font-size: 12px;
font-weight: normal;
color: #676767;
background: url('@@base_url@@/core/admin_templates/img/button_back_disabled.gif') #f9eeae repeat-x;
text-decoration: none;
}
/* Tabs bar */
.tab, .tab-active {
background-color: #F0F1EB;
padding: 3px 7px 2px 7px;
border-top: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
margin-left: 3px !important;
white-space: nowrap;
}
.tab-active {
background-color: #4487D9;
}
.tab a {
color: #4487D9;
font-weight: bold;
}
.tab-active a {
color: #FFFFFF;
font-weight: bold;
}
a.scroll-left, a.scroll-right {
cursor: pointer;
display: block;
float: left;
height: 18px;
margin: 0px 1px;
width: 18px;
}
a.scroll-left {
background: transparent url('@@base_url@@/core/admin_templates/img/tabs/left.png') no-repeat scroll 0 0;
}
a.scroll-right {
background: transparent url('@@base_url@@/core/admin_templates/img/tabs/right.png') no-repeat scroll 0 0;
}
a.disabled {
visibility: hidden !important;
}
a.scroll-left:hover, a.scroll-right:hover {
background-position: 0 -18px;
}
td.scroll-right-container {
width: 20px;
}
td.scroll-right-container.disabled, td.scroll-right-container.disabled * {
width: 0px;
margin: 0px;
}
td.scroll-right-container.disabled br {
display: none;
}
/* Toolbar */
.toolbar {
font-size: 8pt;
border: 1px solid #000000;
border-width: 0px 1px 1px 1px;
background-color: @@ToolbarBgColor@@;
border-collapse: collapse;
}
.toolbar td {
height: 100%;
}
.toolbar-button, .toolbar-button-disabled, .toolbar-button-over {
float: left;
text-align: center;
font-size: 8pt;
padding: 5px 5px 5px 5px;
vertical-align: middle;
color: #006F99;
}
.toolbar-button-over {
color: #000;
}
.toolbar-button-disabled {
color: #444;
}
/* Scrollable Grids */
.layout-only-table td {
border: none !important;
}
/* Main Grid class */
.grid-scrollable {
padding: 0px;
border: 1px solid black !important;
border-top: none !important;
}
/* Div generated by js, which contains all the scrollable grid elements, affects the style of scrollable area without data (if there are too few rows) */
.grid-container {
background-color: #fff;
}
.grid-container table {
border-collapse: collapse;
}
/* Inner div generated in each data-cell */
.grid-cell-div {
overflow: hidden;
height: auto;
}
/* Main row definition */
.grid-data-row td, .grid-data-row-selected td, .grid-data-row-even-selected td, .grid-data-row-mouseover td, .table-color1, .table-color2, .grid-edit-table .edit-form-odd > td, .grid-edit-table .edit-form-even > td {
font-weight: normal;
color: @@OddColor@@;
background-color: @@OddBgColor@@;
padding: 3px 5px 3px 5px;
overflow: hidden;
border-right: 1px solid #c9c9c9;
}
.grid-data-row-even td, .table-color2, .grid-edit-table .edit-form-even > td {
background-color: @@EvenBgColor@@;
color: @@EvenColor@@;
}
.grid-data-row td a, .grid-data-row-selected td a, .grid-data-row-mouseover td a {
text-decoration: underline;
}
/* mouse-over rows */
.grid-data-row-mouseover td, table tr.grid-data-row[_row_highlighted] td {
background: #FFFDF4;
}
/* Selected row, applies to both checkbox and data areas */
.grid-data-row-selected td, table tr.grid-data-row[_row_selected] td {
background: #FEF2D6;
}
.grid-data-row-even-selected td, .grid-data-row-even[_row_selected] td {
background: #FFF7E0;
}
/* General header cell definition */
.grid-header-row td {
font-weight: bold;
background-color: @@ColumnTitlesBgColor@@;
text-decoration: none;
padding: 3px 5px 3px 5px;
color: @@ColumnTitlesColor@@;
border-right: none;
text-align: left;
vertical-align: middle !important;
white-space: nowrap;
border-right: 1px solid #777;
}
/* Filters row */
tr.grid-header-row-1 td {
background-color: @@FiltersBgColor@@;
border-bottom: 1px solid black;
}
/* Grid Filters */
table.range-filter {
width: 100%;
}
.range-filter td {
padding: 0px 0px 2px 2px !important;
border: none !important;
font-size: 8pt !important;
font-weight: normal !important;
text-align: left;
color: #000000 !important;
}
input.filter, select.filter, input.filter-active, select.filter-active {
margin-bottom: 0px;
border: 1px solid #aaa;
}
input.filter-active {
background-color: #FFFF00;
}
select.filter-active {
background-color: #FFFF00;
}
div.filter, div.filter-active {
background-color: white;
border: 1px solid #AAAAAA;
color: black;
font-weight: normal;
padding: 3px;
}
div.filter-active {
background-color: #FFFF00;
}
div.multioptions_filter {
position: absolute;
z-index: 100;
color: black;
background-color: white;
border: 1px solid black;
padding: 3px 5px;
display: none;
vertical-align: middle;
}
/* Column titles row */
tr.grid-header-row-0 td {
height: 25px;
font-weight: bold;
background-color: @@ColumnTitlesBgColor@@;
color: @@ColumnTitlesColor@@;
border-bottom: 1px solid black;
}
tr.grid-header-row-0 td a {
color: @@ColumnTitlesColor@@;
}
tr.grid-header-row-0 td a:hover {
color: #FFCC00;
}
.grid-footer-row td {
background-color: #D7D7D7;
font-weight: bold;
border-right: 1px solid #C9C9C9;
padding: 3px 5px 3px 5px;
}
td.grid-header-last-cell, td.grid-data-last-cell, td.grid-footer-last-cell {
border-right: none !important;
}
td.grid-data-col-0, td.grid-data-col-0 div {
text-align: center;
vertical-align: middle !important;
}
tr.grid-header-row-1 td.grid-header-col-1 {
text-align: center;
vertical-align: middle !important;
}
tr.grid-header-row-1 td.grid-header-col-1 div {
display: table-cell;
vertical-align: middle;
}
.grid-status-bar {
border: 1px solid black;
border-top: none;
padding: 0px;
width: 100%;
border-collapse: collapse;
height: 30px;
}
.grid-status-bar td {
background-color: @@TitleBarBgColor@@;
color: @@TitleBarColor@@;
font-size: 11pt;
font-weight: normal;
padding: 2px 8px 2px 8px;
}
/* /Scrollable Grids */
/* Forms */
table.edit-form {
border: none;
border-top-width: 0px !important;
border-collapse: collapse;
width: 100%;
}
.edit-form-odd, .edit-form-even {
padding: 0px;
}
.subsectiontitle {
font-size: 10pt;
font-weight: bold;
background-color: #4A92CE;
color: #fff;
height: 25px;
border-top: 1px solid black;
vertical-align: middle;
}
/* remove top-border from first sub-section element */
table.edit-form .subsectiontitle:first-child, table.bordered .subsectiontitle:first-child {
border-top-width: 0;
}
.subsectiontitle td {
vertical-align: middle;
/*padding: 3px 5px 3px 5px;*/
padding: 1px 5px;
}
.label-cell {
background: #DEE7F6 url('@@base_url@@/core/admin_templates/img/bgr_input_name_line.gif') no-repeat right bottom;
font: 12px arial, sans-serif;
padding: 4px 20px;
width: 160px;
}
.control-mid {
width: 13px;
border-left: 1px solid #7A95C2;
background: #fff url('@@base_url@@/core/admin_templates/img/bgr_mid.gif') repeat-x left bottom;
}
.control-cell {
font: 11px arial, sans-serif;
padding: 4px 10px 5px 5px;
background: #fff url('@@base_url@@/core/admin_templates/img/bgr_input_line.gif') no-repeat left bottom;
width: auto;
vertical-align: middle;
}
.CodeMirror {
font-size: 13px;
border: 1px solid black;
}
+.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
+.CodeMirror-activeline-background {background: #e8f2ff !important;}
+
.label-cell-filler {
background: #DEE7F6 none;
}
.control-mid-filler {
background: #fff none;
border-left: 1px solid #7A95C2;
}
.control-cell-filler {
background: #fff none;
}
.highlight-area, .code-highlight-area {
border: 1px solid black;
padding: 8px;
font-family: monospace !important;
font-size: 12px;
overflow: auto;
}
.code-highlight-area {
background-color: #F6F6F6;
}
.error {
color: red;
}
.error-cell {
color: red;
}
.field-required {
color: red;
}
.warning-table {
background-color: #F0F1EB;
border: 1px solid #000000;
border-collapse: collapse;
border-top-width: 0px;
}
.form-notice, .form-warning {
font-size: 11px;
}
.form-warning {
color: red;
}
.form-notice {
color: green;
}
.priority {
color: red;
padding-left: 1px;
padding-right: 1px;
font-size: 11px;
}
.small-statistics {
font-size: 11px;
color: #707070;
}
.req-note {
font-style: italic;
color: #333;
}
#scroll_container table.tableborder {
border-collapse: separate
}
/* Uploader */
.uploader-queue div.file {
font-size: 11px;
border: 1px solid #7F99C5;
padding: 3px;
background-color: #DEE7F6;
margin-bottom: 2px;
}
.uploader-queue .left {
float: left;
vertical-align: top;
}
.uploader-queue .file-label {
margin-left: 5px;
}
.uploader-queue .preview .delete-checkbox {
margin-top: -3px;
}
.uploader-queue .progress-container {
margin: 2px 5px 0px 5px;
}
.uploader-queue .progress-empty {
width: 150px;
height: 9px;
border: 1px solid black;
background: url('@@base_url@@/core/admin_templates/img/progress_left.gif') repeat-x;
}
.uploader-queue .progress-full {
height: 9px;
background: url('@@base_url@@/core/admin_templates/img/progress_done.gif');
}
.uploader-queue .thumbnail {
/*margin-bottom: 2px;*/
border: 1px solid black;
background-color: grey;
}
/* To be sorted */
span#category_path, span#category_path a {
color: #FFFFFF;
}
span#category_path a {
text-decoration: underline;
}
/* Section title, right to the big icon */
.admintitle {
font-size: 16pt;
font-weight: bold;
color: @@SectionColor@@;
text-decoration: none;
}
/* Page header (bluebar) */
.page-title td {
background-color: @@TitleBarBgColor@@;
color: @@TitleBarColor@@;
font-size: 11pt;
font-weight: normal;
padding: 2px 8px 2px 8px;
}
/* Right side of bluebar */
.tablenav, tablenav a {
font-size: 11pt;
font-weight: bold;
color: @@TitleBarColor@@;
text-decoration: none;
background-color: @@TitleBarBgColor@@;
background-image: none;
}
/* Section title in the bluebar * -- why 'link'? :S */
.tablenav_link {
font-size: 11pt;
font-weight: bold;
color: @@TitleBarColor@@;
text-decoration: none;
}
/* Active page in top and bottom bluebars pagination */
.current_page {
font-size: 10pt;
font-weight: bold;
background-color: #fff;
color: #2D79D6;
padding: 3px 2px 3px 3px;
}
/* Other pages and arrows in pagination on blue */
.nav_url {
font-size: 10pt;
font-weight: bold;
color: #fff;
padding: 3px 2px 3px 3px;
}
/* Tree */
.tree-body {
background-color: @@TreeBgColor@@;
height: 100%
}
.tree_head.td, .tree_head, .tree_head:hover {
font-weight: bold;
font-size: 10px;
color: #FFFFFF;
font-family: Verdana, Arial;
text-decoration: none;
}
.tree {
padding: 0px;
border: none;
border-collapse: collapse;
}
.tree tr td {
padding: 0px;
margin: 0px;
font-family: helvetica, arial, verdana,;
font-size: 11px;
white-space: nowrap;
}
.tree tr td a {
font-size: 11px;
color: @@TreeColor@@;
font-family: Helvetica, Arial, Verdana;
text-decoration: none;
padding: 2px;
}
.tree tr td a:hover, .tree tr td a.debug-only-item:hover {
color: @@TreeHoverColor@@;
}
.tree tr.highlighted td a, .tree tr.highlighted td a.debug-only-item {
color: @@TreeHighColor@@;
background-color: @@TreeHighBgColor@@;
}
.tree tr.highlighted td a:hover {
color: @@TreeHighHoverColor@@;
}
.tree tr td a.debug-only-item {
color: grey;
}
/* Ajax Dropdown */
.suggest-box {
border: 1px solid #999;
background-color: #fff;
}
.suggest-item, .suggest-item-over {
padding: 1px 2px 0px 2px;
font-family: arial,verdana;
font-size: 12px;
}
.suggest-item-over {
background-color: #3366CC;
color: #fff;
}
/* Dashboard Summary Boxes */
.summary-box {
border: 1px solid black;
margin-bottom: 4px;
}
.summary-box .title {
color: white;
font-weight: bold;
padding: 6px 5px;
vertical-align: middle;
background-color: #4A92CE;
border-bottom: 1px solid black;
}
.summary-box .content {
padding: 4px;
background-color: #F6F6F6;
}
.summary-box .group {
border-bottom: 1px solid black;
margin-bottom: 10px;
padding: 0 0 10px 10px;
}
.summary-box .group.last {
border-width: 0px;
margin-bottom: 0;
padding-bottom: 5px;
}
.summary-box h4 {
margin: 0;
padding: 0 0 3px 0;
font-size: 11px;
font-weight: bold;
}
.summary-box .hint {
font-size: 10px;
color: grey;
margin-bottom: 3px;
}
.summary-box .hint .cache-key {
margin-bottom: 7px;
margin-left: 3px;
}
.summary-box ul {
margin-top: 5px;
margin-bottom: 3px;
padding-left: 30px;
}
.summary-box li {
padding-bottom: 4px;
}
span.cke_skin_kama {
border-width: 0px !important;
-moz-border-radius: 0px !important;
-webkit-border-radius: 0px !important;
padding: 0px !important;
}
.cke_wrapper{
border-width: 0px !important;
-moz-border-radius: 0px !important;
-webkit-border-radius: 0px !important;
}
\ No newline at end of file
Index: branches/5.3.x/core/admin_templates/incs/form_blocks.tpl
===================================================================
--- branches/5.3.x/core/admin_templates/incs/form_blocks.tpl (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/form_blocks.tpl (revision 15914)
@@ -1,1287 +1,1287 @@
<inp2:m_Set tab_index="1"/>
<inp2:m_DefineElement name="combined_header" permission_type="view" perm_section="" perm_prefix="" perm_event="" system_permission="1" title_preset="" tab_preset="" additional_title_render_as="" additional_blue_bar_render_as="" pagination_prefix="" parent="1" grid="Default">
<inp2:m_if check="m_Param" name="perm_section" inverse="1">
<inp2:adm_SectionInfo section="$section" info="perm_section" result_to_var="perm_section"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="permission_type">
<inp2:m_RequireLogin permissions="{$perm_section}.{$permission_type}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/>
<inp2:m_else/>
<inp2:m_RequireLogin permissions="{$perm_section}" perm_event="$perm_event" perm_prefix="$perm_prefix" system="$system_permission"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="prefix" inverse="1"><inp2:adm_SectionInfo section="$section" info="SectionPrefix" result_to_var="prefix"/></inp2:m_if>
<inp2:m_if check="m_get" var="m_wid" inverse="1">
<inp2:m_if check="m_GetConfig" name="UseSmallHeader">
<img src="img/spacer.gif" height="8" width="1" alt=""/>
<inp2:m_else/>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##-->
<tr>
<td valign="top" class="admintitle" align="left" <!--##style="padding-top: 10px; padding-bottom: 10px;"##-->>
<img width="46" height="46" src="<inp2:adm_SectionInfo section='$section' parent='$parent' info='module_path'/>img/icons/icon46_<inp2:adm_SectionInfo section='$section' parent='$parent' info='icon'/>.png" align="absmiddle" title="<inp2:adm_SectionInfo section='$section' parent='$parent' info='label' no_editing='1'/>" alt=""/><inp2:adm_SectionInfo section="$section" parent="$parent" info="label"/>
</td>
<inp2:m_if check="m_Param" name="additional_title_render_as">
<inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/>
</inp2:m_if>
</tr>
</table>
</inp2:m_if>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="additional_title_render_as">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<!--## <tr<inp2:m_ifnot check="m_ModuleEnabled" module="Proj-Base"> style="background: url(<inp2:adm_SectionInfo section="$section" parent="$parent" info="module_path"/>img/logo_bg.gif) no-repeat top right; height: 55px;"</inp2:m_ifnot>> ##-->
<tr>
<inp2:m_RenderElement name="$additional_title_render_as" pass_params="1"/>
</tr>
</table>
</inp2:m_if>
</inp2:m_if>
<inp2:$prefix_ModifyUnitConfig pass_params="1"/>
<inp2:m_if check="m_Param" name="tabs">
<inp2:m_include t="$tabs" pass_params="1"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="tab_preset">
<inp2:m_RenderElement name="edit_tabs" prefix="$prefix" preset_name="$tab_preset"/>
</inp2:m_if>
<table border="0" cellpadding="2" cellspacing="0" class="page-title bordered-no-bottom" width="100%" style="height: 30px;">
<tr>
<td nowrap="nowrap" style="vertical-align: middle;">
<inp2:adm_SectionInfo section="$section" info="label" result_to_var="default_title"/>
<inp2:adm_SectionInfo section="$section" parent="$parent" info="label" result_to_var="group_title"/>
<span class="tablenav_link" id="blue_bar">
<inp2:$prefix_SectionTitle title_preset="$title_preset" section="$section" title="$default_title" group_title="$group_title" cut_first="100" pass_params="true"/>
</span>
</td>
<td align="right" class="tablenav" style="vertical-align: middle;">
<inp2:m_if check="m_Param" name="additional_blue_bar_render_as">
<inp2:m_RenderElement name="$additional_blue_bar_render_as" pass_params="1"/>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="pagination">
<inp2:$prefix_SelectParam possible_names="pagination_prefix,prefix" result_to_var="pagination_prefix"/>
<inp2:m_RenderElement name="grid_pagination_elem" PrefixSpecial="$pagination_prefix" pass_params="1"/>
</inp2:m_if>
</inp2:m_if>
</td>
</tr>
</table>
<script type="text/javascript">
var $visible_toolbar_buttons = <inp2:m_if check="{$prefix}_VisibleToolbarButtons" title_preset="$title_preset">[<inp2:$prefix_VisibleToolbarButtons title_preset="$title_preset"/>]<inp2:m_else/>true</inp2:m_if>;
var $allow_dbl_click = ($visible_toolbar_buttons === true) || in_array('dbl-click', $visible_toolbar_buttons);
set_window_title( $.trim( $('#blue_bar').text().replace(/\s+/g, ' ') ) + ' - <inp2:m_Phrase label="la_AdministrativeConsole" js_escape="1"/>');
setHelpLink('<inp2:lang.current_Field name="UserDocsUrl" js_escape="1"/>', '<inp2:m_Param name="title_preset" js_escape="1"/>');
</script>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_original_label">
<td><inp2:$prefix.original_Field field="$field" nl2br="1"/></td>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="subsection" prefix="" fields="" colspan="3">
<inp2:m_if check="m_Param" name="prefix" equals_to="">
<tr class="subsectiontitle">
<td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td>
</tr>
<inp2:m_else/>
<inp2:m_if check="{$prefix}_FieldsVisible" fields="$fields">
<tr class="subsectiontitle">
<td colspan="<inp2:m_param name='colspan'/>"><inp2:m_phrase label="$title"/></td>
<inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1">
<td><inp2:m_phrase name="$original_title"/></td>
</inp2:m_if>
</tr>
</inp2:m_if>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="form_message" id="" type="warning">
<table width="100%" cellspacing="0" cellpadding="4" class="warning-table"<inp2:m_if check="m_Param" name="id"> id="<inp2:m_Param name='id'/>"</inp2:m_if>>
<tr>
<td valign="top" class="form-<inp2:m_Param name='type'/>">
<inp2:m_Param name="content"/>
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="default_field_caption_element">
<label for="<inp2:m_param name='NamePrefix'/><inp2:{$prefix}_InputName field='$field'/>">
<span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>"><inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title"/></inp2:m_else/><inp2:m_Param name="title_text"/></inp2:m_if></span></span><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required">&nbsp;*</span></inp2:m_if>:<inp2:m_if check="{$prefix}_FieldHintLabel" title_label="$title" direct_label="$hint_label"><span>&nbsp;<img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>" alt="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>"/></inp2:m_if>
</label>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_field_caption" title="la_fld_{$field}" title_text="" hint_label="" NamePrefix="">
<inp2:m_inc param="tab_index" by="1"/>
<td class="label-cell" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:m_if check="m_Param" name="title">
<inp2:m_RenderElement name="$caption_render_as" pass_params="1"/>
<inp2:m_else/>
<inp2:m_if check="m_Param" name="title_text">
<inp2:m_RenderElement name="$caption_render_as" pass_params="1"/>
<inp2:m_else/>
&nbsp;
</inp2:m_if>
</inp2:m_if>
</td>
<td class="control-mid">&nbsp;</td>
<script type="text/javascript">
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_if check="m_Param" name="title"><inp2:m_phrase label="$title" js_escape="1"/></inp2:m_else/><inp2:m_Param name="title_text" js_escape="1"/></inp2:m_if>'
</script>
</inp2:m_DefineElement>
<!--## design default parameters only avaible in design block ##-->
<inp2:m_DefineElement name="form_row" error_field_suffix="" block_name="" title="la_fld_{$field}" has_caption="1" caption_render_as="default_field_caption_element" style="" hint_label="" is_last="">
<inp2:m_if check="{$prefix}_FieldVisible" field="$field">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row"<inp2:m_if check="m_Param" name="row_style"> style="<inp2:m_Param name='row_style'/>"</inp2:m_if>>
<inp2:m_if check="m_Param" name="has_caption">
<inp2:m_RenderElement name="inp_edit_field_caption" field="{$field}{$error_field_suffix}" pass_params="1"/>
</inp2:m_if>
<inp2:m_Param name="content" pass_params="1"/>
<inp2:m_RenderElement name="inp_edit_error" prefix="$prefix" field="{$field}{$error_field_suffix}" block_name="$block_name"/>
<inp2:m_if check="{$prefix}_DisplayOriginal" pass_params="1">
<inp2:m_RenderElement prefix="$prefix" field="$field" name="inp_original_label"/>
</inp2:m_if>
</tr>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_label" style="" format="" db="" as_label="" currency="" no_special="" nl2br="0" with_hidden="0" after_text="">
<inp2:m_RenderElement design="form_row" block_name="inp_label" pass_params="1">
<td class="control-cell" valign="top">
<span style="<inp2:m_Param name='style'/>" id="<inp2:$prefix_InputName field='$field'/>">
<inp2:{$prefix}_Field field="$field" format="$format" as_label="$as_label" currency="$currency" nl2br="$nl2br" no_special="$no_special"/><inp2:m_Param name="after_text"/>
</span>
<inp2:m_if check="m_Param" name="with_hidden">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>">
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_id_label">
<inp2:m_ifnot check="Field" field="$field" equals_to="|0">
<inp2:m_RenderElement name="inp_label" pass_params="true"/>
</inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_error" block_name="">
<script type="text/javascript">
add_form_error('<inp2:m_Param name="prefix" js_escape="1"/>', '<inp2:m_Param name="field" js_escape="1"/>', '<inp2:{$prefix}_InputName field="$field"/>', '<inp2:{$prefix}_Error field="$field" js_escape="1"/>', '<inp2:m_Param name="block_name" js_escape="1"/>');
</script>
<!--##<td class="error-cell"><inp2:{$prefix}_Error field="$field"/>&nbsp;</td>##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_box" class="" format="" maxlength="" onblur="" onchange="" size="" onkeyup="" allow_html="" edit_template="popups/editor" style="width: 100%" after_text="" autocomplete="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_box" pass_params="1">
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='$format'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>" onkeyup="<inp2:m_Param name='onkeyup'/>" onchange="<inp2:m_Param name='onchange'/>" autocomplete="<inp2:m_Param name='autocomplete'/>"><inp2:m_Param name="after_text"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_timezone" onchange="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_timezone" pass_params="1">
<td class="control-cell">
<script type="text/javascript">
var $timezones = {
'Africa': ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Asmera', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Timbuktu', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek'],
'America': ['America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Atka', 'America/Bahia', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Buenos_Aires', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Catamarca', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Coral_Harbour', 'America/Cordoba', 'America/Costa_Rica', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Ensenada', 'America/Fort_Wayne', 'America/Fortaleza', 'America/Glace_Bay', 'America/Godthab', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Jujuy', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Louisville', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Mendoza', 'America/Menominee', 'America/Merida', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montreal', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nipigon', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Ojinaga', 'America/Panama', 'America/Pangnirtung', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Rosario', 'America/Santa_Isabel', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Shiprock', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Thunder_Bay', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Virgin', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'America/Yellowknife'],
'Antarctica': ['Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Vostok'],
'Arctic': ['Arctic/Longyearbyen'],
'Asia': ['Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Ashkhabad', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Calcutta', 'Asia/Choibalsan', 'Asia/Chongqing', 'Asia/Chungking', 'Asia/Colombo', 'Asia/Dacca', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Gaza', 'Asia/Harbin', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Istanbul', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kashgar', 'Asia/Kathmandu', 'Asia/Katmandu', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macao', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qyzylorda', 'Asia/Rangoon', 'Asia/Riyadh', 'Asia/Saigon', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Tel_Aviv', 'Asia/Thimbu', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Ujung_Pandang', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator', 'Asia/Urumqi', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yekaterinburg', 'Asia/Yerevan'],
'Atlantic': ['Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faeroe', 'Atlantic/Faroe', 'Atlantic/Jan_Mayen', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley'],
'Australia': ['Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/North', 'Australia/NSW', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna'],
'Europe': ['Europe/Amsterdam', 'Europe/Andorra', 'Europe/Athens', 'Europe/Belfast', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Nicosia', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Tiraspol', 'Europe/Uzhgorod', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zaporozhye', 'Europe/Zurich'],
'Indian': ['Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion'],
'Pacific': ['Pacific/Apia', 'Pacific/Auckland', 'Pacific/Chatham', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Enderbury', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Johnston', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Ponape', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Samoa', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Truk', 'Pacific/Wake', 'Pacific/Wallis', 'Pacific/Yap'],
'Others': ['UTC']
}
function change_timezone_group($timezone_group) {
var $target = $( jq('#<inp2:InputName name="$field"/>') );
$target.hide().empty();
$target.append('<option value=""></option>');
if ($timezone_group == '') {
return ;
}
for (var $i = 0; $i < $timezones[$timezone_group].length; $i++) {
var $timezone = $timezones[$timezone_group][$i];
var $selected = $timezone == $current_timezone ? ' selected="selected"' : '';
$target.append('<option value="' + $timezone + '" ' + $selected + '>' + $timezone + '</option>');
}
$target.show();
}
var $current_timezone = '<inp2:Field name="$field"/>';
$(document).ready(
function () {
$('#timezone_group').change(
function ($e) {
change_timezone_group( $(this).val() );
}
);
var $current_timezone_group = '';
for (var $timezone_group in $timezones) {
if (in_array($current_timezone, $timezones[$timezone_group])) {
$current_timezone_group = $timezone_group;
break;
}
}
$('#timezone_group').val($current_timezone_group).change();
}
);
</script>
<select id="timezone_group" tabindex="<inp2:m_Get name='tab_index'/>">
<option value=""></option>
<option value="Africa">Africa</option>
<option value="America">America</option>
<option value="Antarctica">Antarctica</option>
<option value="Arctic">Arctic</option>
<option value="Asia">Asia</option>
<option value="Atlantic">Atlantic</option>
<option value="Australia">Australia</option>
<option value="Europe">Europe</option>
<option value="Indian">Indian</option>
<option value="Pacific">Pacific</option>
<option value="Others">Others</option>
</select>
&nbsp;&nbsp;
<select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>"></select>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_password" class="" size="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_password" pass_params="1">
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" autocomplete="off" type="password" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field name='{$field}_plain'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" />
<script type="text/javascript">
$(document).ready(
function() {
<inp2:m_ifnot check="{$prefix}_Field" name="{$field}_plain">
$('#' + jq('<inp2:{$prefix}_InputName field="$field"/>')).val('');
</inp2:m_ifnot>
}
);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_upload" class="" size="" thumbnail="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_upload" pass_params="1">
<td class="control-cell">
<inp2:m_if check="m_Param" name="thumbnail">
<inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse">
<img src="<inp2:{$prefix}_Field field='$field' format='resize:{$thumbnail}'/>" alt=""/><br />
<table cellpadding="0" cellspacing="0">
<tr>
<td>
<input type="hidden" id="<inp2:{$prefix}_InputName field='Delete{$field}'/>" name="<inp2:{$prefix}_InputName field='Delete{$field}'/>" value="0" />
<input type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>" tabindex="<inp2:m_Get name='tab_index'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='Delete{$field}'/>'));">
</td>
<td>
<label for="_cb_<inp2:{$prefix}_InputName field='Delete{$field}'/>"><inp2:m_phrase name="la_btn_Delete"/></label>
</td>
</tr>
</table>
</inp2:m_if>
<input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>">
<inp2:m_else/>
<input type="file" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>">
<inp2:m_if check="{$prefix}_FieldEquals" name="$field" value="" inverse="inverse">
(<inp2:{$prefix}_Field field="$field"/>)
</inp2:m_if>
</inp2:m_if>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field'/>">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_box_ml">
<inp2:m_RenderElement name="inp_edit_box" format="no_default" pass_params="true"/>
<!--##
<inp2:m_RenderElement design="form_row" block_name="inp_edit_box_ml" pass_params="1">
<td class="label-cell" valign="top">
<span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>" >
<inp2:m_phrase label="$title"/><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required">&nbsp;*</span></inp2:m_if>:</span><br>
<a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name='prefix'/>', '<inp2:m_param name='field'/>', 'popups/translator');" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" style="cursor:hand" border="0"></a>
</td>
<td class="control-cell">
<input style="<inp2:m_Param name='style'/>" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='no_default'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
</td>
</inp2:m_RenderElement>
##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_swf_upload" LinkPrefix="m" LinkTag="t" class="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_swf_upload" pass_params="1">
<td class="control-cell">
<div style="width: 63px; height: 21px;" id="<inp2:{$prefix}_InputName field='$field'/>_place_holder">
&nbsp;
</div>
<div id="<inp2:{$prefix}_InputName field='$field'/>_queueinfo" class="uploader-queue"></div>
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[upload]" id="<inp2:{$prefix}_InputName field='$field'/>[upload]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[order]" id="<inp2:{$prefix}_InputName field='$field'/>[order]" value="<inp2:{$prefix}_Field field='$field' format='file_names'/>">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_ids]" value="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_names]" value="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" id="<inp2:{$prefix}_InputName field='$field'/>[tmp_deleted]" value="">
<script type="text/javascript">
UploadsManager.AddUploader('<inp2:{$prefix}_InputName field="$field"/>',
{
baseUrl: '<inp2:m_TemplatesBase />',
allowedFiletypesDescription : '<inp2:{$prefix}_FieldOption field="$field" option="files_description" result_to_var="files_description"/><inp2:m_Phrase name="$files_description" js_escape="1"/>',
allowedFiletypes : '<inp2:{$prefix}_FieldOption field="$field" option="file_types"/>',
allowedFilesize : '<inp2:{$prefix}_FieldOption field="$field" option="max_size"/>',
multiple : '<inp2:{$prefix}_FieldOption field="$field" option="multiple"/>',
prefix : '<inp2:m_Param name="prefix"/>',
field : '<inp2:m_Param name="field"/>',
thumb_format: '<inp2:{$prefix}_FieldOption field="$field" option="thumb_format"/>',
urls : '<inp2:{$prefix}_Field field="$field" format="file_urls" no_special="1" js_escape="1"/>',
names : '<inp2:{$prefix}_Field field="$field" format="file_names" no_special="1" js_escape="1"/>',
sizes : '<inp2:{$prefix}_Field field="$field" format="file_sizes" no_special="1" js_escape="1"/>',
flashsid : '<inp2:m_SID/>',
uploadURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnUploadFile" js_escape="1" no_amp="1" />',
deleteURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnDeleteFile" field_id="#FIELD_ID#" file="#FILE#" js_escape="1" no_amp="1"/>',
previewURL : '<inp2:{$LinkPrefix}_{$LinkTag} pass="all,$prefix" {$prefix}_event="OnViewFile" field="#FIELD#" file="#FILE#" js_escape="1" no_amp="1" />',
// Button settings
buttonImageURL: 'img/upload.png', // Relative to the Flash file
buttonWidth: 63,
buttonHeight: 21,
buttonText: '<span class="theFont">Browse</span>',
buttonTextStyle: ".theFont { font-size: 12; font-family: arial, sans}",
buttonTextTopPadding: 2,
buttonTextLeftPadding: 9,
buttonPlaceholderId: '<inp2:{$prefix}_InputName field="$field"/>_place_holder',
ajax: <inp2:m_if check="m_Get" name="ajax" equals_to="yes">true<inp2:m_else/>false</inp2:m_if>
}
)
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_hidden" db="">
<input type="hidden" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='$db'/>">
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date" class="" error_field_suffix="_date">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_date" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif">&nbsp;
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field='{$field}'/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span>
<script type="text/javascript">
Calendar.setup({
inputField : "<inp2:{$prefix}_InputName field='{$field}_date'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}_date' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>,
onUpdate : function(cal) {
runOnChange('<inp2:$prefix_InputName field='{$field}_date'/>');
}
});
</script>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time' input_format='1'/>" value="">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_time" class="" error_field_suffix="_time">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_time" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>">&nbsp;
<span class="small">(<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date' input_format='1'/>" value="">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date_time" class="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_date_time" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_date'/>" id="<inp2:{$prefix}_InputName field='{$field}_date'/>" value="<inp2:{$prefix}_Field field='{$field}_date' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_date' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif">
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="{$field}_date" input_format="1" human="true"/>)</span>
<input type="hidden" id="full_date_<inp2:{$prefix}_InputName field='{$field}'/>" value="<inp2:{$prefix}_Field field='{$field}' format=''/>" />
<script type="text/javascript">
Calendar.setup({
inputField : "full_date_<inp2:{$prefix}_InputName field='{$field}'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>,
onUpdate : function(cal) {
document.getElementById('<inp2:{$prefix}_InputName field="{$field}_date"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_date" input_format="1"/>") );
document.getElementById('<inp2:{$prefix}_InputName field="{$field}_time"/>').value = cal.date.print( Calendar.phpDateFormat("<inp2:{$prefix}_Format field="{$field}_time" input_format="1"/>") );
}
});
</script>
&nbsp;<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_time'/>" id="<inp2:{$prefix}_InputName field='{$field}_time'/>" value="<inp2:{$prefix}_Field field='{$field}_time' format='_input_'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:{$prefix}_Format field='{$field}_time' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>"><span class="small"> (<inp2:{$prefix}_Format field="{$field}_time" input_format="1" human="true"/>)</span>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_date_time_combined" class="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_date_time_combined" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' format='_input_'/>" tabindex="<inp2:m_get param='tab_index'/>" size="<inp2:{$prefix}_Format field='$field' input_format='1' edit_size='edit_size'/>" class="<inp2:m_param name='class'/>" datepickerIcon="<inp2:m_ProjectBase/>core/admin_templates/img/calendar_icon.gif"/>
<img src="img/calendar_icon.gif" id="cal_img_<inp2:{$prefix}_InputName field="{$field}"/>"
style="cursor: pointer; margin-right: 5px"
title="Date selector"
/>
<span class="small">(<inp2:{$prefix}_Format field="$field" input_format="1" human="true"/>)</span>
<script type="text/javascript">
Calendar.setup({
inputField : "<inp2:{$prefix}_InputName field='{$field}'/>",
ifFormat : Calendar.phpDateFormat("<inp2:{$prefix}_Format field='{$field}' input_format='1'/>"),
button : "cal_img_<inp2:{$prefix}_InputName field='{$field}'/>",
align : "br",
singleClick : true,
showsTime : true,
weekNumbers : false,
firstDay : <inp2:m_GetConfig var="FirstDayOfWeek"/>
});
</script>
<input type="hidden" name="<inp2:{$prefix}_InputName field='{$field}_combined'/>" value="1"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="textarea_field_caption_element">
<inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/>
<inp2:m_if check="m_Param" name="allow_html">
<br>
<inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/>
<a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '950x600'); return false;">
<img src="img/icons/icon24_link_editor.gif" border="0">
</a>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_textarea" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_textarea" pass_params="1">
<td class="control-cell">
<textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" ><inp2:{$prefix}_Field field="$field" format="fck_ready;{$format}"/></textarea>
<script type="text/javascript">
Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_fck" class="" title="la_fld_{$field}" maxlength="" bgcolor="" body_class="" body_id="" onblur="" format="" size="" onkeyup="" style="" has_caption="0" control_options="false">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_fck" pass_params="1">
<td class="control-cell" style="padding: 0px;" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:FCKEditor field="$field" width="100%" height="200" bgcolor="$bgcolor" body_class="$body_class" body_id="$body_id" format="$format" late_load="1"/>
<script type="text/javascript">
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>'
Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_codepress" style="width: 100%;" language="html" has_caption="0" control_options="false">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_codepress" pass_params="1">
<td class="control-cell" colspan="3" onmouseover="show_form_error('<inp2:m_Param name='prefix' js_escape='1'/>', '<inp2:m_Param name='field' js_escape='1'/>')" onmouseout="hide_form_error('<inp2:m_Param name='prefix' js_escape='1'/>')">
<inp2:m_ifnot check="m_Get" name="codepress_included">
<script type="text/javascript" src="<inp2:m_TemplatesBase/>/themes/codepress/codepress.js"></script>
<script type="text/javascript">
CodePress.path = '<inp2:m_TemplatesBase/>/themes/codepress/'; // set path here, because script tags are not found in table cells
</script>
<inp2:m_Set codepress_included="1"/>
</inp2:m_ifnot>
<textarea id="<inp2:$prefix_InputName field='$field'/>" name="<inp2:$prefix_InputName field='$field'/>" class="codepress <inp2:m_Param name='language'/>" style="<inp2:m_Param name='style'/>"><inp2:$prefix_Field field="$field"/></textarea>
<script type="text/javascript">
Application.setHook(
new Array ('<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGoToTab', '<inp2:m_Param name="prefix" js_escape="1"/>:OnPreSaveAndGo', '<inp2:m_Param name="prefix" js_escape="1"/>:OnSave', '<inp2:m_Param name="prefix" js_escape="1"/>:OnCreate', '<inp2:m_Param name="prefix" js_escape="1"/>:OnUpdate'),
function($event) {
<inp2:m_Param name="field"/>.toggleEditor(); // enable textarea back to save data
$event.status = true;
}
);
if (typeof(fields['<inp2:m_Param name="prefix" js_escape="1"/>']) == 'undefined') {
fields['<inp2:m_Param name="prefix" js_escape="1"/>'] = new Object();
}
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>'] = '<inp2:m_phrase label="$title" js_escape="1"/>'
Form.addControl('<inp2:$prefix_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="code_mirror_resource">
<inp2:m_if check="m_Param" name="resource_extension" equals_to="js">
<script src="<inp2:m_TemplatesBase/>/incs/code_mirror/mode/<inp2:m_Param name='resource_file'/>"></script>
<inp2:m_elseif check="m_Param" name="resource_extension" equals_to="js"/>
<link rel="stylesheet" href="<inp2:m_TemplatesBase/>/incs/code_mirror/mode/<inp2:m_Param name='resource_file'/>"/>
</inp2:m_if>
</inp2:m_DefineElement>
-<inp2:m_DefineElement name="inp_edit_codemirror" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto" codemirror_options="lineNumbers: true">
+<inp2:m_DefineElement name="inp_edit_codemirror" caption_render_as="textarea_field_caption_element" class="" format="" edit_template="popups/editor" allow_html="" style="text-align: left; width: 100%; height: 100px;" control_options="false" row_style="height: auto" codemirror_options="lineNumbers: true, styleActiveLine: true">
<inp2:m_RenderElement design="form_row" pass_params="1">
<td class="control-cell">
<textarea tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" ><inp2:{$prefix}_Field field="$field" format="fck_ready;{$format}"/></textarea>
<inp2:m_ifnot check="m_Get" name="codemirror_included">
- <script src="<inp2:m_TemplatesBase/>/incs/code_mirror/lib/codemirror.js"></script>
- <link rel="stylesheet" href="<inp2:m_TemplatesBase/>/incs/code_mirror/lib/codemirror.css">
+ <script src="<inp2:m_Compress files='incs/code_mirror/lib/codemirror.js|incs/code_mirror/addon/selection/active-line.js'/>"></script>
+ <link rel="stylesheet" href="<inp2:m_Compress files='incs/code_mirror/lib/codemirror.css'/>">
<inp2:m_Set codemirror_included="1"/>
</inp2:m_ifnot>
<inp2:adm_IncludeCodeMirrorFilesByLanguage language="$language" render_as="code_mirror_resource"/>
<script type="text/javascript">
$CodeMirrorEditors["<inp2:$prefix_InputName field='$field'/>"] = {mode: "<inp2:m_Param name='language'/>", tabindex: <inp2:m_Get name="tab_index"/>, indentWithTabs: true, indentUnit: 4, lineWrapping: true<inp2:m_if check="m_Param" name="codemirror_options">, <inp2:m_Param name="codemirror_options"/></inp2:m_if>};
Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="textarea_ml_field_caption_element">
<inp2:m_RenderElement name="default_field_caption_element" pass_params="1"/>
<br>
<inp2:m_if check="m_ParamEquals" name="allow_html" value="allow_html">
<inp2:{$prefix}_InputName field="$field" result_to_var="input_name"/>
<a href="<inp2:m_Link template='$edit_template' TargetField='$input_name' pass_through='TargetField' pass='m,$prefix'/>" onclick="openSelector('<inp2:m_Param name='prefix' js_escape='1'/>', this.href, '', '800x575'); return false;">
<img src="img/icons/icon24_link_editor.gif" style="cursor: hand;" border="0">
</a>
</inp2:m_if>
<a href="javascript:PreSaveAndOpenTranslator('<inp2:m_param name="prefix"/>', '<inp2:m_param name="field"/>', 'popups/translator', 1);" title="<inp2:m_Phrase label='la_Translate'/>"><img src="img/icons/icon24_translate.png" border="0"></a>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="source_language_field_caption_element">
<label for="<inp2:m_param name='NamePrefix'/><inp2:{$prefix}_InputName field='$field'/>">
<span class="<inp2:m_if check='{$prefix}_HasError' field='$field'>error-cell</inp2:m_if>"><inp2:$prefix_SourceLanguageTitle label="$title"/></span><inp2:m_if check="{$prefix}_IsRequired" field="$field"><span class="field-required">&nbsp;*</span></inp2:m_if>:<inp2:m_if check="{$prefix}_FieldHintLabel" title_label="$title" direct_label="$hint_label"><span>&nbsp;<img src="<inp2:m_TemplatesBase/>/img/hint_icon.png" width="12" height="13" title="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>" alt="<inp2:$prefix_FieldHintLabel title_label='$title' direct_label='$hint_label' html_escape='1'/>"/></inp2:m_if>
</label>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_textarea_ml">
<inp2:m_RenderElement name="inp_edit_textarea" format="no_default" pass_params="true"/>
<!--##<inp2:m_RenderElement design="form_row" block_name="inp_edit_textarea_ml" pass_params="1">
<td class="control-cell">
<textarea style="<inp2:m_Param name='style'/>" tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" cols="<inp2:m_param name='cols'/>" rows="<inp2:m_param name='rows'/>" class="<inp2:m_param name='class'/>"><inp2:{$prefix}_Field field="$field" format="fck_ready,{$format}"/></textarea>
<script type="text/javascript">
Form.addControl('<inp2:{$prefix}_InputName field="$field"/>', <inp2:m_param name="control_options"/>);
</script>
</td>
</inp2:m_RenderElement>##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_user" class="" size="" old_style="0" onkeyup="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_user" pass_params="1">
<td class="control-cell">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" class="<inp2:m_param name='class'/>" onkeyup="<inp2:m_Param name='onkeyup'/>">
<inp2:m_if check="m_ParamEquals" name="old_style" value="1">
<a href="#" onclick="return OpenUserSelector('','kernel_form','<inp2:{$prefix}_InputName field="$field"/>');">
<inp2:m_else/>
<a href="<inp2:m_t t='user_selector' pass='all,$prefix'/>" onclick="openSelector('<inp2:m_param name='prefix' js_escape='1'/>', this.href, '<inp2:m_param name='field' js_escape='1'/>'); return false;">
</inp2:m_if>
<img src="img/icons/icon24_link_user.gif" style="cursor:hand;" border="0">
</a>
<script type="text/javascript">
function processUserSelector($field, $selector) {
var $field_mask = '<inp2:$prefix_InputName name="#FIELD_NAME#"/>';
var $user_id = parseInt( $selector.Grids['u'].GetSelected() );
$( jq('#' + $field_mask.replace('#FIELD_NAME#', $field)) ).val( $selector.$user_logins[$user_id] );
}
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_category" class="" size="" old_style="0" onkeyup="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_category" pass_params="1">
<td class="control-cell">
<inp2:m_RenderElement name="inp_edit_hidden" pass_params="1"/>
<table cellpadding="0" cellspacing="0">
<tr>
<td id="<inp2:{$prefix}_InputName field='$field'/>_path"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>>
<inp2:$prefix_Field field='$field' db="db" result_to_var="category_id"/>
<inp2:m_include template="categories/category_path" category_id="$category_id"/>
</td>
<td valign="middle">
<img src="img/spacer.gif" width="3" height="1" alt=""/>
<a href="<inp2:adm_SelectorLink prefix='$prefix' selection_mode='single' tab_prefixes='none'/>" onclick="openSelector('<inp2:m_param name='prefix' js_escape='1'/>', this.href, '<inp2:m_param name='field' js_escape='1'/>'); return false;">
<img src="img/icons/icon24_cat.gif" width="24" height="24" border="0"/>
</a>
<a href="#" id="<inp2:{$prefix}_InputName field='$field'/>_disable_link"<inp2:m_ifnot check="Field" name="$field" db="db"> style="display: none;"</inp2:m_ifnot>><inp2:m_Phrase name="la_Text_Disable"/></a>
<script type="text/javascript">
function processItemSelector($field, $selector) {
var $item_ids = $selector.Grids[$selector.$Catalog.ActivePrefix].GetSelected();
CategorySelector.setCategory($field, $item_ids.length ? $item_ids.shift() : '');
}
function CategorySelector() {
}
CategorySelector.getField = function ($field) {
return jq('<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>'.replace('#FIELD_NAME#', $field));
}
CategorySelector.setCategory = function ($field, $category_id) {
var $field_id = this.getField($field);
$('#' + $field_id).val($category_id);
$('#' + $field_id + '_path').load(
'<inp2:m_Link template="categories/category_path" category_id="#ID#" no_amp="1" js_escape="1"/>'.replace('#ID#', $category_id),
function () {
$('#' + $field_id + '_path, #' + $field_id + '_disable_link').toggle( $category_id !== '' );
}
);
}
$(document).ready(
function() {
var $field_id = CategorySelector.getField('<inp2:m_Param name="field" js_escape="1"/>');
$('#' + $field_id + '_disable_link').click(
function ($e) {
CategorySelector.setCategory('<inp2:m_Param name="field" js_escape="1"/>', '');
return false;
}
);
}
);
</script>
</td>
</tr>
</table>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_combo_target" title="la_fld_{$field}" caption_render_as="default_field_caption_element" read_only="0" has_empty="" hint_label="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_combo_target" pass_params="1">
<td class="control-cell">
<inp2:m_if check="m_Param" name="read_only">
<inp2:$prefix_Field name="{$field}Type"/>: "<inp2:$prefix_Field name="$field"/>"
<inp2:m_else/>
<select tabindex="<inp2:m_get param='tab_index'/>" name="<inp2:{$prefix}_InputName field='{$field}Type'/>" id="<inp2:{$prefix}_InputName field='{$field}Type'/>">
<inp2:{$prefix}_PredefinedOptions field="{$field}Type" block="inp_option_phrase" selected="selected" has_empty="$has_empty"/>
</select>
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_get param='tab_index'/>" style="width: 250px;"/>
</inp2:m_if>
<script type="text/javascript">
fields['<inp2:m_Param name="prefix" js_escape="1"/>']['<inp2:m_Param name="field" js_escape="1"/>Type'] = '<inp2:m_phrase label="{$title}Type" js_escape="1"/>'
new AJAXDropDown(
'<inp2:$prefix_InputName name="$field" js_escape="1"/>',
function(cur_value) {
var $type = $( jq('#<inp2:$prefix_InputName name="{$field}Type" js_escape="1"/>') ).val();
if ( !isNaN( parseInt($type) ) ) {
var $url = '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnSuggestAddress" value="#VALUE#" type="#TYPE#" no_amp="1"/>';
return $url.replace('#VALUE#', encodeURIComponent(cur_value)).replace('#TYPE#', encodeURIComponent($type));
}
return false;
}
);
$(document).ready(
function () {
$( jq('#<inp2:$prefix_InputName name="{$field}Type"/>') ).change(
function ($e) {
var $me = $(this);
if ($me.prop('disabled')) {
return ;
}
var $type_selected = !isNaN( parseInt( $me.val() ) );
$( jq('#<inp2:$prefix_InputName name="$field"/>') ).prop('disabled', !$type_selected);
}
)
.change();
}
);
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="email_recipients_js" prefix="email-template" to_readonly="auto">
<script type="text/javascript">
Recipients.formatLine = function($record_index) {
if (this.Records[$record_index]['RecipientName']) {
// name specified
if (this.Records[$record_index]['RecipientAddressType'] == 1) {
// email
this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#>';
}
else {
// other
this.ResultMask = '#RecipientType#: "#RecipientName#" <#RecipientAddress#> - #RecipientAddressType#';
}
}
else {
// name not specified
this.ResultMask = '#RecipientType#: #RecipientAddress# - #RecipientAddressType#';
}
return MultiInputControl.prototype.formatLine.call(this, $record_index);
}
Recipients.compareRecords = function($record_a, $record_b) {
var $equals = true;
var $compare_fields = ['RecipientAddressType', 'RecipientAddress'];
for (var $i = 0; $i < $compare_fields.length; $i++) {
var $field_name = $compare_fields[$i];
if ($record_a[$field_name] !== $record_b[$field_name]) {
return false;
}
}
return $equals;
}
<inp2:m_if check="m_Param" name="to_readonly" equals_to="auto">
<inp2:$prefix_ToRecipientReadOnly result_to_var="to_readonly"/>
</inp2:m_if>
<inp2:m_if check="m_Param" name="to_readonly">
Recipients.isReadOnly = function($record_index) {
return this.Records[$record_index]['RecipientType'] == <inp2:m_GetConst name="EmailTemplate::RECIPIENT_TYPE_TO"/>;
}
<inp2:$prefix_RestoreRecipientType/>
Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:m_GetConst name="EmailTemplate::RECIPIENT_TYPE_CC"/>});
<inp2:m_else/>
Recipients.registerControl('RecipientType', {type: 'radio', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientType" option="default"/>});
</inp2:m_if>
Recipients.registerControl('RecipientAddressType', {type: 'select', required: true, options: <inp2:m_RenderElement name="inp_json_options" prefix="$prefix" field="RecipientAddressType" strip_nl="2"/>, 'default': <inp2:$prefix_FieldOption field="RecipientAddressType" option="default"/>});
Recipients.registerControl('RecipientAddress', {type: 'textbox', required: true, 'default': '<inp2:$prefix_FieldOption field="RecipientAddress" option="default" js_escape="1"/>'});
Recipients.registerControl('RecipientName', {type: 'textbox', required: false, 'default': '<inp2:$prefix_FieldOption field="RecipientName" option="default" js_escape="1"/>'});
Recipients.LoadValues();
</script>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_option_item">
<option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_param name="option"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_option_phrase">
<option value="<inp2:m_param name='key'/>"<inp2:m_param name="selected"/>><inp2:m_phrase label="$option"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_options" onchange="" has_empty="0" empty_value="" empty_label="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_options" pass_params="1">
<td class="control-cell">
<select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" onchange="<inp2:m_Param name='onchange'/>" style="<inp2:m_Param name='style'/>">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value" empty_label="$empty_label"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value" empty_label="$empty_label"/>
</inp2:m_if>
</select>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_single_option">
<option value="<inp2:m_param name='value'/>"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> selected</inp2:m_if>><inp2:$prefix_OptionValue field="$field" value="$value"/></option>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_multioptions" has_empty="0" empty_value="" style="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_multioptions" pass_params="1">
<td class="control-cell">
<select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:{$prefix}_InputName field='$field'/>_select" onchange="update_multiple_options('<inp2:{$prefix}_InputName field='$field'/>');">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_phrase" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_option_item" selected="selected" has_empty="$has_empty" empty_value="$empty_value"/>
</inp2:m_if>
</select>
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_radio_item" onclick="" onchange="">
<input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_param name="option"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_radio_phrase" onclick="" onchange="">
<input type="radio" <inp2:m_param name="checked"/> name="<inp2:{$prefix}_InputName field="$field"/>" id="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>" value="<inp2:m_param name="key"/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="<inp2:m_param name="onclick"/>" onchange="<inp2:m_param name="onchange"/>"><label for="<inp2:{$prefix}_InputName field="$field"/>_<inp2:m_param name="key"/>"><inp2:m_phrase label="$option"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_radio" onclick="" onchange="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_radio" pass_params="1">
<td class="control-cell">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_phrase" selected="checked" onclick="$onclick" onchange="$onchange" />
<inp2:m_else />
<inp2:{$prefix}_PredefinedOptions field="$field" block="inp_radio_item" selected="checked" onclick="$onclick" onchange="$onchange" />
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="js_option_item">
'<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Param name="option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="js_option_phrase">
'<inp2:m_Param name="key" js_escape="1"/>': '<inp2:m_Phrase name="$option" js_escape="1"/>'<inp2:m_ifnot check="m_Param" name="is_last">, </inp2:m_ifnot>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_json_options">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
{<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_phrase" selected="selected"/>}
<inp2:m_else />
{<inp2:{$prefix}_PredefinedOptions field="$field" block="js_option_item" selected="selected"/>}
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkbox" field_class="" onchange="" onclick="" NamePrefix="_cb_">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_checkbox" pass_params="1">
<td class="control-cell">
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>"/>
<input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>">
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_checkbox_item">
<input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_param name="option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/>&nbsp;</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_checkbox_phrase">
<input type="checkbox" <inp2:m_param name='checked'/> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>" value="<inp2:m_param name='key'/>" tabindex="<inp2:m_Get name='tab_index'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='key'/>"><inp2:m_phrase label="$option"/></label><inp2:m_if check="m_Param" name="has_br"><br/><inp2:m_else/>&nbsp;</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkboxes" no_empty="" has_br="0">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_checkboxes" pass_params="1">
<td class="control-cell">
<inp2:m_if check="{$prefix}_FieldOption" field="$field" option="use_phrases">
<inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_phrase" selected="checked" has_br="$has_br"/>
<inp2:m_else/>
<inp2:{$prefix}_PredefinedOptions field="$field" no_empty="$no_empty" block="inp_checkbox_item" selected="checked" has_br="$has_br"/>
</inp2:m_if>
<inp2:m_RenderElement prefix="$prefix" name="inp_edit_hidden" field="$field" db="db"/>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_single_checkbox">
<input type="checkbox"<inp2:m_if check="{$prefix}_Selected" field="$field" value="$value"> checked</inp2:m_if> id="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>" value="<inp2:m_param name='value'/>" onclick="update_checkbox_options(/^<inp2:{$prefix}_InputName field='$field' as_preg='1'/>_([0-9A-Za-z-]+)/, '<inp2:{$prefix}_InputName field='$field'/>');"><label for="<inp2:{$prefix}_InputName field='$field'/>_<inp2:m_param name='value'/>"><inp2:$prefix_OptionValue field="$field" value="$value"/></label>&nbsp;
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_checkbox_allow_html" field_class="" onchange="" onclick="" title="la_enable_html" hint_label="la_Warning_Enable_HTML">
<inp2:m_RenderElement name="inp_edit_checkbox" pass_params="1"/>
<!--##
<inp2:m_if check="{$prefix}_FieldVisible" field="$field">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" id="<inp2:$prefix_InputName field='$field'/>_row">
<td class="control-cell">
<input type="hidden" id="<inp2:{$prefix}_InputName field='$field'/>" name="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field' db='db'/>">
<input tabindex="<inp2:m_Get name='tab_index'/>" type="checkbox" id="_cb_<inp2:{$prefix}_InputName field='$field'/>" name="_cb_<inp2:{$prefix}_InputName field='$field'/>" <inp2:{$prefix}_Field field="$field" checked="checked" db="db"/> class="<inp2:m_param name='field_class'/>" onchange="update_checkbox(this, document.getElementById('<inp2:{$prefix}_InputName field='$field'/>'));<inp2:m_param name='onchange'/>" onclick="<inp2:m_param name='onclick'/>">
</td>
<inp2:m_RenderElement name="inp_edit_error" pass_params="1"/>
</tr>
</inp2:m_if>
##-->
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_weight" class="" size="" maxlength="" onblur="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_weight" pass_params="1">
<td class="control-cell">
<inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="1">
<input type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_kg" />
</inp2:m_if>
<inp2:m_if check="lang.current_FieldEquals" field="UnitSystem" value="2">
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_a'/>" id="<inp2:{$prefix}_InputName field='{$field}_a'/>" value="<inp2:{$prefix}_Field field='{$field}_a'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_lbs" />
<input type="text" name="<inp2:{$prefix}_InputName field='{$field}_b'/>" id="<inp2:{$prefix}_InputName field='{$field}_b'/>" value="<inp2:{$prefix}_Field field='{$field}_b'/>" tabindex="<inp2:m_Get name='tab_index'/>" size="<inp2:m_param name='size'/>" maxlength="<inp2:m_param name='maxlength'/>" class="<inp2:m_param name='class'/>" onblur="<inp2:m_Param name='onblur'/>">
<inp2:m_phrase label="la_oz" />
</inp2:m_if>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_minput" style="" format="" allow_add="1" allow_edit="1" allow_delete="1" allow_move="1" title="">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_minput" pass_params="1">
<td class="control-cell">
<table>
<tr>
<td colspan="2">
<input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Add'/>" id="<inp2:$prefix_InputName field='$field'/>_add_button"/>
<input type="button" class="button" style="width: 70px;" value="<inp2:m_Phrase name='la_btn_Cancel'/>" id="<inp2:$prefix_InputName field='$field'/>_cancel_button"/>
</td>
</tr>
<tr>
<td valign="top">
<select multiple tabindex="<inp2:m_Get name='tab_index'/>" id="<inp2:$prefix_InputName field='$field'/>_minput" style="<inp2:m_Param name='style'/>">
</select>
</td>
<td valign="top">
<inp2:m_if check="m_Param" name="allow_edit">
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Edit'/>" id="<inp2:$prefix_InputName field='$field'/>_edit_button"/><br />
<img src="img/spacer.gif" height="4" width="1" alt=""/><br />
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_delete">
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_Delete'/>" id="<inp2:$prefix_InputName field='$field'/>_delete_button"/><br />
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_move">
<br /><br />
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveUp'/>" id="<inp2:$prefix_InputName field='$field'/>_moveup_button"/><br />
<img src="img/spacer.gif" height="4" width="1" alt=""/><br />
<input type="button" class="button" style="width: 100px;" value="<inp2:m_Phrase name='la_btn_MoveDown'/>" id="<inp2:$prefix_InputName field='$field'/>_movedown_button"/><br />
</inp2:m_if>
</td>
</tr>
<inp2:m_RenderElement name="inp_edit_hidden" prefix="$prefix" field="$field" db="db"/>
<script type="text/javascript">
var <inp2:m_Param name="field"/> = new MultiInputControl('<inp2:m_Param name="field"/>', '<inp2:{$prefix}_InputName field="#FIELD_NAME#"/>', fields['<inp2:m_Param name="prefix"/>'], '<inp2:m_Param name="format"/>');
<inp2:m_Param name="field"/>.ValidateURL = '<inp2:m_Link template="dummy" pass="m,$prefix" {$prefix}_event="OnValidateMInputFields" js_escape="1"/>';
<inp2:m_if check="m_Param" name="allow_add">
<inp2:m_Param name="field"/>.SetPermission('add', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_edit">
<inp2:m_Param name="field"/>.SetPermission('edit', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_delete">
<inp2:m_Param name="field"/>.SetPermission('delete', true);
</inp2:m_if>
<inp2:m_if check="m_Param" name="allow_move">
<inp2:m_Param name="field"/>.SetPermission('move', true);
</inp2:m_if>
<inp2:m_Param name="field"/>.InitEvents();
<inp2:m_Param name="field"/>.SetMessage('required_error', '<inp2:m_Phrase name="la_err_required" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('unique_error', '<inp2:m_Phrase name="la_error_unique" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('delete_confirm', '<inp2:m_Phrase label="la_Delete_Confirm" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('add_button', '<inp2:m_Phrase name="la_btn_Add" escape="1"/>');
<inp2:m_Param name="field"/>.SetMessage('save_button', '<inp2:m_Phrase name="la_btn_Save" escape="1"/>');
</script>
</table>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_picker" has_empty="0" empty_value="" style="width: 225px;" size="15">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_picker" pass_params="1">
<td class="control-cell">
<table cellpadding="0" cellspacing="0">
<tr>
<td><strong><inp2:m_Phrase label="la_SelectedItems" /></strong></td>
<td>&nbsp;</td>
<td><strong><inp2:m_Phrase label="la_AvailableItems" /></strong></td>
</tr>
<tr>
<td>
<inp2:m_DefineElement name="picker_option_block">
<option value="<inp2:Field name='$key_field' />"><inp2:Field name="$value_field" /></option>
</inp2:m_DefineElement>
<select multiple id="<inp2:$prefix_InputName name='$field' />_selected" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>">
<inp2:$optprefix.selected_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" per_page="-1" requery="1" link_to_prefix="$prefix" link_to_field="$field"/>
</select>
</td>
<td align="center">
<img src="img/icons/icon_left.gif" id="<inp2:$prefix_InputName name="$field" />_move_left_button"/><br />
<img src="img/icons/icon_right.gif" id="<inp2:$prefix_InputName name="$field" />_move_right_button"/>
</td>
<td>
<select multiple id="<inp2:$prefix_InputName name='$field' />_available" style="<inp2:m_param name='style'/>" size="<inp2:m_param name='size'/>">
<inp2:$optprefix.available_PrintList render_as="picker_option_block" key_field="$option_key_field" value_field="$option_value_field" requery="1" per_page="-1" link_to_prefix="$prefix" link_to_field="$field"/>
</select>
</td>
</tr>
</table>
<input type="hidden" name="<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />" value="<inp2:$prefix_Field field='$field' db='db'/>">
<input type="hidden" name="unselected_<inp2:$prefix_InputName name='$field' />" id="<inp2:$prefix_InputName name='$field' />_available_field" value="">
<script type="text/javascript">
<inp2:m_Param name="field"/> = new EditPickerControl('<inp2:m_Param name="field"/>', '<inp2:$prefix_InputName name="$field" />');
<inp2:m_Param name="field"/>.SetMessage('nothing_selected', '<inp2:m_Phrase label="la_error_SelectItemToMove" escape="1"/>');
</script>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_cron_box">
<inp2:m_RenderElement design="form_row" block_name="inp_edit_cron_box" pass_params="1">
<td class="control-cell">
<input style="width: 150px;" type="text" name="<inp2:{$prefix}_InputName field='$field'/>" id="<inp2:{$prefix}_InputName field='$field'/>" value="<inp2:{$prefix}_Field field='$field'/>" tabindex="<inp2:m_Get name='tab_index'/>"/>
<select tabindex="<inp2:m_Get name='tab_index'/>" name="<inp2:{$prefix}_InputName field='{$field}Hints'/>" id="<inp2:{$prefix}_InputName field='{$field}Hints'/>" style="width: 175px;">
<inp2:{$prefix}_PredefinedOptions field="{$field}Hints" block="inp_option_item" selected="selected" has_empty="1" empty_value="" empty_label="la_opt_CronCommonSettings"/>
</select>
</td>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="inp_edit_filler" control_options="false">
<tr class="<inp2:m_odd_even odd='edit-form-odd' even='edit-form-even'/>" style="height: auto">
<td class="label-cell-filler" ></td>
<td class="control-mid-filler" ></td>
<td class="control-cell-filler">
<input type="text" style="display: none;"/>
<div id="form_filler" style="width: 100%; height: 5px; background-color: inherit"></div>
<script type="text/javascript">
Form.addControl('form_filler', <inp2:m_param name="control_options"/>);
</script>
</td>
</tr>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="ajax_progress_bar">
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td colspan="2">
<img src="img/spacer.gif" height="10" width="1" alt="" /><br />
<!-- progress bar paddings: begin -->
<table width="90%" cellpadding="2" cellspacing="0" border="0" align="center">
<tr>
<td class="progress-text">0%</td>
<td width="100%">
<!-- progress bar: begin -->
<table cellspacing="0" cellpadding="0" width="100%" border="0" align="center" style="background-color: #FFFFFF; border: 1px solid #E6E6E6;">
<tr>
<td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td>
</tr>
<tr>
<td width="2"><img src="img/spacer.gif" height="13" width="3" alt="" /></td>
<td align="center" width="100%">
<table cellspacing="0" cellpadding="0" width="100%" border="0" style="background: url(img/progress_left.gif) repeat-x;">
<tr>
<td id="progress_bar[done]" style="background: url(img/progress_done.gif);" align="left"></td>
<td id="progress_bar[left]" align="right"><img src="img/spacer.gif" height="9" width="1" alt="" /></td>
</tr>
</table>
</td>
<td width="1"><img src="img/spacer.gif" height="13" width="3" alt="" /></td>
</tr>
<tr>
<td colspan="3"><img src="img/spacer.gif" height="2" width="1" alt="" /></td>
</tr>
</table>
<!-- progress bar: end -->
</td>
<td class="progress-text">100%</td>
</tr>
</table>
<!-- progress bar paddings: end -->
<img src="img/spacer.gif" height="10" width="1" alt="" /><br />
</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td width="50%" align="right"><inp2:m_phrase name="la_fld_PercentsCompleted"/>:</td>
<td id="progress_display[percents_completed]">n/a</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td width="50%" align="right"><inp2:m_phrase name="la_fld_ElapsedTime"/>:</td>
<td id="progress_display[elapsed_time]">n/a</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td width="50%" align="right"><inp2:m_phrase name="la_fld_EstimatedTime"/>:</td>
<td id="progress_display[Estimated_time]">n/a</td>
</tr>
</table>
<table width="100%" border="0" cellspacing="0" cellpadding="2" class="tableborder">
<tr class="<inp2:m_odd_even odd='table-color1' even='table-color2'/>">
<td align="center" colspan="2">
<input type="button" class="button" onclick="<inp2:m_param name='cancel_action'/>" value="<inp2:m_phrase name='la_Cancel'/>" />
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_navigation" toolbar="a_toolbar">
<inp2:m_if check="{$prefix}_IsTopmostPrefix">
<inp2:m_if check="{$prefix}_IsSingle">
<inp2:m_param name="toolbar"/>.HideButton('prev');
<inp2:m_param name="toolbar"/>.HideButton('next');
<inp2:m_else/>
<inp2:m_if check="{$prefix}_IsLast">
<inp2:m_param name="toolbar"/>.DisableButton('next');
</inp2:m_if>
<inp2:m_if check="{$prefix}_IsFirst">
<inp2:m_param name="toolbar"/>.DisableButton('prev');
</inp2:m_if>
</inp2:m_if>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="toolbar_button" icon="" title="" short_title="" toolbar="a_toolbar">
<inp2:m_Param name="toolbar"/>.AddButton(
new ToolBarButton(
'<inp2:m_Param name="icon"/>',
<inp2:m_if check="m_Param" name="short_title">
'<inp2:m_Phrase label="$title" escape="1"/>::<inp2:m_phrase label="$short_title" escape="1"/>',
<inp2:m_else/>
'<inp2:m_Phrase label="$title" escape="1"/>',
</inp2:m_if>
function() {
<inp2:m_Param name="content"/>
}
)
);
</inp2:m_DefineElement>
<inp2:m_DefineElement name="tabs_container" tabs_render_as="">
<table cellpadding="0" cellspacing="0" style="width: 100%;">
<tr>
<td style="width: 20px;">
<img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/>
<a href="#" class="scroll-left disabled"></a>
</td>
<td height="23" align="right">
<div id="tab-measure" style="display: none; width: 100%; height: 23px;">&nbsp;</div>
<div style="overflow: hidden; height: 23px;" class="tab-viewport">
<table class="tabs" cellpadding="0" cellspacing="0" height="23">
<tr>
<inp2:m_RenderElement name="$tabs_render_as" pass_params="1"/>
</tr>
</table>
</div>
</td>
<td class="scroll-right-container disabled">
<img src="<inp2:m_TemplatesBase/>/img/spacer.gif" width="20" height="0" alt=""/><br/>
<a href="#" class="scroll-right disabled"></a>
</td>
</tr>
</table>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_tabs_element">
<inp2:m_DefineElement name="edit_tab">
<inp2:m_RenderElement name="tab" title="$title" t="$template" main_prefix="$PrefixSpecial"/>
</inp2:m_DefineElement>
<inp2:{$prefix}_PrintEditTabs render_as="edit_tab" preset_name="$preset_name"/>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="edit_tabs" preset_name="Default">
<inp2:m_if check="{$prefix}_HasEditTabs" preset_name="$preset_name">
<inp2:m_RenderElement name="tabs_container" tabs_render_as="edit_tabs_element" pass_params="1"/>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="ml_selector" prefix="">
<inp2:m_if check="lang_IsMultiLanguage">
<td align="right" style="padding-right: 5px;">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="right">
<inp2:m_phrase name="la_fld_Language"/>:
<select name="language" onchange="submit_event('<inp2:m_param name='prefix'/>', 'OnPreSaveAndChangeLanguage');">
<inp2:m_DefineElement name="lang_elem">
<option value="<inp2:Field name='LanguageId'/>" <inp2:m_if check="SelectedLanguage" type="data">selected="selected"</inp2:m_if> ><inp2:Field name="LocalName" no_special='no_special' /></option>
</inp2:m_DefineElement>
<inp2:lang_PrintList render_as="lang_elem"/>
</select>
</td>
</tr>
<tr>
<td align="right" style="vertical-align: bottom; padding: 2px 0px 5px 2px;">
<span style="color: red">*</span>&nbsp;<span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span>
</td>
</tr>
</table>
</td>
<inp2:m_else/>
<td align="right" style="vertical-align: bottom; padding: 2px 5px 5px 2px;">
<span style="color: red">*</span>&nbsp;<span class="req-note"><inp2:m_Phrase name="la_text_RequiredFields"/></span>
</td>
</inp2:m_if>
</inp2:m_DefineElement>
<inp2:m_DefineElement name="form_error_warning">
<inp2:m_RenderElement design="form_message" pass_params="1">
<inp2:m_Phrase name="la_Warning_NewFormError"/><br/>
<span id="error_msg_<inp2:m_Param name='prefix'/>" style="font-weight: bold;"><br/></span>
</inp2:m_RenderElement>
</inp2:m_DefineElement>
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/mysql/mysql.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/mysql/mysql.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/mysql/mysql.js (nonexistent)
@@ -1,203 +0,0 @@
-/*
- * MySQL Mode for CodeMirror 2 by MySQL-Tools
- * @author James Thorne (partydroid)
- * @link http://github.com/partydroid/MySQL-Tools
- * @link http://mysqltools.org
- * @version 02/Jan/2012
-*/
-CodeMirror.defineMode("mysql", function(config) {
- var indentUnit = config.indentUnit;
- var curPunc;
-
- function wordRegexp(words) {
- return new RegExp("^(?:" + words.join("|") + ")$", "i");
- }
- var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
- "isblank", "isliteral", "union", "a"]);
- var keywords = wordRegexp([
- ('ACCESSIBLE'),('ALTER'),('AS'),('BEFORE'),('BINARY'),('BY'),('CASE'),('CHARACTER'),('COLUMN'),('CONTINUE'),('CROSS'),('CURRENT_TIMESTAMP'),('DATABASE'),('DAY_MICROSECOND'),('DEC'),('DEFAULT'),
- ('DESC'),('DISTINCT'),('DOUBLE'),('EACH'),('ENCLOSED'),('EXIT'),('FETCH'),('FLOAT8'),('FOREIGN'),('GRANT'),('HIGH_PRIORITY'),('HOUR_SECOND'),('IN'),('INNER'),('INSERT'),('INT2'),('INT8'),
- ('INTO'),('JOIN'),('KILL'),('LEFT'),('LINEAR'),('LOCALTIME'),('LONG'),('LOOP'),('MATCH'),('MEDIUMTEXT'),('MINUTE_SECOND'),('NATURAL'),('NULL'),('OPTIMIZE'),('OR'),('OUTER'),('PRIMARY'),
- ('RANGE'),('READ_WRITE'),('REGEXP'),('REPEAT'),('RESTRICT'),('RIGHT'),('SCHEMAS'),('SENSITIVE'),('SHOW'),('SPECIFIC'),('SQLSTATE'),('SQL_CALC_FOUND_ROWS'),('STARTING'),('TERMINATED'),
- ('TINYINT'),('TRAILING'),('UNDO'),('UNLOCK'),('USAGE'),('UTC_DATE'),('VALUES'),('VARCHARACTER'),('WHERE'),('WRITE'),('ZEROFILL'),('ALL'),('AND'),('ASENSITIVE'),('BIGINT'),('BOTH'),('CASCADE'),
- ('CHAR'),('COLLATE'),('CONSTRAINT'),('CREATE'),('CURRENT_TIME'),('CURSOR'),('DAY_HOUR'),('DAY_SECOND'),('DECLARE'),('DELETE'),('DETERMINISTIC'),('DIV'),('DUAL'),('ELSEIF'),('EXISTS'),('FALSE'),
- ('FLOAT4'),('FORCE'),('FULLTEXT'),('HAVING'),('HOUR_MINUTE'),('IGNORE'),('INFILE'),('INSENSITIVE'),('INT1'),('INT4'),('INTERVAL'),('ITERATE'),('KEYS'),('LEAVE'),('LIMIT'),('LOAD'),('LOCK'),
- ('LONGTEXT'),('MASTER_SSL_VERIFY_SERVER_CERT'),('MEDIUMINT'),('MINUTE_MICROSECOND'),('MODIFIES'),('NO_WRITE_TO_BINLOG'),('ON'),('OPTIONALLY'),('OUT'),('PRECISION'),('PURGE'),('READS'),
- ('REFERENCES'),('RENAME'),('REQUIRE'),('REVOKE'),('SCHEMA'),('SELECT'),('SET'),('SPATIAL'),('SQLEXCEPTION'),('SQL_BIG_RESULT'),('SSL'),('TABLE'),('TINYBLOB'),('TO'),('TRUE'),('UNIQUE'),
- ('UPDATE'),('USING'),('UTC_TIMESTAMP'),('VARCHAR'),('WHEN'),('WITH'),('YEAR_MONTH'),('ADD'),('ANALYZE'),('ASC'),('BETWEEN'),('BLOB'),('CALL'),('CHANGE'),('CHECK'),('CONDITION'),('CONVERT'),
- ('CURRENT_DATE'),('CURRENT_USER'),('DATABASES'),('DAY_MINUTE'),('DECIMAL'),('DELAYED'),('DESCRIBE'),('DISTINCTROW'),('DROP'),('ELSE'),('ESCAPED'),('EXPLAIN'),('FLOAT'),('FOR'),('FROM'),
- ('GROUP'),('HOUR_MICROSECOND'),('IF'),('INDEX'),('INOUT'),('INT'),('INT3'),('INTEGER'),('IS'),('KEY'),('LEADING'),('LIKE'),('LINES'),('LOCALTIMESTAMP'),('LONGBLOB'),('LOW_PRIORITY'),
- ('MEDIUMBLOB'),('MIDDLEINT'),('MOD'),('NOT'),('NUMERIC'),('OPTION'),('ORDER'),('OUTFILE'),('PROCEDURE'),('READ'),('REAL'),('RELEASE'),('REPLACE'),('RETURN'),('RLIKE'),('SECOND_MICROSECOND'),
- ('SEPARATOR'),('SMALLINT'),('SQL'),('SQLWARNING'),('SQL_SMALL_RESULT'),('STRAIGHT_JOIN'),('THEN'),('TINYTEXT'),('TRIGGER'),('UNION'),('UNSIGNED'),('USE'),('UTC_TIME'),('VARBINARY'),('VARYING'),
- ('WHILE'),('XOR'),('FULL'),('COLUMNS'),('MIN'),('MAX'),('STDEV'),('COUNT')
- ]);
- var operatorChars = /[*+\-<>=&|]/;
-
- function tokenBase(stream, state) {
- var ch = stream.next();
- curPunc = null;
- if (ch == "$" || ch == "?") {
- stream.match(/^[\w\d]*/);
- return "variable-2";
- }
- else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
- stream.match(/^[^\s\u00a0>]*>?/);
- return "atom";
- }
- else if (ch == "\"" || ch == "'") {
- state.tokenize = tokenLiteral(ch);
- return state.tokenize(stream, state);
- }
- else if (ch == "`") {
- state.tokenize = tokenOpLiteral(ch);
- return state.tokenize(stream, state);
- }
- else if (/[{}\(\),\.;\[\]]/.test(ch)) {
- curPunc = ch;
- return null;
- }
- else if (ch == "-" && stream.eat("-")) {
- stream.skipToEnd();
- return "comment";
- }
- else if (ch == "/" && stream.eat("*")) {
- state.tokenize = tokenComment;
- return state.tokenize(stream, state);
- }
- else if (operatorChars.test(ch)) {
- stream.eatWhile(operatorChars);
- return null;
- }
- else if (ch == ":") {
- stream.eatWhile(/[\w\d\._\-]/);
- return "atom";
- }
- else {
- stream.eatWhile(/[_\w\d]/);
- if (stream.eat(":")) {
- stream.eatWhile(/[\w\d_\-]/);
- return "atom";
- }
- var word = stream.current();
- if (ops.test(word))
- return null;
- else if (keywords.test(word))
- return "keyword";
- else
- return "variable";
- }
- }
-
- function tokenLiteral(quote) {
- return function(stream, state) {
- var escaped = false, ch;
- while ((ch = stream.next()) != null) {
- if (ch == quote && !escaped) {
- state.tokenize = tokenBase;
- break;
- }
- escaped = !escaped && ch == "\\";
- }
- return "string";
- };
- }
-
- function tokenOpLiteral(quote) {
- return function(stream, state) {
- var escaped = false, ch;
- while ((ch = stream.next()) != null) {
- if (ch == quote && !escaped) {
- state.tokenize = tokenBase;
- break;
- }
- escaped = !escaped && ch == "\\";
- }
- return "variable-2";
- };
- }
-
- function tokenComment(stream, state) {
- for (;;) {
- if (stream.skipTo("*")) {
- stream.next();
- if (stream.eat("/")) {
- state.tokenize = tokenBase;
- break;
- }
- } else {
- stream.skipToEnd();
- break;
- }
- }
- return "comment";
- }
-
-
- function pushContext(state, type, col) {
- state.context = {prev: state.context, indent: state.indent, col: col, type: type};
- }
- function popContext(state) {
- state.indent = state.context.indent;
- state.context = state.context.prev;
- }
-
- return {
- startState: function() {
- return {tokenize: tokenBase,
- context: null,
- indent: 0,
- col: 0};
- },
-
- token: function(stream, state) {
- if (stream.sol()) {
- if (state.context && state.context.align == null) state.context.align = false;
- state.indent = stream.indentation();
- }
- if (stream.eatSpace()) return null;
- var style = state.tokenize(stream, state);
-
- if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") {
- state.context.align = true;
- }
-
- if (curPunc == "(") pushContext(state, ")", stream.column());
- else if (curPunc == "[") pushContext(state, "]", stream.column());
- else if (curPunc == "{") pushContext(state, "}", stream.column());
- else if (/[\]\}\)]/.test(curPunc)) {
- while (state.context && state.context.type == "pattern") popContext(state);
- if (state.context && curPunc == state.context.type) popContext(state);
- }
- else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
- else if (/atom|string|variable/.test(style) && state.context) {
- if (/[\}\]]/.test(state.context.type))
- pushContext(state, "pattern", stream.column());
- else if (state.context.type == "pattern" && !state.context.align) {
- state.context.align = true;
- state.context.col = stream.column();
- }
- }
-
- return style;
- },
-
- indent: function(state, textAfter) {
- var firstChar = textAfter && textAfter.charAt(0);
- var context = state.context;
- if (/[\]\}]/.test(firstChar))
- while (context && context.type == "pattern") context = context.prev;
-
- var closing = context && firstChar == context.type;
- if (!context)
- return 0;
- else if (context.type == "pattern")
- return context.col;
- else if (context.align)
- return context.col + (closing ? 0 : 1);
- else
- return context.indent + (closing ? 0 : indentUnit);
- }
- };
-});
-
-CodeMirror.defineMIME("text/x-mysql", "mysql");
Property changes on: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/mysql/mysql.js
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-LF
\ No newline at end of property
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/sql/sql.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/sql/sql.js (nonexistent)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/sql/sql.js (revision 15914)
@@ -0,0 +1,349 @@
+CodeMirror.defineMode("sql", function(config, parserConfig) {
+ "use strict";
+
+ var client = parserConfig.client || {},
+ atoms = parserConfig.atoms || {"false": true, "true": true, "null": true},
+ builtin = parserConfig.builtin || {},
+ keywords = parserConfig.keywords || {},
+ operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
+ support = parserConfig.support || {},
+ hooks = parserConfig.hooks || {},
+ dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+
+ // call hooks from the mime type
+ if (hooks[ch]) {
+ var result = hooks[ch](stream, state);
+ if (result !== false) return result;
+ }
+
+ if (support.hexNumber == true &&
+ ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
+ || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
+ // hex
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
+ return "number";
+ } else if (support.binaryNumber == true &&
+ (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
+ || (ch == "0" && stream.match(/^b[01]+/)))) {
+ // bitstring
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
+ return "number";
+ } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
+ // numbers
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
+ stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
+ support.decimallessFloat == true && stream.eat('.');
+ return "number";
+ } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
+ // placeholders
+ return "variable-3";
+ } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
+ // strings
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
+ state.tokenize = tokenLiteral(ch);
+ return state.tokenize(stream, state);
+ } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
+ || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
+ && (stream.peek() == "'" || stream.peek() == '"'))) {
+ // charset casting: _utf8'str', N'str', n'str'
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
+ return "keyword";
+ } else if (/^[\(\),\;\[\]]/.test(ch)) {
+ // no highlightning
+ return null;
+ } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
+ // 1-line comment
+ stream.skipToEnd();
+ return "comment";
+ } else if ((support.commentHash && ch == "#")
+ || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
+ // 1-line comments
+ // ref: https://kb.askmonty.org/en/comment-syntax/
+ stream.skipToEnd();
+ return "comment";
+ } else if (ch == "/" && stream.eat("*")) {
+ // multi-line comments
+ // ref: https://kb.askmonty.org/en/comment-syntax/
+ state.tokenize = tokenComment;
+ return state.tokenize(stream, state);
+ } else if (ch == ".") {
+ // .1 for 0.1
+ if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
+ return "number";
+ }
+ // .table_name (ODBC)
+ // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
+ if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
+ return "variable-2";
+ }
+ } else if (operatorChars.test(ch)) {
+ // operators
+ stream.eatWhile(operatorChars);
+ return null;
+ } else if (ch == '{' &&
+ (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
+ // dates (weird ODBC syntax)
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
+ return "number";
+ } else {
+ stream.eatWhile(/^[_\w\d]/);
+ var word = stream.current().toLowerCase();
+ // dates (standard SQL syntax)
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
+ if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
+ return "number";
+ if (atoms.hasOwnProperty(word)) return "atom";
+ if (builtin.hasOwnProperty(word)) return "builtin";
+ if (keywords.hasOwnProperty(word)) return "keyword";
+ if (client.hasOwnProperty(word)) return "string-2";
+ return null;
+ }
+ }
+
+ // 'string', with char specified in quote escaped by '\'
+ function tokenLiteral(quote) {
+ return function(stream, state) {
+ var escaped = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && !escaped) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ escaped = !escaped && ch == "\\";
+ }
+ return "string";
+ };
+ }
+ function tokenComment(stream, state) {
+ while (true) {
+ if (stream.skipTo("*")) {
+ stream.next();
+ if (stream.eat("/")) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ } else {
+ stream.skipToEnd();
+ break;
+ }
+ }
+ return "comment";
+ }
+
+ function pushContext(stream, state, type) {
+ state.context = {
+ prev: state.context,
+ indent: stream.indentation(),
+ col: stream.column(),
+ type: type
+ };
+ }
+
+ function popContext(state) {
+ state.indent = state.context.indent;
+ state.context = state.context.prev;
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: tokenBase, context: null};
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (state.context && state.context.align == null)
+ state.context.align = false;
+ }
+ if (stream.eatSpace()) return null;
+
+ var style = state.tokenize(stream, state);
+ if (style == "comment") return style;
+
+ if (state.context && state.context.align == null)
+ state.context.align = true;
+
+ var tok = stream.current();
+ if (tok == "(")
+ pushContext(stream, state, ")");
+ else if (tok == "[")
+ pushContext(stream, state, "]");
+ else if (state.context && state.context.type == tok)
+ popContext(state);
+ return style;
+ },
+
+ indent: function(state, textAfter) {
+ var cx = state.context;
+ if (!cx) return CodeMirror.Pass;
+ if (cx.align) return cx.col + (textAfter.charAt(0) == cx.type ? 0 : 1);
+ else return cx.indent + config.indentUnit;
+ }
+ };
+});
+
+(function() {
+ "use strict";
+
+ // `identifier`
+ function hookIdentifier(stream) {
+ // MySQL/MariaDB identifiers
+ // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
+ var ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == "`" && !stream.eat("`")) return "variable-2";
+ }
+ return null;
+ }
+
+ // variable token
+ function hookVar(stream) {
+ // variables
+ // @@prefix.varName @varName
+ // varName can be quoted with ` or ' or "
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
+ if (stream.eat("@")) {
+ stream.match(/^session\./);
+ stream.match(/^local\./);
+ stream.match(/^global\./);
+ }
+
+ if (stream.eat("'")) {
+ stream.match(/^.*'/);
+ return "variable-2";
+ } else if (stream.eat('"')) {
+ stream.match(/^.*"/);
+ return "variable-2";
+ } else if (stream.eat("`")) {
+ stream.match(/^.*`/);
+ return "variable-2";
+ } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
+ return "variable-2";
+ }
+ return null;
+ };
+
+ // short client keyword token
+ function hookClient(stream) {
+ // \N means NULL
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
+ if (stream.eat("N")) {
+ return "atom";
+ }
+ // \g, etc
+ // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
+ return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
+ }
+
+ // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
+ var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where ";
+
+ // turn a space-separated list into an array
+ function set(str) {
+ var obj = {}, words = str.split(" ");
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+ return obj;
+ }
+
+ // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
+ CodeMirror.defineMIME("text/x-sql", {
+ name: "sql",
+ keywords: set(sqlKeywords + "begin"),
+ builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
+ atoms: set("false true null unknown"),
+ operatorChars: /^[*+\-%<>!=]/,
+ dateSQL: set("date time timestamp"),
+ support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
+ });
+
+ CodeMirror.defineMIME("text/x-mysql", {
+ name: "sql",
+ client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
+ keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
+ builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
+ atoms: set("false true null unknown"),
+ operatorChars: /^[*+\-%<>!=&|^]/,
+ dateSQL: set("date time timestamp"),
+ support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+ hooks: {
+ "@": hookVar,
+ "`": hookIdentifier,
+ "\\": hookClient
+ }
+ });
+
+ CodeMirror.defineMIME("text/x-mariadb", {
+ name: "sql",
+ client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
+ keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
+ builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
+ atoms: set("false true null unknown"),
+ operatorChars: /^[*+\-%<>!=&|^]/,
+ dateSQL: set("date time timestamp"),
+ support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+ hooks: {
+ "@": hookVar,
+ "`": hookIdentifier,
+ "\\": hookClient
+ }
+ });
+
+ // the query language used by Apache Cassandra is called CQL, but this mime type
+ // is called Cassandra to avoid confusion with Contextual Query Language
+ CodeMirror.defineMIME("text/x-cassandra", {
+ name: "sql",
+ client: { },
+ keywords: set("use select from using consistency where limit first reversed first and in insert into values using consistency ttl update set delete truncate begin batch apply create keyspace with columnfamily primary key index on drop alter type add any one quorum all local_quorum each_quorum"),
+ builtin: set("ascii bigint blob boolean counter decimal double float int text timestamp uuid varchar varint"),
+ atoms: set("false true"),
+ operatorChars: /^[<>=]/,
+ dateSQL: { },
+ support: set("commentSlashSlash decimallessFloat"),
+ hooks: { }
+ });
+
+ // this is based on Peter Raganitsch's 'plsql' mode
+ CodeMirror.defineMIME("text/x-plsql", {
+ name: "sql",
+ client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
+ keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
+ functions: set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"),
+ builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"),
+ operatorChars: /^[*+\-%<>!=~]/,
+ dateSQL: set("date time timestamp"),
+ support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
+ });
+}());
+
+/*
+ How Properties of Mime Types are used by SQL Mode
+ =================================================
+
+ keywords:
+ A list of keywords you want to be highlighted.
+ functions:
+ A list of function names you want to be highlighted.
+ builtin:
+ A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
+ operatorChars:
+ All characters that must be handled as operators.
+ client:
+ Commands parsed and executed by the client (not the server).
+ support:
+ A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
+ * ODBCdotTable: .tableName
+ * zerolessFloat: .1
+ * doubleQuote
+ * nCharCast: N'string'
+ * charsetCast: _utf8'string'
+ * commentHash: use # char for comments
+ * commentSlashSlash: use // for comments
+ * commentSpaceRequired: require a space after -- for comments
+ atoms:
+ Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
+ UNKNOWN, INFINITY, UNDERFLOW, NaN...
+ dateSQL:
+ Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
+*/
Property changes on: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/sql/sql.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+LF
\ No newline at end of property
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/xml/xml.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/xml/xml.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/xml/xml.js (revision 15914)
@@ -1,324 +1,326 @@
CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
+ var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
+
var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
'track': true, 'wbr': true},
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
'th': true, 'tr': true},
contextGrabbers: {
'dd': {'dd': true, 'dt': true},
'dt': {'dd': true, 'dt': true},
'li': {'li': true},
'option': {'option': true, 'optgroup': true},
'optgroup': {'optgroup': true},
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
'rp': {'rp': true, 'rt': true},
'rt': {'rp': true, 'rt': true},
'tbody': {'tbody': true, 'tfoot': true},
'td': {'td': true, 'th': true},
'tfoot': {'tbody': true},
'th': {'td': true, 'th': true},
'thead': {'tbody': true, 'tfoot': true},
'tr': {'tr': true}
},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: true
} : {
autoSelfClosers: {},
implicitlyClosed: {},
contextGrabbers: {},
doNotIndent: {},
allowUnquoted: false,
allowMissing: false
};
var alignCDATA = parserConfig.alignCDATA;
// Return variables for tokenizers
var tagName, type;
function inText(stream, state) {
function chain(parser) {
state.tokenize = parser;
return parser(stream, state);
}
var ch = stream.next();
if (ch == "<") {
if (stream.eat("!")) {
if (stream.eat("[")) {
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
else return null;
- }
- else if (stream.match("--")) return chain(inBlock("comment", "-->"));
- else if (stream.match("DOCTYPE", true, true)) {
+ } else if (stream.match("--")) {
+ return chain(inBlock("comment", "-->"));
+ } else if (stream.match("DOCTYPE", true, true)) {
stream.eatWhile(/[\w\._\-]/);
return chain(doctype(1));
+ } else {
+ return null;
}
- else return null;
- }
- else if (stream.eat("?")) {
+ } else if (stream.eat("?")) {
stream.eatWhile(/[\w\._\-]/);
state.tokenize = inBlock("meta", "?>");
return "meta";
- }
- else {
+ } else {
var isClose = stream.eat("/");
tagName = "";
var c;
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
if (!tagName) return "error";
type = isClose ? "closeTag" : "openTag";
state.tokenize = inTag;
return "tag";
}
- }
- else if (ch == "&") {
+ } else if (ch == "&") {
var ok;
if (stream.eat("#")) {
if (stream.eat("x")) {
- ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+ ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
} else {
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
}
} else {
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
}
return ok ? "atom" : "error";
- }
- else {
+ } else {
stream.eatWhile(/[^&<]/);
return null;
}
}
function inTag(stream, state) {
var ch = stream.next();
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
state.tokenize = inText;
type = ch == ">" ? "endTag" : "selfcloseTag";
return "tag";
- }
- else if (ch == "=") {
+ } else if (ch == "=") {
type = "equals";
return null;
- }
- else if (/[\'\"]/.test(ch)) {
+ } else if (ch == "<") {
+ return "error";
+ } else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
return state.tokenize(stream, state);
- }
- else {
+ } else {
stream.eatWhile(/[^\s\u00a0=<>\"\']/);
return "word";
}
}
function inAttribute(quote) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inTag;
break;
}
}
return "string";
};
}
function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = inText;
break;
}
stream.next();
}
return style;
};
}
function doctype(depth) {
return function(stream, state) {
var ch;
while ((ch = stream.next()) != null) {
if (ch == "<") {
state.tokenize = doctype(depth + 1);
return state.tokenize(stream, state);
} else if (ch == ">") {
if (depth == 1) {
state.tokenize = inText;
break;
} else {
state.tokenize = doctype(depth - 1);
return state.tokenize(stream, state);
}
}
}
return "meta";
};
}
- var curState, setStyle;
+ var curState, curStream, setStyle;
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function pushContext(tagName, startOfLine) {
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
curState.context = {
prev: curState.context,
tagName: tagName,
indent: curState.indented,
startOfLine: startOfLine,
noIndent: noIndent
};
}
function popContext() {
if (curState.context) curState.context = curState.context.prev;
}
function element(type) {
if (type == "openTag") {
curState.tagName = tagName;
+ curState.tagStart = curStream.column();
return cont(attributes, endtag(curState.startOfLine));
} else if (type == "closeTag") {
var err = false;
if (curState.context) {
if (curState.context.tagName != tagName) {
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
popContext();
}
err = !curState.context || curState.context.tagName != tagName;
}
} else {
err = true;
}
if (err) setStyle = "error";
return cont(endclosetag(err));
}
return cont();
}
function endtag(startOfLine) {
return function(type) {
var tagName = curState.tagName;
- curState.tagName = null;
+ curState.tagName = curState.tagStart = null;
if (type == "selfcloseTag" ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
maybePopContext(tagName.toLowerCase());
return cont();
}
if (type == "endTag") {
maybePopContext(tagName.toLowerCase());
pushContext(tagName, startOfLine);
return cont();
}
return cont();
};
}
function endclosetag(err) {
return function(type) {
if (err) setStyle = "error";
if (type == "endTag") { popContext(); return cont(); }
setStyle = "error";
return cont(arguments.callee);
};
}
function maybePopContext(nextTagName) {
var parentTagName;
while (true) {
if (!curState.context) {
return;
}
parentTagName = curState.context.tagName.toLowerCase();
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
return;
}
popContext();
}
}
function attributes(type) {
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
if (type == "endTag" || type == "selfcloseTag") return pass();
setStyle = "error";
return cont(attributes);
}
function attribute(type) {
if (type == "equals") return cont(attvalue, attributes);
if (!Kludges.allowMissing) setStyle = "error";
else if (type == "word") setStyle = "attribute";
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
}
function attvalue(type) {
if (type == "string") return cont(attvaluemaybe);
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
setStyle = "error";
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
}
function attvaluemaybe(type) {
if (type == "string") return cont(attvaluemaybe);
else return pass();
}
return {
startState: function() {
- return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
+ return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};
},
token: function(stream, state) {
- if (stream.sol()) {
+ if (!state.tagName && stream.sol()) {
state.startOfLine = true;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
setStyle = type = tagName = null;
var style = state.tokenize(stream, state);
state.type = type;
if ((style || type) && style != "comment") {
- curState = state;
+ curState = state; curStream = stream;
while (true) {
var comb = state.cc.pop() || element;
if (comb(type || style)) break;
}
}
state.startOfLine = false;
return setStyle || style;
},
indent: function(state, textAfter, fullLine) {
var context = state.context;
if ((state.tokenize != inTag && state.tokenize != inText) ||
context && context.noIndent)
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+ if (state.tagName) return state.tagStart + indentUnit * multilineTagIndentFactor;
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
if (context && /^<\//.test(textAfter))
context = context.prev;
while (context && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return 0;
},
electricChars: "/",
+ blockCommentStart: "<!--",
+ blockCommentEnd: "-->",
configuration: parserConfig.htmlMode ? "html" : "xml"
};
});
CodeMirror.defineMIME("text/xml", "xml");
CodeMirror.defineMIME("application/xml", "xml");
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/javascript/javascript.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/javascript/javascript.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/javascript/javascript.js (revision 15914)
@@ -1,422 +1,476 @@
// TODO actually recognize syntax of TypeScript constructs
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
+ var statementIndent = parserConfig.statementIndent;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
-
+
var jsKeywords = {
- "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+ "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
- "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+ "this": kw("this")
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),
// scope modifiers
"public": kw("public"),
"private": kw("private"),
"protected": kw("protected"),
"static": kw("static"),
"super": kw("super"),
// types
"string": type, "number": type, "bool": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
- var isOperatorChar = /[+\-*&%=<>!?|]/;
+ var isOperatorChar = /[+\-*&%=<>!?|~^]/;
function chain(stream, state, f) {
state.tokenize = f;
return f(stream, state);
}
function nextUntilUnescaped(stream, end) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (next == end && !escaped)
return false;
escaped = !escaped && next == "\\";
}
return escaped;
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type, content;
function ret(tp, style, cont) {
type = tp; content = cont;
return style;
}
function jsTokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'")
return chain(stream, state, jsTokenString(ch));
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
return ret(ch);
else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
- }
+ }
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
}
else if (ch == "/") {
if (stream.eat("*")) {
return chain(stream, state, jsTokenComment);
}
else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
}
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
nextUntilUnescaped(stream, "/");
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
return ret("regexp", "string-2");
}
else {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
}
else if (ch == "#") {
- stream.skipToEnd();
- return ret("error", "error");
+ stream.skipToEnd();
+ return ret("error", "error");
}
else if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return ret("operator", null, stream.current());
}
else {
stream.eatWhile(/[\w\$_]/);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
}
}
function jsTokenString(quote) {
return function(stream, state) {
if (!nextUntilUnescaped(stream, quote))
state.tokenize = jsTokenBase;
return ret("string", "string");
};
}
function jsTokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = jsTokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
// Parser
- var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true};
function JSLexical(indented, column, type, align, prev, info) {
this.indented = indented;
this.column = column;
this.type = type;
this.prev = prev;
this.info = info;
if (align != null) this.align = align;
}
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
}
function parseJS(state, style, type, content, stream) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
-
+
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
while(true) {
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
if (combinator(type, content)) {
while(cc.length && cc[cc.length - 1].lex)
cc.pop()();
if (cx.marked) return cx.marked;
if (type == "variable" && inScope(state, content)) return "variable-2";
return style;
}
}
}
// Combinator utils
var cx = {state: null, column: null, marked: null, cc: null};
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function register(varname) {
function inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state;
if (state.context) {
cx.marked = "def";
if (inList(state.localVars)) return;
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
state.globalVars = {name: varname, next: state.globalVars};
}
}
// Combinators
var defaultVars = {name: "this", next: {name: "arguments"}};
function pushcontext() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
}
function pushlex(type, info) {
var result = function() {
- var state = cx.state;
- state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
+ var state = cx.state, indent = state.indented;
+ if (state.lexical.type == "stat") indent = state.lexical.indented;
+ state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
return result;
}
function poplex() {
var state = cx.state;
if (state.lexical.prev) {
if (state.lexical.type == ")")
state.indented = state.lexical.indented;
state.lexical = state.lexical.prev;
}
}
poplex.lex = true;
function expect(wanted) {
- return function expecting(type) {
+ return function(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else return cont(arguments.callee);
};
}
function statement(type) {
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
+ if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse(cx.state.indented));
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
poplex, statement, poplex);
if (type == "variable") return cont(pushlex("stat"), maybelabel);
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
- if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+ return expressionInner(type, false);
+ }
+ function expressionNoComma(type) {
+ return expressionInner(type, true);
+ }
+ function expressionInner(type, noComma) {
+ var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef);
- if (type == "keyword c") return cont(maybeexpression);
- if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
- if (type == "operator") return cont(expression);
- if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
- if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+ if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+ if (type == "operator") return cont(noComma ? expressionNoComma : expression);
+ if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
+ if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
return cont();
}
function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
-
- function maybeoperator(type, value) {
- if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
- if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
+ function maybeexpressionNoComma(type) {
+ if (type.match(/[;\}\)\],]/)) return pass();
+ return pass(expressionNoComma);
+ }
+
+ function maybeoperatorComma(type, value) {
+ if (type == ",") return cont(expression);
+ return maybeoperatorNoComma(type, value, maybeoperatorComma);
+ }
+ function maybeoperatorNoComma(type, value, me) {
+ if (!me) me = maybeoperatorNoComma;
+ if (type == "operator") {
+ if (/\+\+|--/.test(value)) return cont(me);
+ if (value == "?") return cont(expression, expect(":"), expression);
+ return cont(expression);
+ }
if (type == ";") return;
- if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
- if (type == ".") return cont(property, maybeoperator);
- if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+ if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me);
+ if (type == ".") return cont(property, me);
+ if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
- return pass(maybeoperator, expect(";"), poplex);
+ return pass(maybeoperatorComma, expect(";"), poplex);
}
function property(type) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
- function objprop(type) {
- if (type == "variable") cx.marked = "property";
- if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+ function objprop(type, value) {
+ if (type == "variable") {
+ cx.marked = "property";
+ if (value == "get" || value == "set") return cont(getterSetter);
+ } else if (type == "number" || type == "string") {
+ cx.marked = type + " property";
+ }
+ if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma);
+ }
+ function getterSetter(type) {
+ if (type == ":") return cont(expression);
+ if (type != "variable") return cont(expect(":"), expression);
+ cx.marked = "property";
+ return cont(functiondef);
}
function commasep(what, end) {
function proceed(type) {
- if (type == ",") return cont(what, proceed);
+ if (type == ",") {
+ var lex = cx.state.lexical;
+ if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
+ return cont(what, proceed);
+ }
if (type == end) return cont();
return cont(expect(end));
}
- return function commaSeparated(type) {
+ return function(type) {
if (type == end) return cont();
else return pass(what, proceed);
};
}
function block(type) {
if (type == "}") return cont();
return pass(statement, block);
}
function maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable") {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
}
return pass();
}
function vardef2(type, value) {
- if (value == "=") return cont(expression, vardef2);
+ if (value == "=") return cont(expressionNoComma, vardef2);
if (type == ",") return cont(vardef1);
}
+ function maybeelse(indent) {
+ return function(type, value) {
+ if (type == "keyword b" && value == "else") {
+ cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical);
+ return cont(statement, poplex);
+ }
+ return pass();
+ };
+ }
function forspec1(type) {
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
- return cont(forspec2);
+ return pass(expression, expect(";"), forspec2);
}
function formaybein(_type, value) {
if (value == "in") return cont(expression);
- return cont(maybeoperator, forspec2);
+ return cont(maybeoperatorComma, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in") return cont(expression);
- return cont(expression, expect(";"), forspec3);
+ return pass(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
lastType: null,
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
globalVars: parserConfig.globalVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: 0
};
},
token: function(stream, state) {
if (stream.sol()) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
}
- if (stream.eatSpace()) return null;
+ if (state.tokenize != jsTokenComment && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
- state.lastType = type;
+ state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
if (state.tokenize != jsTokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+ if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
+ lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
+
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
- return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
- else if (lexical.info == "switch" && !closing)
+ return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
+ else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
electricChars: ":{}",
+ blockCommentStart: jsonMode ? null : "/*",
+ blockCommentEnd: jsonMode ? null : "*/",
+ lineComment: jsonMode ? null : "//",
jsonMode: jsonMode
};
});
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/clike/clike.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/clike/clike.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/clike/clike.js (revision 15914)
@@ -1,302 +1,361 @@
CodeMirror.defineMode("clike", function(config, parserConfig) {
var indentUnit = config.indentUnit,
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
dontAlignCalls = parserConfig.dontAlignCalls,
keywords = parserConfig.keywords || {},
builtin = parserConfig.builtin || {},
blockKeywords = parserConfig.blockKeywords || {},
atoms = parserConfig.atoms || {},
hooks = parserConfig.hooks || {},
multiLineStrings = parserConfig.multiLineStrings;
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
var curPunc;
function tokenBase(stream, state) {
var ch = stream.next();
if (hooks[ch]) {
var result = hooks[ch](stream, state);
if (result !== false) return result;
}
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (/\d/.test(ch)) {
stream.eatWhile(/[\w\.]/);
return "number";
}
if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
}
if (stream.eat("/")) {
stream.skipToEnd();
return "comment";
}
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";
}
stream.eatWhile(/[\w\$_]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "keyword";
}
if (builtin.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "builtin";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
escaped = !escaped && next == "\\";
}
if (end || !(escaped || multiLineStrings))
state.tokenize = null;
return "string";
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = null;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
var indent = state.indented;
if (state.context && state.context.type == "statement")
indent = state.context.indented;
return state.context = new Context(indent, col, type, null, state.context);
}
function popContext(state) {
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
return state.context = state.context.prev;
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function(stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
}
if (stream.eatSpace()) return null;
curPunc = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment" || style == "meta") return style;
if (ctx.align == null) ctx.align = true;
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
else if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}") {
while (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
- else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
- else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+ else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1);
+ else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
else return ctx.indented + (closing ? 0 : indentUnit);
},
- electricChars: "{}"
+ electricChars: "{}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//"
};
});
(function() {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
"double static else struct entry switch extern typedef float union for unsigned " +
"goto while enum void const signed volatile";
function cppHook(stream, state) {
if (!state.startOfLine) return false;
for (;;) {
if (stream.skipTo("\\")) {
stream.next();
if (stream.eol()) {
state.tokenize = cppHook;
break;
}
} else {
stream.skipToEnd();
state.tokenize = null;
break;
}
}
return "meta";
}
// C#-style strings where "" escapes a quote.
function tokenAtString(stream, state) {
var next;
while ((next = stream.next()) != null) {
if (next == '"' && !stream.eat('"')) {
state.tokenize = null;
break;
}
}
return "string";
}
function mimes(ms, mode) {
for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
}
mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
name: "clike",
keywords: words(cKeywords),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
hooks: {"#": cppHook}
});
mimes(["text/x-c++src", "text/x-c++hdr"], {
name: "clike",
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
"static_cast typeid catch operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected " +
"wchar_t"),
blockKeywords: words("catch class do else finally for if struct switch try while"),
atoms: words("true false null"),
hooks: {"#": cppHook}
});
CodeMirror.defineMIME("text/x-java", {
name: "clike",
- keywords: words("abstract assert boolean break byte case catch char class const continue default " +
+ keywords: words("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends final finally float for goto if implements import " +
"instanceof int interface long native new package private protected public " +
"return short static strictfp super switch synchronized this throw throws transient " +
"try void volatile while"),
blockKeywords: words("catch class do else finally for if switch try while"),
atoms: words("true false null"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
CodeMirror.defineMIME("text/x-csharp", {
name: "clike",
- keywords: words("abstract as base break case catch checked class const continue" +
- " default delegate do else enum event explicit extern finally fixed for" +
- " foreach goto if implicit in interface internal is lock namespace new" +
- " operator out override params private protected public readonly ref return sealed" +
- " sizeof stackalloc static struct switch this throw try typeof unchecked" +
- " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
+ keywords: words("abstract as base break case catch checked class const continue" +
+ " default delegate do else enum event explicit extern finally fixed for" +
+ " foreach goto if implicit in interface internal is lock namespace new" +
+ " operator out override params private protected public readonly ref return sealed" +
+ " sizeof stackalloc static struct switch this throw try typeof unchecked" +
+ " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
" global group into join let orderby partial remove select set value var yield"),
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
" Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
" UInt64 bool byte char decimal double short int long object" +
" sbyte float string ushort uint ulong"),
atoms: words("true false null"),
hooks: {
"@": function(stream, state) {
if (stream.eat('"')) {
state.tokenize = tokenAtString;
return tokenAtString(stream, state);
}
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
CodeMirror.defineMIME("text/x-scala", {
name: "clike",
keywords: words(
-
+
/* scala */
"abstract case catch class def do else extends false final finally for forSome if " +
"implicit import lazy match new null object override package private protected return " +
"sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
"<% >: # @ " +
-
+
/* package scala */
"assert assume require print println printf readLine readBoolean readByte readShort " +
"readChar readInt readLong readFloat readDouble " +
-
+
"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
"Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
-
- /* package java.lang */
+
+ /* package java.lang */
"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
-
-
+
+
),
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
atoms: words("true false null"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
+ mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
+ name: "clike",
+ keywords: words("float int bool void " +
+ "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
+ "mat2 mat3 mat4 " +
+ "sampler1D sampler2D sampler3D samplerCube " +
+ "sampler1DShadow sampler2DShadow" +
+ "const attribute uniform varying " +
+ "break continue discard return " +
+ "for while do if else struct " +
+ "in out inout"),
+ blockKeywords: words("for while do if else struct"),
+ builtin: words("radians degrees sin cos tan asin acos atan " +
+ "pow exp log exp2 sqrt inversesqrt " +
+ "abs sign floor ceil fract mod min max clamp mix step smootstep " +
+ "length distance dot cross normalize ftransform faceforward " +
+ "reflect refract matrixCompMult " +
+ "lessThan lessThanEqual greaterThan greaterThanEqual " +
+ "equal notEqual any all not " +
+ "texture1D texture1DProj texture1DLod texture1DProjLod " +
+ "texture2D texture2DProj texture2DLod texture2DProjLod " +
+ "texture3D texture3DProj texture3DLod texture3DProjLod " +
+ "textureCube textureCubeLod " +
+ "shadow1D shadow2D shadow1DProj shadow2DProj " +
+ "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
+ "dFdx dFdy fwidth " +
+ "noise1 noise2 noise3 noise4"),
+ atoms: words("true false " +
+ "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
+ "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
+ "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
+ "gl_FogCoord " +
+ "gl_Position gl_PointSize gl_ClipVertex " +
+ "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
+ "gl_TexCoord gl_FogFragCoord " +
+ "gl_FragCoord gl_FrontFacing " +
+ "gl_FragColor gl_FragData gl_FragDepth " +
+ "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
+ "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
+ "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
+ "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
+ "gl_ProjectionMatrixInverseTranspose " +
+ "gl_ModelViewProjectionMatrixInverseTranspose " +
+ "gl_TextureMatrixInverseTranspose " +
+ "gl_NormalScale gl_DepthRange gl_ClipPlane " +
+ "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
+ "gl_FrontLightModelProduct gl_BackLightModelProduct " +
+ "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
+ "gl_FogParameters " +
+ "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
+ "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
+ "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
+ "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
+ "gl_MaxDrawBuffers"),
+ hooks: {"#": cppHook}
+ });
}());
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/css/css.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/css/css.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/css/css.js (revision 15914)
@@ -1,465 +1,606 @@
CodeMirror.defineMode("css", function(config) {
- var indentUnit = config.indentUnit, type;
-
- var atMediaTypes = keySet([
- "all", "aural", "braille", "handheld", "print", "projection", "screen",
- "tty", "tv", "embossed"
- ]);
-
- var atMediaFeatures = keySet([
- "width", "min-width", "max-width", "height", "min-height", "max-height",
- "device-width", "min-device-width", "max-device-width", "device-height",
- "min-device-height", "max-device-height", "aspect-ratio",
- "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
- "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
- "max-color", "color-index", "min-color-index", "max-color-index",
- "monochrome", "min-monochrome", "max-monochrome", "resolution",
- "min-resolution", "max-resolution", "scan", "grid"
- ]);
+ return CodeMirror.getMode(config, "text/css");
+});
- var propertyKeywords = keySet([
- "align-content", "align-items", "align-self", "alignment-adjust",
- "alignment-baseline", "anchor-point", "animation", "animation-delay",
- "animation-direction", "animation-duration", "animation-iteration-count",
- "animation-name", "animation-play-state", "animation-timing-function",
- "appearance", "azimuth", "backface-visibility", "background",
- "background-attachment", "background-clip", "background-color",
- "background-image", "background-origin", "background-position",
- "background-repeat", "background-size", "baseline-shift", "binding",
- "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
- "bookmark-target", "border", "border-bottom", "border-bottom-color",
- "border-bottom-left-radius", "border-bottom-right-radius",
- "border-bottom-style", "border-bottom-width", "border-collapse",
- "border-color", "border-image", "border-image-outset",
- "border-image-repeat", "border-image-slice", "border-image-source",
- "border-image-width", "border-left", "border-left-color",
- "border-left-style", "border-left-width", "border-radius", "border-right",
- "border-right-color", "border-right-style", "border-right-width",
- "border-spacing", "border-style", "border-top", "border-top-color",
- "border-top-left-radius", "border-top-right-radius", "border-top-style",
- "border-top-width", "border-width", "bottom", "box-decoration-break",
- "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
- "caption-side", "clear", "clip", "color", "color-profile", "column-count",
- "column-fill", "column-gap", "column-rule", "column-rule-color",
- "column-rule-style", "column-rule-width", "column-span", "column-width",
- "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
- "cue-after", "cue-before", "cursor", "direction", "display",
- "dominant-baseline", "drop-initial-after-adjust",
- "drop-initial-after-align", "drop-initial-before-adjust",
- "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
- "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
- "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
- "float", "float-offset", "font", "font-feature-settings", "font-family",
- "font-kerning", "font-language-override", "font-size", "font-size-adjust",
- "font-stretch", "font-style", "font-synthesis", "font-variant",
- "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
- "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
- "font-weight", "grid-cell", "grid-column", "grid-column-align",
- "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
- "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
- "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
- "icon", "image-orientation", "image-rendering", "image-resolution",
- "inline-box-align", "justify-content", "left", "letter-spacing",
- "line-break", "line-height", "line-stacking", "line-stacking-ruby",
- "line-stacking-shift", "line-stacking-strategy", "list-style",
- "list-style-image", "list-style-position", "list-style-type", "margin",
- "margin-bottom", "margin-left", "margin-right", "margin-top",
- "marker-offset", "marks", "marquee-direction", "marquee-loop",
- "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
- "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
- "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
- "outline-color", "outline-offset", "outline-style", "outline-width",
- "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
- "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
- "page", "page-break-after", "page-break-before", "page-break-inside",
- "page-policy", "pause", "pause-after", "pause-before", "perspective",
- "perspective-origin", "pitch", "pitch-range", "play-during", "position",
- "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
- "resize", "rest", "rest-after", "rest-before", "richness", "right",
- "rotation", "rotation-point", "ruby-align", "ruby-overhang",
- "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
- "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
- "tab-size", "table-layout", "target", "target-name", "target-new",
- "target-position", "text-align", "text-align-last", "text-decoration",
- "text-decoration-color", "text-decoration-line", "text-decoration-skip",
- "text-decoration-style", "text-emphasis", "text-emphasis-color",
- "text-emphasis-position", "text-emphasis-style", "text-height",
- "text-indent", "text-justify", "text-outline", "text-shadow",
- "text-space-collapse", "text-transform", "text-underline-position",
- "text-wrap", "top", "transform", "transform-origin", "transform-style",
- "transition", "transition-delay", "transition-duration",
- "transition-property", "transition-timing-function", "unicode-bidi",
- "vertical-align", "visibility", "voice-balance", "voice-duration",
- "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
- "voice-volume", "volume", "white-space", "widows", "width", "word-break",
- "word-spacing", "word-wrap", "z-index"
- ]);
+CodeMirror.defineMode("css-base", function(config, parserConfig) {
+ "use strict";
- var colorKeywords = keySet([
- "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
- "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
- ]);
-
- var valueKeywords = keySet([
- "above", "absolute", "activeborder", "activecaption", "afar",
- "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
- "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
- "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
- "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
- "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
- "both", "bottom", "break-all", "break-word", "button", "button-bevel",
- "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
- "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
- "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
- "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
- "col-resize", "collapse", "compact", "condensed", "contain", "content",
- "content-box", "context-menu", "continuous", "copy", "cover", "crop",
- "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
- "decimal-leading-zero", "default", "default-button", "destination-atop",
- "destination-in", "destination-out", "destination-over", "devanagari",
- "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
- "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
- "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
- "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
- "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
- "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
- "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
- "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
- "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
- "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
- "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
- "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
- "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
- "help", "hidden", "hide", "higher", "highlight", "highlighttext",
- "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
- "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
- "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
- "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
- "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
- "landscape", "lao", "large", "larger", "left", "level", "lighter",
- "line-through", "linear", "lines", "list-item", "listbox", "listitem",
- "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
- "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
- "lower-roman", "lowercase", "ltr", "malayalam", "match",
- "media-controls-background", "media-current-time-display",
- "media-fullscreen-button", "media-mute-button", "media-play-button",
- "media-return-to-realtime-button", "media-rewind-button",
- "media-seek-back-button", "media-seek-forward-button", "media-slider",
- "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
- "media-volume-slider-container", "media-volume-sliderthumb", "medium",
- "menu", "menulist", "menulist-button", "menulist-text",
- "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
- "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
- "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
- "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
- "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
- "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
- "outside", "overlay", "overline", "padding", "padding-box", "painted",
- "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
- "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
- "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
- "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
- "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
- "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
- "searchfield-cancel-button", "searchfield-decoration",
- "searchfield-results-button", "searchfield-results-decoration",
- "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
- "single", "skip-white-space", "slide", "slider-horizontal",
- "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
- "small", "small-caps", "small-caption", "smaller", "solid", "somali",
- "source-atop", "source-in", "source-out", "source-over", "space", "square",
- "square-button", "start", "static", "status-bar", "stretch", "stroke",
- "sub", "subpixel-antialiased", "super", "sw-resize", "table",
- "table-caption", "table-cell", "table-column", "table-column-group",
- "table-footer-group", "table-header-group", "table-row", "table-row-group",
- "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
- "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
- "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
- "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
- "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
- "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
- "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
- "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
- "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
- "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
- "xx-large", "xx-small", "yellow"
- ]);
+ var indentUnit = config.indentUnit,
+ hooks = parserConfig.hooks || {},
+ atMediaTypes = parserConfig.atMediaTypes || {},
+ atMediaFeatures = parserConfig.atMediaFeatures || {},
+ propertyKeywords = parserConfig.propertyKeywords || {},
+ colorKeywords = parserConfig.colorKeywords || {},
+ valueKeywords = parserConfig.valueKeywords || {},
+ allowNested = !!parserConfig.allowNested,
+ type = null;
- function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
- function ret(style, tp) {type = tp; return style;}
+ function ret(style, tp) { type = tp; return style; }
function tokenBase(stream, state) {
var ch = stream.next();
- if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
- else if (ch == "/" && stream.eat("*")) {
- state.tokenize = tokenCComment;
- return tokenCComment(stream, state);
- }
- else if (ch == "<" && stream.eat("!")) {
- state.tokenize = tokenSGMLComment;
- return tokenSGMLComment(stream, state);
+ if (hooks[ch]) {
+ // result[0] is style and result[1] is type
+ var result = hooks[ch](stream, state);
+ if (result !== false) return result;
}
+ if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
else if (ch == "=") ret(null, "compare");
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
else if (ch == "\"" || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
else if (ch == "#") {
stream.eatWhile(/[\w\\\-]/);
return ret("atom", "hash");
}
else if (ch == "!") {
stream.match(/^\s*\w*/);
return ret("keyword", "important");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
}
else if (ch === "-") {
if (/\d/.test(stream.peek())) {
stream.eatWhile(/[\w.%]/);
return ret("number", "unit");
} else if (stream.match(/^[^-]+-/)) {
return ret("meta", "meta");
}
}
else if (/[,+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
return ret("qualifier", "qualifier");
}
else if (ch == ":") {
return ret("operator", ch);
}
else if (/[;{}\[\]\(\)]/.test(ch)) {
return ret(null, ch);
}
else if (ch == "u" && stream.match("rl(")) {
stream.backUp(1);
state.tokenize = tokenParenthesized;
return ret("property", "variable");
}
else {
stream.eatWhile(/[\w\\\-]/);
return ret("property", "variable");
}
}
- function tokenCComment(stream, state) {
- var maybeEnd = false, ch;
- while ((ch = stream.next()) != null) {
- if (maybeEnd && ch == "/") {
- state.tokenize = tokenBase;
- break;
- }
- maybeEnd = (ch == "*");
- }
- return ret("comment", "comment");
- }
-
- function tokenSGMLComment(stream, state) {
- var dashes = 0, ch;
- while ((ch = stream.next()) != null) {
- if (dashes >= 2 && ch == ">") {
- state.tokenize = tokenBase;
- break;
- }
- dashes = (ch == "-") ? dashes + 1 : 0;
- }
- return ret("comment", "comment");
- }
-
function tokenString(quote, nonInclusive) {
return function(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped) {
if (nonInclusive) stream.backUp(1);
state.tokenize = tokenBase;
}
return ret("string", "string");
};
}
function tokenParenthesized(stream, state) {
stream.next(); // Must be '('
if (!stream.match(/\s*[\"\']/, false))
state.tokenize = tokenString(")", true);
else
state.tokenize = tokenBase;
return ret(null, "(");
}
return {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
stack: []};
},
token: function(stream, state) {
-
+
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
- //
+ //
// rule** or **ruleset:
// A selector + braces combo, or an at-rule.
- //
+ //
// declaration block:
// A sequence of declarations.
- //
+ //
// declaration:
// A property + colon + value combo.
- //
+ //
// property value:
// The entire value of a property.
- //
+ //
// component value:
// A single piece of a property value. Like the 5px in
// text-shadow: 0 0 5px blue;. Can also refer to things that are
// multiple terms, like the 1-4 terms that make up the background-size
// portion of the background shorthand.
- //
+ //
// term:
// The basic unit of author-facing CSS, like a single number (5),
// dimension (5px), string ("foo"), or function. Officially defined
// by the CSS 2.1 grammar (look for the 'term' production)
- //
- //
+ //
+ //
// simple selector:
// A single atomic selector, like a type selector, an attr selector, a
// class selector, etc.
- //
+ //
// compound selector:
// One or more simple selectors without a combinator. div.example is
// compound, div > .example is not.
- //
+ //
// complex selector:
// One or more compound selectors chained with combinators.
- //
+ //
// combinator:
// The parts of selectors that express relationships. There are four
// currently - the space (descendant combinator), the greater-than
// bracket (child combinator), the plus sign (next sibling combinator),
// and the tilda (following sibling combinator).
- //
+ //
// sequence of selectors:
// One or more of the named type of selector chained with commas.
+ state.tokenize = state.tokenize || tokenBase;
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
+ if (style && typeof style != "string") style = ret(style[0], style[1]);
// Changing style returned based on context
var context = state.stack[state.stack.length-1];
- if (style == "property") {
- if (context == "propertyValue"){
- if (valueKeywords[stream.current()]) {
+ if (style == "variable") {
+ if (type == "variable-definition") state.stack.push("propertyValue");
+ return "variable-2";
+ } else if (style == "property") {
+ var word = stream.current().toLowerCase();
+ if (context == "propertyValue") {
+ if (valueKeywords.hasOwnProperty(word)) {
style = "string-2";
- } else if (colorKeywords[stream.current()]) {
+ } else if (colorKeywords.hasOwnProperty(word)) {
style = "keyword";
} else {
style = "variable-2";
}
} else if (context == "rule") {
- if (!propertyKeywords[stream.current()]) {
+ if (!propertyKeywords.hasOwnProperty(word)) {
style += " error";
}
+ } else if (context == "block") {
+ // if a value is present in both property, value, or color, the order
+ // of preference is property -> color -> value
+ if (propertyKeywords.hasOwnProperty(word)) {
+ style = "property";
+ } else if (colorKeywords.hasOwnProperty(word)) {
+ style = "keyword";
+ } else if (valueKeywords.hasOwnProperty(word)) {
+ style = "string-2";
+ } else {
+ style = "tag";
+ }
} else if (!context || context == "@media{") {
style = "tag";
} else if (context == "@media") {
if (atMediaTypes[stream.current()]) {
style = "attribute"; // Known attribute
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "keyword";
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "error"; // "and" is only allowed in @mediaType
- } else if (atMediaFeatures[stream.current()]) {
+ } else if (atMediaFeatures.hasOwnProperty(word)) {
style = "error"; // Known property, should be in @mediaType(
} else {
// Unknown, expecting keyword or attribute, assuming attribute
style = "attribute error";
}
} else if (context == "@mediaType") {
- if (atMediaTypes[stream.current()]) {
+ if (atMediaTypes.hasOwnProperty(word)) {
style = "attribute";
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "operator";
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
- } else if (atMediaFeatures[stream.current()]) {
- style = "error"; // Known property, should be in parentheses
} else {
// Unknown attribute or property, but expecting property (preceded
// by "and"). Should be in parentheses
style = "error";
}
} else if (context == "@mediaType(") {
- if (propertyKeywords[stream.current()]) {
+ if (propertyKeywords.hasOwnProperty(word)) {
// do nothing, remains "property"
- } else if (atMediaTypes[stream.current()]) {
+ } else if (atMediaTypes.hasOwnProperty(word)) {
style = "error"; // Known property, should be in parentheses
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "operator";
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
} else {
style += " error";
}
+ } else if (context == "@import") {
+ style = "tag";
} else {
style = "error";
}
} else if (style == "atom") {
- if(!context || context == "@media{") {
+ if(!context || context == "@media{" || context == "block") {
style = "builtin";
} else if (context == "propertyValue") {
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
style += " error";
}
} else {
style = "error";
}
} else if (context == "@media" && type == "{") {
style = "error";
}
// Push/pop context stack
if (type == "{") {
if (context == "@media" || context == "@mediaType") {
state.stack.pop();
state.stack[state.stack.length-1] = "@media{";
}
- else state.stack.push("rule");
+ else {
+ var newContext = allowNested ? "block" : "rule";
+ state.stack.push(newContext);
+ }
}
else if (type == "}") {
+ var lastState = state.stack[state.stack.length - 1];
+ if (lastState == "interpolation") style = "operator";
state.stack.pop();
if (context == "propertyValue") state.stack.pop();
}
+ else if (type == "interpolation") state.stack.push("interpolation");
else if (type == "@media") state.stack.push("@media");
+ else if (type == "@import") state.stack.push("@import");
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
state.stack.push("@mediaType");
else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
else if (context == "@mediaType(" && type == ")") state.stack.pop();
- else if (context == "rule" && type == ":") state.stack.push("propertyValue");
+ else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue");
else if (context == "propertyValue" && type == ";") state.stack.pop();
+ else if (context == "@import" && type == ";") state.stack.pop();
return style;
},
indent: function(state, textAfter) {
var n = state.stack.length;
if (/^\}/.test(textAfter))
n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
return state.baseIndent + n * indentUnit;
},
- electricChars: "}"
+ electricChars: "}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/"
};
});
-CodeMirror.defineMIME("text/css", "css");
+(function() {
+ function keySet(array) {
+ var keys = {};
+ for (var i = 0; i < array.length; ++i) {
+ keys[array[i]] = true;
+ }
+ return keys;
+ }
+
+ var atMediaTypes = keySet([
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
+ "tty", "tv", "embossed"
+ ]);
+
+ var atMediaFeatures = keySet([
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
+ "device-width", "min-device-width", "max-device-width", "device-height",
+ "min-device-height", "max-device-height", "aspect-ratio",
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+ "max-color", "color-index", "min-color-index", "max-color-index",
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
+ "min-resolution", "max-resolution", "scan", "grid"
+ ]);
+
+ var propertyKeywords = keySet([
+ "align-content", "align-items", "align-self", "alignment-adjust",
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
+ "animation-direction", "animation-duration", "animation-iteration-count",
+ "animation-name", "animation-play-state", "animation-timing-function",
+ "appearance", "azimuth", "backface-visibility", "background",
+ "background-attachment", "background-clip", "background-color",
+ "background-image", "background-origin", "background-position",
+ "background-repeat", "background-size", "baseline-shift", "binding",
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
+ "border-bottom-left-radius", "border-bottom-right-radius",
+ "border-bottom-style", "border-bottom-width", "border-collapse",
+ "border-color", "border-image", "border-image-outset",
+ "border-image-repeat", "border-image-slice", "border-image-source",
+ "border-image-width", "border-left", "border-left-color",
+ "border-left-style", "border-left-width", "border-radius", "border-right",
+ "border-right-color", "border-right-style", "border-right-width",
+ "border-spacing", "border-style", "border-top", "border-top-color",
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+ "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+ "cue-after", "cue-before", "cursor", "direction", "display",
+ "dominant-baseline", "drop-initial-after-adjust",
+ "drop-initial-after-align", "drop-initial-before-adjust",
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+ "float", "float-offset", "font", "font-feature-settings", "font-family",
+ "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+ "font-weight", "grid-cell", "grid-column", "grid-column-align",
+ "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
+ "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
+ "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
+ "icon", "image-orientation", "image-rendering", "image-resolution",
+ "inline-box-align", "justify-content", "left", "letter-spacing",
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
+ "list-style-image", "list-style-position", "list-style-type", "margin",
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
+ "marker-offset", "marks", "marquee-direction", "marquee-loop",
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+ "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+ "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
+ "outline-color", "outline-offset", "outline-style", "outline-width",
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+ "page", "page-break-after", "page-break-before", "page-break-inside",
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
+ "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+ "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
+ "resize", "rest", "rest-after", "rest-before", "richness", "right",
+ "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+ "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+ "tab-size", "table-layout", "target", "target-name", "target-new",
+ "target-position", "text-align", "text-align-last", "text-decoration",
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
+ "text-emphasis-position", "text-emphasis-style", "text-height",
+ "text-indent", "text-justify", "text-outline", "text-shadow",
+ "text-space-collapse", "text-transform", "text-underline-position",
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
+ "transition", "transition-delay", "transition-duration",
+ "transition-property", "transition-timing-function", "unicode-bidi",
+ "vertical-align", "visibility", "voice-balance", "voice-duration",
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+ "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+ "word-spacing", "word-wrap", "z-index",
+ // SVG-specific
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+ "color-interpolation", "color-interpolation-filters", "color-profile",
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+ "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
+ ]);
+
+ var colorKeywords = keySet([
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+ "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew",
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+ "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
+ "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+ "whitesmoke", "yellow", "yellowgreen"
+ ]);
+
+ var valueKeywords = keySet([
+ "above", "absolute", "activeborder", "activecaption", "afar",
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+ "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
+ "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
+ "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+ "both", "bottom", "break-all", "break-word", "button", "button-bevel",
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+ "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+ "col-resize", "collapse", "compact", "condensed", "contain", "content",
+ "content-box", "context-menu", "continuous", "copy", "cover", "crop",
+ "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
+ "decimal-leading-zero", "default", "default-button", "destination-atop",
+ "destination-in", "destination-out", "destination-over", "devanagari",
+ "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
+ "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+ "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
+ "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+ "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
+ "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
+ "landscape", "lao", "large", "larger", "left", "level", "lighter",
+ "line-through", "linear", "lines", "list-item", "listbox", "listitem",
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+ "lower-roman", "lowercase", "ltr", "malayalam", "match",
+ "media-controls-background", "media-current-time-display",
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
+ "media-return-to-realtime-button", "media-rewind-button",
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+ "menu", "menulist", "menulist-button", "menulist-text",
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+ "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
+ "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+ "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+ "outside", "overlay", "overline", "padding", "padding-box", "painted",
+ "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
+ "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
+ "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
+ "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
+ "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
+ "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
+ "searchfield-cancel-button", "searchfield-decoration",
+ "searchfield-results-button", "searchfield-results-decoration",
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+ "single", "skip-white-space", "slide", "slider-horizontal",
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+ "small", "small-caps", "small-caption", "smaller", "solid", "somali",
+ "source-atop", "source-in", "source-out", "source-over", "space", "square",
+ "square-button", "start", "static", "status-bar", "stretch", "stroke",
+ "sub", "subpixel-antialiased", "super", "sw-resize", "table",
+ "table-caption", "table-cell", "table-column", "table-column-group",
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+ "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+ "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
+ "xx-large", "xx-small"
+ ]);
+
+ function tokenCComment(stream, state) {
+ var maybeEnd = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (maybeEnd && ch == "/") {
+ state.tokenize = null;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ["comment", "comment"];
+ }
+
+ CodeMirror.defineMIME("text/css", {
+ atMediaTypes: atMediaTypes,
+ atMediaFeatures: atMediaFeatures,
+ propertyKeywords: propertyKeywords,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ hooks: {
+ "<": function(stream, state) {
+ function tokenSGMLComment(stream, state) {
+ var dashes = 0, ch;
+ while ((ch = stream.next()) != null) {
+ if (dashes >= 2 && ch == ">") {
+ state.tokenize = null;
+ break;
+ }
+ dashes = (ch == "-") ? dashes + 1 : 0;
+ }
+ return ["comment", "comment"];
+ }
+ if (stream.eat("!")) {
+ state.tokenize = tokenSGMLComment;
+ return tokenSGMLComment(stream, state);
+ }
+ },
+ "/": function(stream, state) {
+ if (stream.eat("*")) {
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ }
+ return false;
+ }
+ },
+ name: "css-base"
+ });
+
+ CodeMirror.defineMIME("text/x-scss", {
+ atMediaTypes: atMediaTypes,
+ atMediaFeatures: atMediaFeatures,
+ propertyKeywords: propertyKeywords,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ allowNested: true,
+ hooks: {
+ "$": function(stream) {
+ stream.match(/^[\w-]+/);
+ if (stream.peek() == ":") {
+ return ["variable", "variable-definition"];
+ }
+ return ["variable", "variable"];
+ },
+ "/": function(stream, state) {
+ if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ["comment", "comment"];
+ } else if (stream.eat("*")) {
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ } else {
+ return ["operator", "operator"];
+ }
+ },
+ "#": function(stream) {
+ if (stream.eat("{")) {
+ return ["operator", "interpolation"];
+ } else {
+ stream.eatWhile(/[\w\\\-]/);
+ return ["atom", "hash"];
+ }
+ }
+ },
+ name: "css-base"
+ });
+})();
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/mode/htmlmixed/htmlmixed.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/mode/htmlmixed/htmlmixed.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/mode/htmlmixed/htmlmixed.js (revision 15914)
@@ -1,84 +1,104 @@
-CodeMirror.defineMode("htmlmixed", function(config) {
+CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
- var jsMode = CodeMirror.getMode(config, "javascript");
var cssMode = CodeMirror.getMode(config, "css");
+ var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes;
+ scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i,
+ mode: CodeMirror.getMode(config, "javascript")});
+ if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) {
+ var conf = scriptTypesConf[i];
+ scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)});
+ }
+ scriptTypes.push({matches: /./,
+ mode: CodeMirror.getMode(config, "text/plain")});
+
function html(stream, state) {
+ var tagName = state.htmlState.tagName;
var style = htmlMode.token(stream, state.htmlState);
- if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
- if (/^script$/i.test(state.htmlState.context.tagName)) {
- state.token = javascript;
- state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
- }
- else if (/^style$/i.test(state.htmlState.context.tagName)) {
- state.token = css;
- state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
+ if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
+ // Script block: mode to change to depends on type attribute
+ var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i);
+ scriptType = scriptType ? scriptType[1] : "";
+ if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1);
+ for (var i = 0; i < scriptTypes.length; ++i) {
+ var tp = scriptTypes[i];
+ if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) {
+ if (tp.mode) {
+ state.token = script;
+ state.localMode = tp.mode;
+ state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, ""));
+ }
+ break;
+ }
}
+ } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") {
+ state.token = css;
+ state.localMode = cssMode;
+ state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
}
return style;
}
function maybeBackup(stream, pat, style) {
var cur = stream.current();
var close = cur.search(pat), m;
if (close > -1) stream.backUp(cur.length - close);
else if (m = cur.match(/<\/?$/)) {
stream.backUp(cur.length);
if (!stream.match(pat, false)) stream.match(cur[0]);
}
return style;
}
- function javascript(stream, state) {
+ function script(stream, state) {
if (stream.match(/^<\/\s*script\s*>/i, false)) {
state.token = html;
- state.localState = null;
+ state.localState = state.localMode = null;
return html(stream, state);
}
return maybeBackup(stream, /<\/\s*script\s*>/,
- jsMode.token(stream, state.localState));
+ state.localMode.token(stream, state.localState));
}
function css(stream, state) {
if (stream.match(/^<\/\s*style\s*>/i, false)) {
state.token = html;
- state.localState = null;
+ state.localState = state.localMode = null;
return html(stream, state);
}
return maybeBackup(stream, /<\/\s*style\s*>/,
cssMode.token(stream, state.localState));
}
return {
startState: function() {
var state = htmlMode.startState();
- return {token: html, localState: null, mode: "html", htmlState: state};
+ return {token: html, localMode: null, localState: null, htmlState: state};
},
copyState: function(state) {
if (state.localState)
- var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
- return {token: state.token, localState: local, mode: state.mode,
+ var local = CodeMirror.copyState(state.localMode, state.localState);
+ return {token: state.token, localMode: state.localMode, localState: local,
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
},
token: function(stream, state) {
return state.token(stream, state);
},
indent: function(state, textAfter) {
- if (state.token == html || /^\s*<\//.test(textAfter))
+ if (!state.localMode || /^\s*<\//.test(textAfter))
return htmlMode.indent(state.htmlState, textAfter);
- else if (state.token == javascript)
- return jsMode.indent(state.localState, textAfter);
+ else if (state.localMode.indent)
+ return state.localMode.indent(state.localState, textAfter);
else
- return cssMode.indent(state.localState, textAfter);
+ return CodeMirror.Pass;
},
electricChars: "/{}:",
innerMode: function(state) {
- var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
- return {state: state.localState || state.htmlState, mode: mode};
+ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
}
};
}, "xml", "javascript", "css");
CodeMirror.defineMIME("text/html", "htmlmixed");
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/addon/selection/active-line.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/addon/selection/active-line.js (nonexistent)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/addon/selection/active-line.js (revision 15914)
@@ -0,0 +1,39 @@
+// Because sometimes you need to style the cursor's line.
+//
+// Adds an option 'styleActiveLine' which, when enabled, gives the
+// active line's wrapping <div> the CSS class "CodeMirror-activeline",
+// and gives its background <div> the class "CodeMirror-activeline-background".
+
+(function() {
+ "use strict";
+ var WRAP_CLASS = "CodeMirror-activeline";
+ var BACK_CLASS = "CodeMirror-activeline-background";
+
+ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+ var prev = old && old != CodeMirror.Init;
+ if (val && !prev) {
+ updateActiveLine(cm);
+ cm.on("cursorActivity", updateActiveLine);
+ } else if (!val && prev) {
+ cm.off("cursorActivity", updateActiveLine);
+ clearActiveLine(cm);
+ delete cm.state.activeLine;
+ }
+ });
+
+ function clearActiveLine(cm) {
+ if ("activeLine" in cm.state) {
+ cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS);
+ cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS);
+ }
+ }
+
+ function updateActiveLine(cm) {
+ var line = cm.getLineHandle(cm.getCursor().line);
+ if (cm.state.activeLine == line) return;
+ clearActiveLine(cm);
+ cm.addLineClass(line, "wrap", WRAP_CLASS);
+ cm.addLineClass(line, "background", BACK_CLASS);
+ cm.state.activeLine = line;
+ }
+})();
Property changes on: branches/5.3.x/core/admin_templates/incs/code_mirror/addon/selection/active-line.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+LF
\ No newline at end of property
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.js
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.js (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.js (revision 15914)
@@ -1,4786 +1,5715 @@
-// CodeMirror version 3.02
+// CodeMirror version 3.14
//
// CodeMirror is the only global var we claim
window.CodeMirror = (function() {
"use strict";
// BROWSER SNIFFING
// Crude, but necessary to handle a number of hard-to-feature-detect
// bugs and behavior differences.
var gecko = /gecko\/\d/i.test(navigator.userAgent);
var ie = /MSIE \d/.test(navigator.userAgent);
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
var webkit = /WebKit\//.test(navigator.userAgent);
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
var chrome = /Chrome\//.test(navigator.userAgent);
var opera = /Opera\//.test(navigator.userAgent);
var safari = /Apple Computer/.test(navigator.vendor);
var khtml = /KHTML\//.test(navigator.userAgent);
var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent);
var phantom = /PhantomJS/.test(navigator.userAgent);
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
// This is woefully incomplete. Suggestions for alternative methods welcome.
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
var mac = ios || /Mac/.test(navigator.platform);
var windows = /windows/i.test(navigator.platform);
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
if (opera_version) opera_version = Number(opera_version[1]);
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
+ var captureMiddleClick = gecko || (ie && !ie_lt9);
// Optimize some code when these features are not used
var sawReadOnlySpans = false, sawCollapsedSpans = false;
// CONSTRUCTOR
function CodeMirror(place, options) {
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
-
+
this.options = options = options || {};
// Determine effective options based on given values and defaults.
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
options[opt] = defaults[opt];
setGuttersForLineNumbers(options);
- var display = this.display = makeDisplay(place);
+ var docStart = typeof options.value == "string" ? 0 : options.value.first;
+ var display = this.display = makeDisplay(place, docStart);
display.wrapper.CodeMirror = this;
updateGutters(this);
if (options.autofocus && !mobile) focusInput(this);
- this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]));
- this.nextOpId = 0;
- loadMode(this);
+ this.state = {keyMaps: [],
+ overlays: [],
+ modeGen: 0,
+ overwrite: false, focused: false,
+ suppressEdits: false, pasteIncoming: false,
+ draggingText: false,
+ highlight: new Delayed()};
+
themeChanged(this);
if (options.lineWrapping)
this.display.wrapper.className += " CodeMirror-wrap";
- // Initialize the content.
- this.setValue(options.value || "");
+ var doc = options.value;
+ if (typeof doc == "string") doc = new Doc(options.value, options.mode);
+ operation(this, attachDoc)(this, doc);
+
// Override magic textarea content restore that IE sometimes does
// on our hidden textarea on reload
if (ie) setTimeout(bind(resetInput, this, true), 20);
- this.view.history = makeHistory();
registerEventHandlers(this);
// IE throws unspecified error in certain cases, when
// trying to access activeElement before onload
var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20);
else onBlur(this);
operation(this, function() {
for (var opt in optionHandlers)
if (optionHandlers.propertyIsEnumerable(opt))
optionHandlers[opt](this, options[opt], Init);
for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
})();
}
// DISPLAY CONSTRUCTOR
- function makeDisplay(place) {
+ function makeDisplay(place, docStart) {
var d = {};
- var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
+
+ var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;");
if (webkit) input.style.width = "1000px";
else input.setAttribute("wrap", "off");
- input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
+ // if border: 0; -- iOS fails to open keyboard (issue #1287)
+ if (ios) input.style.border = "1px solid black";
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
+
// Wraps and hides input textarea
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
// The actual fake scrollbars.
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
// DIVs containing the selection and the actual code
- d.lineDiv = elt("div");
+ d.lineDiv = elt("div", null, "CodeMirror-code");
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
// Blinky cursor, and element used to ensure cursor fits at the end of a line
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
// Secondary cursor, shown when on a 'jump' in bi-directional text
d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
// Used to measure text size
d.measure = elt("div", null, "CodeMirror-measure");
// Wraps everything that needs to exist inside the vertically-padded coordinate system
d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
null, "position: relative; outline: none");
// Moved around its parent to cover visible view
d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative");
// Set to the height of the text, causes scrolling
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
- d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px");
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
// Will contain the gutters, if any
d.gutters = elt("div", null, "CodeMirror-gutters");
d.lineGutter = null;
- // Helper element to properly size the gutter backgrounds
- var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
// Provides scrolling
- d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
d.scroller.setAttribute("tabIndex", "-1");
// The element in which the editor lives.
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
- d.scrollbarFiller, d.scroller], "CodeMirror");
+ d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
// Work around IE7 z-index bug
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
// Needed to hide big blue blinking cursor on Mobile Safari
if (ios) input.style.width = "0px";
if (!webkit) d.scroller.draggable = true;
// Needed to handle Tab key in KHTML
if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; }
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
// Current visible range (may be bigger than the view window).
- d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0;
+ d.viewOffset = d.lastSizeC = 0;
+ d.showingFrom = d.showingTo = docStart;
// Used to only resize the line number gutter when necessary (when
// the amount of lines crosses a boundary that makes its width change)
d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
// See readInput and resetInput
d.prevInput = "";
// Set to true when a non-horizontal-scrolling widget is added. As
// an optimization, widget aligning is skipped when d is false.
d.alignWidgets = false;
// Flag that indicates whether we currently expect input to appear
// (after some event like 'keypress' or 'input') and are polling
// intensively.
d.pollingFast = false;
// Self-resetting timeout for the poller
d.poll = new Delayed();
- // True when a drag from the editor is active
- d.draggingText = false;
d.cachedCharWidth = d.cachedTextHeight = null;
d.measureLineCache = [];
d.measureLineCachePos = 0;
// Tracks when resetInput has punted to just putting a short
// string instead of the (large) selection.
d.inaccurateSelection = false;
- // Used to adjust overwrite behaviour when a paste has been
- // detected
- d.pasteIncoming = false;
+ // Tracks the maximum line length so that the horizontal scrollbar
+ // can be kept static when scrolling.
+ d.maxLine = null;
+ d.maxLineLength = 0;
+ d.maxLineChanged = false;
// Used for measuring wheel scrolling granularity
d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
-
- return d;
- }
-
- // VIEW CONSTRUCTOR
- function makeView(doc) {
- var selPos = {line: 0, ch: 0};
- return {
- doc: doc,
- // frontier is the point up to which the content has been parsed,
- frontier: 0, highlight: new Delayed(),
- sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
- scrollTop: 0, scrollLeft: 0,
- overwrite: false, focused: false,
- // Tracks the maximum line length so that
- // the horizontal scrollbar can be kept
- // static when scrolling.
- maxLine: getLine(doc, 0),
- maxLineLength: 0,
- maxLineChanged: false,
- suppressEdits: false,
- goalColumn: null,
- cantEdit: false,
- keyMaps: [],
- overlays: [],
- modeGen: 0
- };
+ return d;
}
// STATE UPDATES
// Used to get the editor into a consistent state again when options change.
function loadMode(cm) {
- var doc = cm.view.doc;
- cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode);
- doc.iter(0, doc.size, function(line) {
+ cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
+ cm.doc.iter(function(line) {
if (line.stateAfter) line.stateAfter = null;
if (line.styles) line.styles = null;
});
- cm.view.frontier = 0;
+ cm.doc.frontier = cm.doc.first;
startWorker(cm, 100);
- cm.view.modeGen++;
- if (cm.curOp) regChange(cm, 0, doc.size);
+ cm.state.modeGen++;
+ if (cm.curOp) regChange(cm);
}
function wrappingChanged(cm) {
- var doc = cm.view.doc, th = textHeight(cm.display);
if (cm.options.lineWrapping) {
cm.display.wrapper.className += " CodeMirror-wrap";
- var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
- doc.iter(0, doc.size, function(line) {
- if (line.height == 0) return;
- var guess = Math.ceil(line.text.length / perLine) || 1;
- if (guess != 1) updateLineHeight(line, guess * th);
- });
cm.display.sizer.style.minWidth = "";
} else {
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
- computeMaxLength(cm.view);
- doc.iter(0, doc.size, function(line) {
- if (line.height != 0) updateLineHeight(line, th);
- });
+ computeMaxLength(cm);
}
- regChange(cm, 0, doc.size);
+ estimateLineHeights(cm);
+ regChange(cm);
clearCaches(cm);
- setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100);
+ setTimeout(function(){updateScrollbars(cm);}, 100);
+ }
+
+ function estimateHeight(cm) {
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
+ return function(line) {
+ if (lineIsHidden(cm.doc, line))
+ return 0;
+ else if (wrapping)
+ return (Math.ceil(line.text.length / perLine) || 1) * th;
+ else
+ return th;
+ };
+ }
+
+ function estimateLineHeights(cm) {
+ var doc = cm.doc, est = estimateHeight(cm);
+ doc.iter(function(line) {
+ var estHeight = est(line);
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
+ });
}
function keyMapChanged(cm) {
- var style = keyMap[cm.options.keyMap].style;
+ var map = keyMap[cm.options.keyMap], style = map.style;
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
(style ? " cm-keymap-" + style : "");
+ cm.state.disableInput = map.disableInput;
}
function themeChanged(cm) {
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
clearCaches(cm);
}
function guttersChanged(cm) {
updateGutters(cm);
- updateDisplay(cm, true);
+ regChange(cm);
+ setTimeout(function(){alignHorizontally(cm);}, 20);
}
function updateGutters(cm) {
var gutters = cm.display.gutters, specs = cm.options.gutters;
removeChildren(gutters);
for (var i = 0; i < specs.length; ++i) {
var gutterClass = specs[i];
var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass));
if (gutterClass == "CodeMirror-linenumbers") {
cm.display.lineGutter = gElt;
gElt.style.width = (cm.display.lineNumWidth || 1) + "px";
}
}
gutters.style.display = i ? "" : "none";
}
function lineLength(doc, line) {
if (line.height == 0) return 0;
var len = line.text.length, merged, cur = line;
while (merged = collapsedSpanAtStart(cur)) {
var found = merged.find();
cur = getLine(doc, found.from.line);
len += found.from.ch - found.to.ch;
}
cur = line;
while (merged = collapsedSpanAtEnd(cur)) {
var found = merged.find();
len -= cur.text.length - found.from.ch;
cur = getLine(doc, found.to.line);
len += cur.text.length - found.to.ch;
}
return len;
}
- function computeMaxLength(view) {
- view.maxLine = getLine(view.doc, 0);
- view.maxLineLength = lineLength(view.doc, view.maxLine);
- view.maxLineChanged = true;
- view.doc.iter(1, view.doc.size, function(line) {
- var len = lineLength(view.doc, line);
- if (len > view.maxLineLength) {
- view.maxLineLength = len;
- view.maxLine = line;
+ function computeMaxLength(cm) {
+ var d = cm.display, doc = cm.doc;
+ d.maxLine = getLine(doc, doc.first);
+ d.maxLineLength = lineLength(doc, d.maxLine);
+ d.maxLineChanged = true;
+ doc.iter(function(line) {
+ var len = lineLength(doc, line);
+ if (len > d.maxLineLength) {
+ d.maxLineLength = len;
+ d.maxLine = line;
}
});
}
// Make sure the gutters options contains the element
// "CodeMirror-linenumbers" when the lineNumbers option is true.
function setGuttersForLineNumbers(options) {
var found = false;
for (var i = 0; i < options.gutters.length; ++i) {
if (options.gutters[i] == "CodeMirror-linenumbers") {
if (options.lineNumbers) found = true;
else options.gutters.splice(i--, 1);
}
}
if (!found && options.lineNumbers)
options.gutters.push("CodeMirror-linenumbers");
}
// SCROLLBARS
// Re-synchronize the fake scrollbars with the actual size of the
// content. Optionally force a scrollTop.
- function updateScrollbars(d /* display */, docHeight) {
- var totalHeight = docHeight + 2 * paddingTop(d);
+ function updateScrollbars(cm) {
+ var d = cm.display, docHeight = cm.doc.height;
+ var totalHeight = docHeight + paddingVert(d);
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
+ d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
- var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
- var needsV = scrollHeight > d.scroller.clientHeight;
+ var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1);
+ var needsV = scrollHeight > (d.scroller.clientHeight + 1);
if (needsV) {
d.scrollbarV.style.display = "block";
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
- d.scrollbarV.firstChild.style.height =
+ d.scrollbarV.firstChild.style.height =
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
} else d.scrollbarV.style.display = "";
if (needsH) {
d.scrollbarH.style.display = "block";
d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0";
d.scrollbarH.firstChild.style.width =
(d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px";
} else d.scrollbarH.style.display = "";
if (needsH && needsV) {
d.scrollbarFiller.style.display = "block";
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
} else d.scrollbarFiller.style.display = "";
+ if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+ d.gutterFiller.style.display = "block";
+ d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
+ d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
+ } else d.gutterFiller.style.display = "";
if (mac_geLion && scrollbarWidth(d.measure) === 0)
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
}
function visibleLines(display, doc, viewPort) {
var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
if (typeof viewPort == "number") top = viewPort;
else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
top = Math.floor(top - paddingTop(display));
var bottom = Math.ceil(top + height);
return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
}
// LINE NUMBERS
function alignHorizontally(cm) {
var display = cm.display;
if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
- var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft;
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
var gutterW = display.gutters.offsetWidth, l = comp + "px";
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
}
if (cm.options.fixedGutter)
display.gutters.style.left = (comp + gutterW) + "px";
}
function maybeUpdateLineNumberWidth(cm) {
if (!cm.options.lineNumbers) return false;
- var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display;
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
if (last.length != display.lineNumChars) {
var test = display.measure.appendChild(elt("div", [elt("div", last)],
"CodeMirror-linenumber CodeMirror-gutter-elt"));
var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
display.lineGutter.style.width = "";
display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding);
display.lineNumWidth = display.lineNumInnerWidth + padding;
display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
display.lineGutter.style.width = display.lineNumWidth + "px";
return true;
}
return false;
}
function lineNumberFor(options, i) {
return String(options.lineNumberFormatter(i + options.firstLineNumber));
}
function compensateForHScroll(display) {
- return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
+ return getRect(display.scroller).left - getRect(display.sizer).left;
}
// DISPLAY DRAWING
function updateDisplay(cm, changes, viewPort) {
- var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
- var updated = updateDisplayInner(cm, changes, viewPort);
+ var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
+ var visible = visibleLines(cm.display, cm.doc, viewPort);
+ for (;;) {
+ if (!updateDisplayInner(cm, changes, visible)) break;
+ updated = true;
+ updateSelection(cm);
+ updateScrollbars(cm);
+
+ // Clip forced viewport to actual scrollable area
+ if (viewPort)
+ viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
+ typeof viewPort == "number" ? viewPort : viewPort.top);
+ visible = visibleLines(cm.display, cm.doc, viewPort);
+ if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo)
+ break;
+ changes = [];
+ }
+
if (updated) {
- signalLater(cm, cm, "update", cm);
+ signalLater(cm, "update", cm);
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
- signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
+ signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
}
- updateSelection(cm);
- updateScrollbars(cm.display, cm.view.doc.height);
-
return updated;
}
// Uses a set of changes plus the current scroll position to
// determine which DOM updates have to be made, and makes the
// updates.
- function updateDisplayInner(cm, changes, viewPort) {
- var display = cm.display, doc = cm.view.doc;
+ function updateDisplayInner(cm, changes, visible) {
+ var display = cm.display, doc = cm.doc;
if (!display.wrapper.clientWidth) {
- display.showingFrom = display.showingTo = display.viewOffset = 0;
+ display.showingFrom = display.showingTo = doc.first;
+ display.viewOffset = 0;
return;
}
- // Compute the new visible window
- // If scrollTop is specified, use that to determine which lines
- // to render instead of the current scrollbar position.
- var visible = visibleLines(display, doc, viewPort);
// Bail out if the visible area is already rendered and nothing changed.
- if (changes !== true && changes.length == 0 &&
+ if (changes.length == 0 &&
visible.from > display.showingFrom && visible.to < display.showingTo)
return;
- if (changes && maybeUpdateLineNumberWidth(cm))
- changes = true;
+ if (maybeUpdateLineNumberWidth(cm))
+ changes = [{from: doc.first, to: doc.first + doc.size}];
var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
- // When merged lines are present, the line that needs to be
- // redrawn might not be the one that was changed.
- if (changes !== true && sawCollapsedSpans)
- for (var i = 0; i < changes.length; ++i) {
- var ch = changes[i], merged;
- while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
- var from = merged.find().from.line;
- if (ch.diff) ch.diff -= ch.from - from;
- ch.from = from;
- }
- }
-
// Used to determine which lines need their line numbers updated
- var positionsChangedFrom = changes === true ? 0 : Infinity;
- if (cm.options.lineNumbers && changes && changes !== true)
+ var positionsChangedFrom = Infinity;
+ if (cm.options.lineNumbers)
for (var i = 0; i < changes.length; ++i)
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
- var from = Math.max(visible.from - cm.options.viewportMargin, 0);
- var to = Math.min(doc.size, visible.to + cm.options.viewportMargin);
- if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom;
- if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo);
+ var end = doc.first + doc.size;
+ var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
+ var to = Math.min(end, visible.to + cm.options.viewportMargin);
+ if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
+ if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
if (sawCollapsedSpans) {
from = lineNo(visualLine(doc, getLine(doc, from)));
- while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to;
+ while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
}
// Create a range of theoretically intact lines, and punch holes
// in that using the change info.
- var intact = changes === true ? [] :
- computeIntact([{from: display.showingFrom, to: display.showingTo}], changes);
+ var intact = [{from: Math.max(display.showingFrom, doc.first),
+ to: Math.min(display.showingTo, end)}];
+ if (intact[0].from >= intact[0].to) intact = [];
+ else intact = computeIntact(intact, changes);
+ // When merged lines are present, we might have to reduce the
+ // intact ranges because changes in continued fragments of the
+ // intact lines do require the lines to be redrawn.
+ if (sawCollapsedSpans)
+ for (var i = 0; i < intact.length; ++i) {
+ var range = intact[i], merged;
+ while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
+ var newTo = merged.find().from.line;
+ if (newTo > range.from) range.to = newTo;
+ else { intact.splice(i--, 1); break; }
+ }
+ }
+
// Clip off the parts that won't be visible
var intactLines = 0;
for (var i = 0; i < intact.length; ++i) {
var range = intact[i];
if (range.from < from) range.from = from;
if (range.to > to) range.to = to;
if (range.from >= range.to) intact.splice(i--, 1);
else intactLines += range.to - range.from;
}
- if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
+ if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
+ updateViewOffset(cm);
return;
+ }
intact.sort(function(a, b) {return a.from - b.from;});
- var focused = document.activeElement;
+ // Avoid crashing on IE's "unspecified error" when in iframes
+ try {
+ var focused = document.activeElement;
+ } catch(e) {}
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
patchDisplay(cm, from, to, intact, positionsChangedFrom);
display.lineDiv.style.display = "";
- if (document.activeElement != focused && focused.offsetHeight) focused.focus();
+ if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus();
var different = from != display.showingFrom || to != display.showingTo ||
display.lastSizeC != display.wrapper.clientHeight;
// This is just a bogus formula that detects when the editor is
// resized or the font size changes.
- if (different) display.lastSizeC = display.wrapper.clientHeight;
+ if (different) {
+ display.lastSizeC = display.wrapper.clientHeight;
+ startWorker(cm, 400);
+ }
display.showingFrom = from; display.showingTo = to;
- startWorker(cm, 100);
var prevBottom = display.lineDiv.offsetTop;
for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
if (ie_lt8) {
var bot = node.offsetTop + node.offsetHeight;
height = bot - prevBottom;
prevBottom = bot;
} else {
- var box = node.getBoundingClientRect();
+ var box = getRect(node);
height = box.bottom - box.top;
}
var diff = node.lineObj.height - height;
if (height < 2) height = textHeight(display);
if (diff > .001 || diff < -.001) {
updateLineHeight(node.lineObj, height);
var widgets = node.lineObj.widgets;
if (widgets) for (var i = 0; i < widgets.length; ++i)
widgets[i].height = widgets[i].node.offsetHeight;
}
}
- display.viewOffset = heightAtLine(cm, getLine(doc, from));
- // Position the mover div to align with the current virtual scroll position
- display.mover.style.top = display.viewOffset + "px";
+ updateViewOffset(cm);
- if (visibleLines(display, doc, viewPort).to >= to)
- updateDisplayInner(cm, [], viewPort);
return true;
}
+ function updateViewOffset(cm) {
+ var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
+ // Position the mover div to align with the current virtual scroll position
+ cm.display.mover.style.top = off + "px";
+ }
+
function computeIntact(intact, changes) {
for (var i = 0, l = changes.length || 0; i < l; ++i) {
var change = changes[i], intact2 = [], diff = change.diff || 0;
for (var j = 0, l2 = intact.length; j < l2; ++j) {
var range = intact[j];
if (change.to <= range.from && change.diff) {
intact2.push({from: range.from + diff, to: range.to + diff});
} else if (change.to <= range.from || change.from >= range.to) {
intact2.push(range);
} else {
if (change.from > range.from)
intact2.push({from: range.from, to: change.from});
if (change.to < range.to)
intact2.push({from: change.to + diff, to: range.to + diff});
}
}
intact = intact2;
}
return intact;
}
function getDimensions(cm) {
var d = cm.display, left = {}, width = {};
for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
left[cm.options.gutters[i]] = n.offsetLeft;
width[cm.options.gutters[i]] = n.offsetWidth;
}
return {fixedPos: compensateForHScroll(d),
gutterTotalWidth: d.gutters.offsetWidth,
gutterLeft: left,
gutterWidth: width,
wrapperWidth: d.wrapper.clientWidth};
}
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
var dims = getDimensions(cm);
var display = cm.display, lineNumbers = cm.options.lineNumbers;
if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
removeChildren(display.lineDiv);
var container = display.lineDiv, cur = container.firstChild;
function rm(node) {
var next = node.nextSibling;
if (webkit && mac && cm.display.currentWheelTarget == node) {
node.style.display = "none";
node.lineObj = null;
} else {
node.parentNode.removeChild(node);
}
return next;
}
- var nextIntact = intact.shift(), lineNo = from;
- cm.view.doc.iter(from, to, function(line) {
- if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift();
- if (lineIsHidden(line)) {
+ var nextIntact = intact.shift(), lineN = from;
+ cm.doc.iter(from, to, function(line) {
+ if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
+ if (lineIsHidden(cm.doc, line)) {
if (line.height != 0) updateLineHeight(line, 0);
- if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
- if (line.widgets[i].showIfHidden) {
+ if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) {
+ var w = line.widgets[i];
+ if (w.showIfHidden) {
var prev = cur.previousSibling;
- if (prev.nodeType == "pre") {
+ if (/pre/i.test(prev.nodeName)) {
var wrap = elt("div", null, null, "position: relative");
prev.parentNode.replaceChild(wrap, prev);
wrap.appendChild(prev);
prev = wrap;
}
- prev.appendChild(buildLineWidget(line.widgets[i], prev, dims));
+ var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget"));
+ if (!w.handleMouseEvents) wnode.ignoreEvents = true;
+ positionLineWidget(w, wnode, prev, dims);
}
- } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) {
+ }
+ } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
// This line is intact. Skip to the actual node. Update its
// line number if needed.
while (cur.lineObj != line) cur = rm(cur);
- if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber)
- setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo));
+ if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
+ setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
cur = cur.nextSibling;
} else {
+ // For lines with widgets, make an attempt to find and reuse
+ // the existing element, so that widgets aren't needlessly
+ // removed and re-inserted into the dom
+ if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
+ if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
// This line needs to be generated.
- var lineNode = buildLineElement(cm, line, lineNo, dims);
- container.insertBefore(lineNode, cur);
+ var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
+ if (lineNode != reuse) {
+ container.insertBefore(lineNode, cur);
+ } else {
+ while (cur != reuse) cur = rm(cur);
+ cur = cur.nextSibling;
+ }
+
lineNode.lineObj = line;
}
- ++lineNo;
+ ++lineN;
});
while (cur) cur = rm(cur);
}
- function buildLineElement(cm, line, lineNo, dims) {
+ function buildLineElement(cm, line, lineNo, dims, reuse) {
var lineElement = lineContent(cm, line);
- var markers = line.gutterMarkers, display = cm.display;
-
- if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
- (!line.widgets || !line.widgets.length)) return lineElement;
+ var markers = line.gutterMarkers, display = cm.display, wrap;
- // Lines with gutter elements or a background class need
- // to be wrapped again, and have the extra elements added
- // to the wrapper div
+ if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
+ return lineElement;
- var wrap = elt("div", null, line.wrapClass, "position: relative");
+ // Lines with gutter elements, widgets or a background class need
+ // to be wrapped again, and have the extra elements added to the
+ // wrapper div
+
+ if (reuse) {
+ reuse.alignable = null;
+ var isOk = true, widgetsSeen = 0, insertBefore = null;
+ for (var n = reuse.firstChild, next; n; n = next) {
+ next = n.nextSibling;
+ if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
+ reuse.removeChild(n);
+ } else {
+ for (var i = 0, first = true; i < line.widgets.length; ++i) {
+ var widget = line.widgets[i];
+ if (!widget.above) { insertBefore = n; first = false; }
+ if (widget.node == n.firstChild) {
+ positionLineWidget(widget, n, reuse, dims);
+ ++widgetsSeen;
+ break;
+ }
+ }
+ if (i == line.widgets.length) { isOk = false; break; }
+ }
+ }
+ reuse.insertBefore(lineElement, insertBefore);
+ if (isOk && widgetsSeen == line.widgets.length) {
+ wrap = reuse;
+ reuse.className = line.wrapClass || "";
+ }
+ }
+ if (!wrap) {
+ wrap = elt("div", null, line.wrapClass, "position: relative");
+ wrap.appendChild(lineElement);
+ }
+ // Kludge to make sure the styled element lies behind the selection (by z-index)
+ if (line.bgClass)
+ wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
if (cm.options.lineNumbers || markers) {
- var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " +
- (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
- if (cm.options.fixedGutter) wrap.alignable = [gutterWrap];
+ var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
+ (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
+ wrap.firstChild);
+ if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
wrap.lineNumber = gutterWrap.appendChild(
elt("div", lineNumberFor(cm.options, lineNo),
"CodeMirror-linenumber CodeMirror-gutter-elt",
"left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: "
+ display.lineNumInnerWidth + "px"));
if (markers)
for (var k = 0; k < cm.options.gutters.length; ++k) {
var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id];
if (found)
gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " +
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
}
}
- // Kludge to make sure the styled element lies behind the selection (by z-index)
- if (line.bgClass)
- wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
- wrap.appendChild(lineElement);
- if (line.widgets) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
- var widget = ws[i], node = buildLineWidget(widget, wrap, dims);
+ if (ie_lt8) wrap.style.zIndex = 2;
+ if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
+ if (!widget.handleMouseEvents) node.ignoreEvents = true;
+ positionLineWidget(widget, node, wrap, dims);
if (widget.above)
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
else
wrap.appendChild(node);
+ signalLater(widget, "redraw");
}
- if (ie_lt8) wrap.style.zIndex = 2;
return wrap;
}
- function buildLineWidget(widget, wrap, dims) {
- var node = elt("div", [widget.node], "CodeMirror-linewidget");
- node.widget = widget;
+ function positionLineWidget(widget, node, wrap, dims) {
if (widget.noHScroll) {
(wrap.alignable || (wrap.alignable = [])).push(node);
var width = dims.wrapperWidth;
node.style.left = dims.fixedPos + "px";
if (!widget.coverGutter) {
width -= dims.gutterTotalWidth;
node.style.paddingLeft = dims.gutterTotalWidth + "px";
}
node.style.width = width + "px";
}
if (widget.coverGutter) {
node.style.zIndex = 5;
node.style.position = "relative";
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
}
- return node;
}
// SELECTION / CURSOR
function updateSelection(cm) {
var display = cm.display;
- var collapsed = posEq(cm.view.sel.from, cm.view.sel.to);
+ var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
if (collapsed || cm.options.showCursorWhenSelecting)
updateSelectionCursor(cm);
else
display.cursor.style.display = display.otherCursor.style.display = "none";
if (!collapsed)
updateSelectionRange(cm);
else
display.selectionDiv.style.display = "none";
// Move the hidden textarea near the cursor to prevent scrolling artifacts
- var headPos = cursorCoords(cm, cm.view.sel.head, "div");
- var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
- display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
- headPos.top + lineOff.top - wrapOff.top)) + "px";
- display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
- headPos.left + lineOff.left - wrapOff.left)) + "px";
+ if (cm.options.moveInputWithCursor) {
+ var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
+ var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
+ display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+ headPos.top + lineOff.top - wrapOff.top)) + "px";
+ display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left)) + "px";
+ }
}
// No selection, plain cursor
function updateSelectionCursor(cm) {
- var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div");
+ var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
display.cursor.style.left = pos.left + "px";
display.cursor.style.top = pos.top + "px";
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
display.cursor.style.display = "";
if (pos.other) {
display.otherCursor.style.display = "";
display.otherCursor.style.left = pos.other.left + "px";
display.otherCursor.style.top = pos.other.top + "px";
display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
} else { display.otherCursor.style.display = "none"; }
}
// Highlight selection
function updateSelectionRange(cm) {
- var display = cm.display, doc = cm.view.doc, sel = cm.view.sel;
+ var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
var fragment = document.createDocumentFragment();
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
function add(left, top, width, bottom) {
if (top < 0) top = 0;
fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left +
"px; top: " + top + "px; width: " + (width == null ? clientWidth - left : width) +
"px; height: " + (bottom - top) + "px"));
}
- function drawForLine(line, fromArg, toArg, retTop) {
+ function drawForLine(line, fromArg, toArg) {
var lineObj = getLine(doc, line);
- var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
- function coords(ch) {
- return charCoords(cm, {line: line, ch: ch}, "div", lineObj);
+ var lineLen = lineObj.text.length;
+ var start, end;
+ function coords(ch, bias) {
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias);
}
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
- var leftPos = coords(dir == "rtl" ? to - 1 : from);
- var rightPos = coords(dir == "rtl" ? from : to - 1);
- var left = leftPos.left, right = rightPos.right;
+ var leftPos = coords(from, "left"), rightPos, left, right;
+ if (from == to) {
+ rightPos = leftPos;
+ left = right = leftPos.left;
+ } else {
+ rightPos = coords(to - 1, "right");
+ if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; }
+ left = leftPos.left;
+ right = rightPos.right;
+ }
+ if (fromArg == null && from == 0) left = pl;
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
add(left, leftPos.top, null, leftPos.bottom);
left = pl;
if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
}
if (toArg == null && to == lineLen) right = clientWidth;
- if (fromArg == null && from == 0) left = pl;
- rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal);
+ if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
+ start = leftPos;
+ if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
+ end = rightPos;
if (left < pl + 1) left = pl;
add(left, rightPos.top, right - left, rightPos.bottom);
});
- return rVal;
+ return {start: start, end: end};
}
if (sel.from.line == sel.to.line) {
drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
} else {
- var fromObj = getLine(doc, sel.from.line);
- var cur = fromObj, merged, path = [sel.from.line, sel.from.ch], singleLine;
- while (merged = collapsedSpanAtEnd(cur)) {
- var found = merged.find();
- path.push(found.from.ch, found.to.line, found.to.ch);
- if (found.to.line == sel.to.line) {
- path.push(sel.to.ch);
- singleLine = true;
- break;
+ var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
+ var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
+ var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
+ var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
+ if (singleVLine) {
+ if (leftEnd.top < rightStart.top - 2) {
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
+ add(pl, rightStart.top, rightStart.left, rightStart.bottom);
+ } else {
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
}
- cur = getLine(doc, found.to.line);
- }
-
- // This is a single, merged line
- if (singleLine) {
- for (var i = 0; i < path.length; i += 3)
- drawForLine(path[i], path[i+1], path[i+2]);
- } else {
- var middleTop, middleBot, toObj = getLine(doc, sel.to.line);
- if (sel.from.ch)
- // Draw the first line of selection.
- middleTop = drawForLine(sel.from.line, sel.from.ch, null, false);
- else
- // Simply include it in the middle block.
- middleTop = heightAtLine(cm, fromObj) - display.viewOffset;
-
- if (!sel.to.ch)
- middleBot = heightAtLine(cm, toObj) - display.viewOffset;
- else
- middleBot = drawForLine(sel.to.line, collapsedSpanAtStart(toObj) ? null : 0, sel.to.ch, true);
-
- if (middleTop < middleBot) add(pl, middleTop, null, middleBot);
}
+ if (leftEnd.bottom < rightStart.top)
+ add(pl, leftEnd.bottom, null, rightStart.top);
}
removeChildrenAndAdd(display.selectionDiv, fragment);
display.selectionDiv.style.display = "";
}
// Cursor-blinking
function restartBlink(cm) {
+ if (!cm.state.focused) return;
var display = cm.display;
clearInterval(display.blinker);
var on = true;
display.cursor.style.visibility = display.otherCursor.style.visibility = "";
display.blinker = setInterval(function() {
- if (!display.cursor.offsetHeight) return;
display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden";
}, cm.options.cursorBlinkRate);
}
// HIGHLIGHT WORKER
function startWorker(cm, time) {
- if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo)
- cm.view.highlight.set(time, bind(highlightWorker, cm));
+ if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
+ cm.state.highlight.set(time, bind(highlightWorker, cm));
}
function highlightWorker(cm) {
- var view = cm.view, doc = view.doc;
- if (view.frontier >= cm.display.showingTo) return;
+ var doc = cm.doc;
+ if (doc.frontier < doc.first) doc.frontier = doc.first;
+ if (doc.frontier >= cm.display.showingTo) return;
var end = +new Date + cm.options.workTime;
- var state = copyState(view.mode, getStateBefore(cm, view.frontier));
+ var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
var changed = [], prevChange;
- doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) {
- if (view.frontier >= cm.display.showingFrom) { // Visible
+ doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
+ if (doc.frontier >= cm.display.showingFrom) { // Visible
var oldStyles = line.styles;
line.styles = highlightLine(cm, line, state);
var ischange = !oldStyles || oldStyles.length != line.styles.length;
- for (var i = 0; !ischange && i < oldStyles.length; ++i)
- ischange = oldStyles[i] != line.styles[i];
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
if (ischange) {
- if (prevChange && prevChange.end == view.frontier) prevChange.end++;
- else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1});
+ if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
+ else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
}
- line.stateAfter = copyState(view.mode, state);
+ line.stateAfter = copyState(doc.mode, state);
} else {
processLine(cm, line, state);
- line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null;
+ line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
}
- ++view.frontier;
+ ++doc.frontier;
if (+new Date > end) {
startWorker(cm, cm.options.workDelay);
return true;
}
});
if (changed.length)
operation(cm, function() {
for (var i = 0; i < changed.length; ++i)
regChange(this, changed[i].start, changed[i].end);
})();
}
// Finds the line to start with when starting a parse. Tries to
// find a line with a stateAfter, so that it can start with a
// valid state. If that fails, it returns the line with the
// smallest indentation, which tends to need the least context to
// parse correctly.
- function findStartLine(cm, n) {
- var minindent, minline, doc = cm.view.doc;
+ function findStartLine(cm, n, precise) {
+ var minindent, minline, doc = cm.doc;
for (var search = n, lim = n - 100; search > lim; --search) {
- if (search == 0) return 0;
- var line = getLine(doc, search-1);
- if (line.stateAfter) return search;
+ if (search <= doc.first) return doc.first;
+ var line = getLine(doc, search - 1);
+ if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
var indented = countColumn(line.text, null, cm.options.tabSize);
if (minline == null || minindent > indented) {
minline = search - 1;
minindent = indented;
}
}
return minline;
}
- function getStateBefore(cm, n) {
- var view = cm.view;
- if (!view.mode.startState) return true;
- var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter;
- if (!state) state = startState(view.mode);
- else state = copyState(view.mode, state);
- view.doc.iter(pos, n, function(line) {
+ function getStateBefore(cm, n, precise) {
+ var doc = cm.doc, display = cm.display;
+ if (!doc.mode.startState) return true;
+ var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
+ if (!state) state = startState(doc.mode);
+ else state = copyState(doc.mode, state);
+ doc.iter(pos, n, function(line) {
processLine(cm, line, state);
- var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo;
- line.stateAfter = save ? copyState(view.mode, state) : null;
+ var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
+ line.stateAfter = save ? copyState(doc.mode, state) : null;
++pos;
});
return state;
}
// POSITION MEASUREMENT
-
+
function paddingTop(display) {return display.lineSpace.offsetTop;}
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
function paddingLeft(display) {
- var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
+ var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
return e.offsetLeft;
}
- function measureChar(cm, line, ch, data) {
+ function measureChar(cm, line, ch, data, bias) {
var dir = -1;
data = data || measureLine(cm, line);
-
+
for (var pos = ch;; pos += dir) {
var r = data[pos];
if (r) break;
if (dir < 0 && pos == 0) dir = 1;
}
+ var rightV = (pos < ch || bias == "right") && r.topRight != null;
return {left: pos < ch ? r.right : r.left,
right: pos > ch ? r.left : r.right,
- top: r.top, bottom: r.bottom};
+ top: rightV ? r.topRight : r.top,
+ bottom: rightV ? r.bottomRight : r.bottom};
}
- function measureLine(cm, line) {
- // First look in the cache
- var display = cm.display, cache = cm.display.measureLineCache;
+ function findCachedMeasurement(cm, line) {
+ var cache = cm.display.measureLineCache;
for (var i = 0; i < cache.length; ++i) {
var memo = cache[i];
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
- display.scroller.clientWidth == memo.width)
- return memo.measure;
+ cm.display.scroller.clientWidth == memo.width &&
+ memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
+ return memo;
}
-
+ }
+
+ function clearCachedMeasurement(cm, line) {
+ var exists = findCachedMeasurement(cm, line);
+ if (exists) exists.text = exists.measure = exists.markedSpans = null;
+ }
+
+ function measureLine(cm, line) {
+ // First look in the cache
+ var cached = findCachedMeasurement(cm, line);
+ if (cached) return cached.measure;
+
+ // Failing that, recompute and store result in cache
var measure = measureLineInner(cm, line);
- // Store result in the cache
- var memo = {text: line.text, width: display.scroller.clientWidth,
- markedSpans: line.markedSpans, measure: measure};
- if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo;
+ var cache = cm.display.measureLineCache;
+ var memo = {text: line.text, width: cm.display.scroller.clientWidth,
+ markedSpans: line.markedSpans, measure: measure,
+ classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
+ if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
else cache.push(memo);
return measure;
}
function measureLineInner(cm, line) {
var display = cm.display, measure = emptyArray(line.text.length);
var pre = lineContent(cm, line, measure);
// IE does not cache element positions of inline elements between
// calls to getBoundingClientRect. This makes the loop below,
// which gathers the positions of all the characters on the line,
// do an amount of layout work quadratic to the number of
// characters. When line wrapping is off, we try to improve things
// by first subdividing the line into a bunch of inline blocks, so
// that IE can reuse most of the layout information from caches
// for those blocks. This does interfere with line wrapping, so it
// doesn't work when wrapping is on, but in that case the
// situation is slightly better, since IE does cache line-wrapping
// information and only recomputes per-line.
if (ie && !ie_lt8 && !cm.options.lineWrapping && pre.childNodes.length > 100) {
var fragment = document.createDocumentFragment();
var chunk = 10, n = pre.childNodes.length;
for (var i = 0, chunks = Math.ceil(n / chunk); i < chunks; ++i) {
var wrap = elt("div", null, null, "display: inline-block");
for (var j = 0; j < chunk && n; ++j) {
wrap.appendChild(pre.firstChild);
--n;
}
fragment.appendChild(wrap);
}
pre.appendChild(fragment);
}
removeChildrenAndAdd(display.measure, pre);
- var outer = display.lineDiv.getBoundingClientRect();
+ var outer = getRect(display.lineDiv);
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
- for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
- var size = cur.getBoundingClientRect();
- var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
+ // Work around an IE7/8 bug where it will sometimes have randomly
+ // replaced our pre with a clone at this point.
+ if (ie_lt9 && display.measure.first != pre)
+ removeChildrenAndAdd(display.measure, pre);
+
+ function categorizeVSpan(top, bot) {
+ if (bot > maxBot) bot = maxBot;
+ if (top < 0) top = 0;
for (var j = 0; j < vranges.length; j += 2) {
var rtop = vranges[j], rbot = vranges[j+1];
if (rtop > bot || rbot < top) continue;
if (rtop <= top && rbot >= bot ||
top <= rtop && bot >= rbot ||
Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) {
vranges[j] = Math.min(top, rtop);
vranges[j+1] = Math.max(bot, rbot);
- break;
+ return j;
}
}
- if (j == vranges.length) vranges.push(top, bot);
- data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j};
+ vranges.push(top, bot);
+ return j;
+ }
+
+ for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
+ var size, node = cur;
+ // A widget might wrap, needs special care
+ if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) {
+ if (cur.firstChild.nodeType == 1) node = cur.firstChild;
+ var rects = node.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1];
+ if (rects.length > 1) {
+ var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top);
+ var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top);
+ data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left,
+ top: vCatLeft, topRight: vCatRight};
+ continue;
+ }
+ }
+ size = getRect(node);
+ var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top);
+ var right = size.right;
+ if (cur.measureRight) right = getRect(cur.measureRight).left;
+ data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat};
}
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
- var vr = cur.top;
+ var vr = cur.top, vrRight = cur.topRight;
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
+ if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; }
}
return data;
}
+ function measureLineWidth(cm, line) {
+ var hasBadSpan = false;
+ if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
+ var sp = line.markedSpans[i];
+ if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
+ }
+ var cached = !hasBadSpan && findCachedMeasurement(cm, line);
+ if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right;
+
+ var pre = lineContent(cm, line);
+ var end = pre.appendChild(zeroWidthElement(cm.display.measure));
+ removeChildrenAndAdd(cm.display.measure, pre);
+ return getRect(end).right - getRect(cm.display.lineDiv).left;
+ }
+
function clearCaches(cm) {
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
- cm.view.maxLineChanged = true;
+ if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
+ cm.display.lineNumChars = null;
}
+ function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
+ function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
+
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
function intoCoordSystem(cm, lineObj, rect, context) {
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
var size = widgetHeight(lineObj.widgets[i]);
rect.top += size; rect.bottom += size;
}
if (context == "line") return rect;
if (!context) context = "local";
var yOff = heightAtLine(cm, lineObj);
- if (context != "local") yOff -= cm.display.viewOffset;
- if (context == "page") {
- var lOff = cm.display.lineSpace.getBoundingClientRect();
- yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
- var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
+ if (context == "local") yOff += paddingTop(cm.display);
+ else yOff -= cm.display.viewOffset;
+ if (context == "page" || context == "window") {
+ var lOff = getRect(cm.display.lineSpace);
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
rect.left += xOff; rect.right += xOff;
}
rect.top += yOff; rect.bottom += yOff;
return rect;
}
- function charCoords(cm, pos, context, lineObj) {
- if (!lineObj) lineObj = getLine(cm.view.doc, pos.line);
- return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
+ // Context may be "window", "page", "div", or "local"/null
+ // Result is in "div" coords
+ function fromCoordSystem(cm, coords, context) {
+ if (context == "div") return coords;
+ var left = coords.left, top = coords.top;
+ // First move into "page" coordinate system
+ if (context == "page") {
+ left -= pageScrollX();
+ top -= pageScrollY();
+ } else if (context == "local" || !context) {
+ var localBox = getRect(cm.display.sizer);
+ left += localBox.left;
+ top += localBox.top;
+ }
+
+ var lineSpaceBox = getRect(cm.display.lineSpace);
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
+ }
+
+ function charCoords(cm, pos, context, lineObj, bias) {
+ if (!lineObj) lineObj = getLine(cm.doc, pos.line);
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
}
function cursorCoords(cm, pos, context, lineObj, measurement) {
- lineObj = lineObj || getLine(cm.view.doc, pos.line);
+ lineObj = lineObj || getLine(cm.doc, pos.line);
if (!measurement) measurement = measureLine(cm, lineObj);
function get(ch, right) {
- var m = measureChar(cm, lineObj, ch, measurement);
+ var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left");
if (right) m.left = m.right; else m.right = m.left;
return intoCoordSystem(cm, lineObj, m, context);
}
+ function getBidi(ch, partPos) {
+ var part = order[partPos], right = part.level % 2;
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
+ part = order[--partPos];
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
+ right = true;
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
+ part = order[++partPos];
+ ch = bidiLeft(part) - part.level % 2;
+ right = false;
+ }
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
+ return get(ch, right);
+ }
var order = getOrder(lineObj), ch = pos.ch;
if (!order) return get(ch);
- var main, other, linedir = order[0].level;
- for (var i = 0; i < order.length; ++i) {
- var part = order[i], rtl = part.level % 2, nb, here;
- if (part.from < ch && part.to > ch) return get(ch, rtl);
- var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
- if (left == ch) {
- // Opera and IE return bogus offsets and widths for edges
- // where the direction flips, but only for the side with the
- // lower level. So we try to use the side with the higher
- // level.
- if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
- else here = get(rtl && part.from != part.to ? ch - 1 : ch);
- if (rtl == linedir) main = here; else other = here;
- } else if (right == ch) {
- var nb = i < order.length - 1 && order[i+1];
- if (!rtl && nb && nb.from == nb.to) continue;
- if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
- else here = get(rtl ? ch : ch - 1, true);
- if (rtl == linedir) main = here; else other = here;
- }
- }
- if (linedir && !ch) other = get(order[0].to - 1);
- if (!main) return other;
- if (other) main.other = other;
- return main;
+ var partPos = getBidiPartAt(order, ch);
+ var val = getBidi(ch, partPos);
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
+ return val;
+ }
+
+ function PosWithInfo(line, ch, outside, xRel) {
+ var pos = new Pos(line, ch);
+ pos.xRel = xRel;
+ if (outside) pos.outside = true;
+ return pos;
}
// Coords must be lineSpace-local
function coordsChar(cm, x, y) {
- var doc = cm.view.doc;
+ var doc = cm.doc;
y += cm.display.viewOffset;
- if (y < 0) return {line: 0, ch: 0, outside: true};
- var lineNo = lineAtHeight(doc, y);
- if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length};
+ if (y < 0) return PosWithInfo(doc.first, 0, true, -1);
+ var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+ if (lineNo > last)
+ return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
if (x < 0) x = 0;
for (;;) {
var lineObj = getLine(doc, lineNo);
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
var merged = collapsedSpanAtEnd(lineObj);
var mergedPos = merged && merged.find();
- if (merged && found.ch >= mergedPos.from.ch)
+ if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0))
lineNo = mergedPos.to.line;
else
return found;
}
}
function coordsCharInner(cm, lineObj, lineNo, x, y) {
var innerOff = y - heightAtLine(cm, lineObj);
- var wrongLine = false, cWidth = cm.display.wrapper.clientWidth;
+ var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
var measurement = measureLine(cm, lineObj);
function getX(ch) {
- var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line",
+ var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
lineObj, measurement);
wrongLine = true;
- if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth);
- else if (innerOff < sp.top) return sp.left + cWidth;
+ if (innerOff > sp.bottom) return sp.left - adjust;
+ else if (innerOff < sp.top) return sp.left + adjust;
else wrongLine = false;
return sp.left;
}
var bidi = getOrder(lineObj), dist = lineObj.text.length;
var from = lineLeft(lineObj), to = lineRight(lineObj);
- var fromX = paddingLeft(cm.display), toX = getX(to);
+ var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
- if (x > toX) return {line: lineNo, ch: to, outside: wrongLine};
+ if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1);
// Do a binary search between these bounds.
for (;;) {
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
- var after = x - fromX < toX - x, ch = after ? from : to;
+ var ch = x < fromX || x - fromX <= toX - x ? from : to;
+ var xDiff = x - (ch == from ? fromX : toX);
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
- return {line: lineNo, ch: ch, after: after, outside: wrongLine};
+ var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
+ xDiff < 0 ? -1 : xDiff ? 1 : 0);
+ return pos;
}
var step = Math.ceil(dist / 2), middle = from + step;
if (bidi) {
middle = from;
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
}
var middleX = getX(middle);
- if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
- else {from = middle; fromX = middleX; dist = step;}
+ if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;}
+ else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;}
}
}
var measureText;
function textHeight(display) {
if (display.cachedTextHeight != null) return display.cachedTextHeight;
if (measureText == null) {
measureText = elt("pre");
// Measure a bunch of lines, for browsers that compute
// fractional heights.
for (var i = 0; i < 49; ++i) {
measureText.appendChild(document.createTextNode("x"));
measureText.appendChild(elt("br"));
}
measureText.appendChild(document.createTextNode("x"));
}
removeChildrenAndAdd(display.measure, measureText);
var height = measureText.offsetHeight / 50;
if (height > 3) display.cachedTextHeight = height;
removeChildren(display.measure);
return height || 1;
}
function charWidth(display) {
if (display.cachedCharWidth != null) return display.cachedCharWidth;
var anchor = elt("span", "x");
var pre = elt("pre", [anchor]);
removeChildrenAndAdd(display.measure, pre);
var width = anchor.offsetWidth;
if (width > 2) display.cachedCharWidth = width;
return width || 10;
}
// OPERATIONS
// Operations are used to wrap changes in such a way that each
// change won't have to update the cursor and display (which would
// be awkward, slow, and error-prone), but instead updates are
// batched and then all combined and executed at once.
+ var nextOpId = 0;
function startOperation(cm) {
- if (cm.curOp) ++cm.curOp.depth;
- else cm.curOp = {
- // Nested operations delay update until the outermost one
- // finishes.
- depth: 1,
+ cm.curOp = {
// An array of ranges of lines that have to be updated. See
// updateDisplay.
changes: [],
- delayedCallbacks: [],
updateInput: null,
userSelChange: null,
textChanged: null,
selectionChanged: false,
+ cursorActivity: false,
updateMaxLine: false,
- id: ++cm.nextOpId
+ updateScrollPos: false,
+ id: ++nextOpId
};
+ if (!delayedCallbackDepth++) delayedCallbacks = [];
}
function endOperation(cm) {
- var op = cm.curOp;
- if (--op.depth) return;
+ var op = cm.curOp, doc = cm.doc, display = cm.display;
cm.curOp = null;
- var view = cm.view, display = cm.display;
- if (op.updateMaxLine) computeMaxLength(view);
- if (view.maxLineChanged && !cm.options.lineWrapping) {
- var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right;
- display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
- view.maxLineChanged = false;
+
+ if (op.updateMaxLine) computeMaxLength(cm);
+ if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) {
+ var width = measureLineWidth(cm, display.maxLine);
+ display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
+ display.maxLineChanged = false;
var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
- if (maxScrollLeft < view.scrollLeft)
+ if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
}
var newScrollPos, updated;
- if (op.selectionChanged) {
- var coords = cursorCoords(cm, view.sel.head);
+ if (op.updateScrollPos) {
+ newScrollPos = op.updateScrollPos;
+ } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
+ var coords = cursorCoords(cm, doc.sel.head);
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
}
- if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
+ if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
+ if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
+ }
if (!updated && op.selectionChanged) updateSelection(cm);
- if (newScrollPos) scrollCursorIntoView(cm);
+ if (op.updateScrollPos) {
+ display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
+ display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
+ alignHorizontally(cm);
+ if (op.scrollToPos)
+ scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
+ } else if (newScrollPos) {
+ scrollCursorIntoView(cm);
+ }
if (op.selectionChanged) restartBlink(cm);
- if (view.focused && op.updateInput)
+ if (cm.state.focused && op.updateInput)
resetInput(cm, op.userSelChange);
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
+ if (hidden) for (var i = 0; i < hidden.length; ++i)
+ if (!hidden[i].lines.length) signal(hidden[i], "hide");
+ if (unhidden) for (var i = 0; i < unhidden.length; ++i)
+ if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
+
+ var delayed;
+ if (!--delayedCallbackDepth) {
+ delayed = delayedCallbacks;
+ delayedCallbacks = null;
+ }
if (op.textChanged)
signal(cm, "change", cm, op.textChanged);
- if (op.selectionChanged) signal(cm, "cursorActivity", cm);
- for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm);
+ if (op.cursorActivity) signal(cm, "cursorActivity", cm);
+ if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
}
// Wraps a function in an operation. Returns the wrapped function.
function operation(cm1, f) {
return function() {
- var cm = cm1 || this;
- startOperation(cm);
- try {var result = f.apply(cm, arguments);}
- finally {endOperation(cm);}
+ var cm = cm1 || this, withOp = !cm.curOp;
+ if (withOp) startOperation(cm);
+ try { var result = f.apply(cm, arguments); }
+ finally { if (withOp) endOperation(cm); }
return result;
};
}
+ function docOperation(f) {
+ return function() {
+ var withOp = this.cm && !this.cm.curOp, result;
+ if (withOp) startOperation(this.cm);
+ try { result = f.apply(this, arguments); }
+ finally { if (withOp) endOperation(this.cm); }
+ return result;
+ };
+ }
+ function runInOp(cm, f) {
+ var withOp = !cm.curOp, result;
+ if (withOp) startOperation(cm);
+ try { result = f(); }
+ finally { if (withOp) endOperation(cm); }
+ return result;
+ }
function regChange(cm, from, to, lendiff) {
+ if (from == null) from = cm.doc.first;
+ if (to == null) to = cm.doc.first + cm.doc.size;
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
}
// INPUT HANDLING
function slowPoll(cm) {
- if (cm.view.pollingFast) return;
+ if (cm.display.pollingFast) return;
cm.display.poll.set(cm.options.pollInterval, function() {
readInput(cm);
- if (cm.view.focused) slowPoll(cm);
+ if (cm.state.focused) slowPoll(cm);
});
}
function fastPoll(cm) {
var missed = false;
cm.display.pollingFast = true;
function p() {
var changed = readInput(cm);
if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
else {cm.display.pollingFast = false; slowPoll(cm);}
}
cm.display.poll.set(20, p);
}
// prevInput is a hack to work with IME. If we reset the textarea
// on every change, that breaks IME. So we look for changes
// compared to the previous content instead. (Modern browsers have
// events that indicate IME taking place, but these are not widely
// supported or compatible enough yet to rely on.)
function readInput(cm) {
- var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel;
- if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false;
+ var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
+ if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
var text = input.value;
if (text == prevInput && posEq(sel.from, sel.to)) return false;
- startOperation(cm);
- view.sel.shift = false;
+ if (ie && !ie_lt9 && cm.display.inputHasSelection === text) {
+ resetInput(cm, true);
+ return false;
+ }
+
+ var withOp = !cm.curOp;
+ if (withOp) startOperation(cm);
+ sel.shift = false;
var same = 0, l = Math.min(prevInput.length, text.length);
- while (same < l && prevInput[same] == text[same]) ++same;
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
var from = sel.from, to = sel.to;
if (same < prevInput.length)
- from = {line: from.line, ch: from.ch - (prevInput.length - same)};
- else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming)
- to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))};
+ from = Pos(from.line, from.ch - (prevInput.length - same));
+ else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
+ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
+
var updateInput = cm.curOp.updateInput;
- updateDoc(cm, from, to, splitLines(text.slice(same)), "end",
- cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to});
+ var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
+ origin: cm.state.pasteIncoming ? "paste" : "+input"};
+ makeChange(cm.doc, changeEvent, "end");
cm.curOp.updateInput = updateInput;
- if (text.length > 1000) input.value = cm.display.prevInput = "";
+ signalLater(cm, "inputRead", cm, changeEvent);
+
+ if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
else cm.display.prevInput = text;
- endOperation(cm);
- cm.display.pasteIncoming = false;
+ if (withOp) endOperation(cm);
+ cm.state.pasteIncoming = false;
return true;
}
function resetInput(cm, user) {
- var view = cm.view, minimal, selected;
- if (!posEq(view.sel.from, view.sel.to)) {
+ var minimal, selected, doc = cm.doc;
+ if (!posEq(doc.sel.from, doc.sel.to)) {
cm.display.prevInput = "";
minimal = hasCopyEvent &&
- (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
- if (minimal) cm.display.input.value = "-";
- else cm.display.input.value = selected || cm.getSelection();
- if (view.focused) selectInput(cm.display.input);
- } else if (user) cm.display.prevInput = cm.display.input.value = "";
+ (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
+ var content = minimal ? "-" : selected || cm.getSelection();
+ cm.display.input.value = content;
+ if (cm.state.focused) selectInput(cm.display.input);
+ if (ie && !ie_lt9) cm.display.inputHasSelection = content;
+ } else if (user) {
+ cm.display.prevInput = cm.display.input.value = "";
+ if (ie && !ie_lt9) cm.display.inputHasSelection = null;
+ }
cm.display.inaccurateSelection = minimal;
}
function focusInput(cm) {
- if (cm.options.readOnly != "nocursor" && (ie || document.activeElement != cm.display.input))
+ if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
cm.display.input.focus();
}
function isReadOnly(cm) {
- return cm.options.readOnly || cm.view.cantEdit;
+ return cm.options.readOnly || cm.doc.cantEdit;
}
// EVENT HANDLERS
function registerEventHandlers(cm) {
var d = cm.display;
on(d.scroller, "mousedown", operation(cm, onMouseDown));
- on(d.scroller, "dblclick", operation(cm, e_preventDefault));
+ if (ie)
+ on(d.scroller, "dblclick", operation(cm, function(e) {
+ if (signalDOMEvent(cm, e)) return;
+ var pos = posFromMouse(cm, e);
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
+ e_preventDefault(e);
+ var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
+ extendSelection(cm.doc, word.from, word.to);
+ }));
+ else
+ on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
on(d.lineSpace, "selectstart", function(e) {
if (!eventInWidget(d, e)) e_preventDefault(e);
});
// Gecko browsers fire contextmenu *after* opening the menu, at
// which point we can't mess with it anymore. Context menu is
// handled in onMouseDown for Gecko.
- if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
+ if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
on(d.scroller, "scroll", function() {
- setScrollTop(cm, d.scroller.scrollTop);
- setScrollLeft(cm, d.scroller.scrollLeft, true);
- signal(cm, "scroll", cm);
+ if (d.scroller.clientHeight) {
+ setScrollTop(cm, d.scroller.scrollTop);
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
+ signal(cm, "scroll", cm);
+ }
});
on(d.scrollbarV, "scroll", function() {
- setScrollTop(cm, d.scrollbarV.scrollTop);
+ if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
});
on(d.scrollbarH, "scroll", function() {
- setScrollLeft(cm, d.scrollbarH.scrollLeft);
+ if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
});
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
- function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); }
+ function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
on(d.scrollbarH, "mousedown", reFocus);
on(d.scrollbarV, "mousedown", reFocus);
// Prevent wrapper from ever scrolling
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
- if (!window.registered) window.registered = 0;
- ++window.registered;
+ var resizeTimer;
function onResize() {
- // Might be a text scaling operation, clear size caches.
- d.cachedCharWidth = d.cachedTextHeight = null;
- clearCaches(cm);
- updateDisplay(cm, true);
+ if (resizeTimer == null) resizeTimer = setTimeout(function() {
+ resizeTimer = null;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
+ clearCaches(cm);
+ runInOp(cm, bind(regChange, cm));
+ }, 100);
}
on(window, "resize", onResize);
// Above handler holds on to the editor and its data structures.
// Here we poll to unregister it when the editor is no longer in
// the document, so that it can be garbage-collected.
- setTimeout(function unregister() {
+ function unregister() {
for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
if (p) setTimeout(unregister, 5000);
- else {--window.registered; off(window, "resize", onResize);}
- }, 5000);
+ else off(window, "resize", onResize);
+ }
+ setTimeout(unregister, 5000);
on(d.input, "keyup", operation(cm, function(e) {
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
- if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false;
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ if (e.keyCode == 16) cm.doc.sel.shift = false;
}));
on(d.input, "input", bind(fastPoll, cm));
on(d.input, "keydown", operation(cm, onKeyDown));
on(d.input, "keypress", operation(cm, onKeyPress));
on(d.input, "focus", bind(onFocus, cm));
on(d.input, "blur", bind(onBlur, cm));
function drag_(e) {
- if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
+ if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return;
e_stop(e);
}
if (cm.options.dragDrop) {
on(d.scroller, "dragstart", function(e){onDragStart(cm, e);});
on(d.scroller, "dragenter", drag_);
on(d.scroller, "dragover", drag_);
on(d.scroller, "drop", operation(cm, onDrop));
}
on(d.scroller, "paste", function(e){
if (eventInWidget(d, e)) return;
- focusInput(cm);
+ focusInput(cm);
fastPoll(cm);
});
on(d.input, "paste", function() {
- d.pasteIncoming = true;
+ cm.state.pasteIncoming = true;
fastPoll(cm);
});
function prepareCopy() {
if (d.inaccurateSelection) {
d.prevInput = "";
d.inaccurateSelection = false;
d.input.value = cm.getSelection();
selectInput(d.input);
}
}
on(d.input, "cut", prepareCopy);
on(d.input, "copy", prepareCopy);
// Needed to handle Tab key in KHTML
if (khtml) on(d.sizer, "mouseup", function() {
if (document.activeElement == d.input) d.input.blur();
focusInput(cm);
});
}
function eventInWidget(display, e) {
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
- if (!n) return true;
- if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
- n.parentNode == display.sizer && n != display.mover) return true;
+ if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true;
}
}
function posFromMouse(cm, e, liberal) {
var display = cm.display;
if (!liberal) {
var target = e_target(e);
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
- target == display.scrollbarFiller) return null;
+ target == display.scrollbarFiller || target == display.gutterFiller) return null;
}
- var x, y, space = display.lineSpace.getBoundingClientRect();
+ var x, y, space = getRect(display.lineSpace);
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
return coordsChar(cm, x - space.left, y - space.top);
}
var lastClick, lastDoubleClick;
function onMouseDown(e) {
- var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc;
- sel.shift = e_prop(e, "shiftKey");
+ if (signalDOMEvent(this, e)) return;
+ var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
+ sel.shift = e.shiftKey;
if (eventInWidget(display, e)) {
if (!webkit) {
display.scroller.draggable = false;
setTimeout(function(){display.scroller.draggable = true;}, 100);
}
return;
}
if (clickInGutter(cm, e)) return;
var start = posFromMouse(cm, e);
switch (e_button(e)) {
case 3:
- if (gecko) onContextMenu.call(cm, cm, e);
+ if (captureMiddleClick) onContextMenu.call(cm, cm, e);
return;
case 2:
- if (start) extendSelection(cm, start);
+ if (start) extendSelection(cm.doc, start);
setTimeout(bind(focusInput, cm), 20);
e_preventDefault(e);
return;
}
// For button 1, if it was clicked inside the editor
// (posFromMouse returning non-null), we have to adjust the
// selection.
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
- if (!view.focused) onFocus(cm);
+ if (!cm.state.focused) onFocus(cm);
var now = +new Date, type = "single";
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
type = "triple";
e_preventDefault(e);
setTimeout(bind(focusInput, cm), 20);
selectLine(cm, start.line);
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
type = "double";
lastDoubleClick = {time: now, pos: start};
e_preventDefault(e);
var word = findWordAt(getLine(doc, start.line).text, start);
- extendSelection(cm, word.from, word.to);
+ extendSelection(cm.doc, word.from, word.to);
} else { lastClick = {time: now, pos: start}; }
var last = start;
if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) &&
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
var dragEnd = operation(cm, function(e2) {
if (webkit) display.scroller.draggable = false;
- view.draggingText = false;
+ cm.state.draggingText = false;
off(document, "mouseup", dragEnd);
off(display.scroller, "drop", dragEnd);
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
e_preventDefault(e2);
- extendSelection(cm, start);
+ extendSelection(cm.doc, start);
focusInput(cm);
}
});
// Let the drag handler handle this.
if (webkit) display.scroller.draggable = true;
- view.draggingText = dragEnd;
+ cm.state.draggingText = dragEnd;
// IE's approach to draggable
if (display.scroller.dragDrop) display.scroller.dragDrop();
on(document, "mouseup", dragEnd);
on(display.scroller, "drop", dragEnd);
return;
}
e_preventDefault(e);
- if (type == "single") extendSelection(cm, clipPos(doc, start));
+ if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
- var startstart = sel.from, startend = sel.to;
+ var startstart = sel.from, startend = sel.to, lastPos = start;
function doSelect(cur) {
+ if (posEq(lastPos, cur)) return;
+ lastPos = cur;
+
if (type == "single") {
- extendSelection(cm, clipPos(doc, start), cur);
+ extendSelection(cm.doc, clipPos(doc, start), cur);
return;
}
startstart = clipPos(doc, startstart);
startend = clipPos(doc, startend);
if (type == "double") {
var word = findWordAt(getLine(doc, cur.line).text, cur);
- if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
- else extendSelection(cm, startstart, word.to);
+ if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
+ else extendSelection(cm.doc, startstart, word.to);
} else if (type == "triple") {
- if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0}));
- else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0}));
+ if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
+ else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
}
}
- var editorSize = display.wrapper.getBoundingClientRect();
+ var editorSize = getRect(display.wrapper);
// Used to ensure timeout re-tries don't fire when another extend
// happened in the meantime (clearTimeout isn't reliable -- at
// least on Chrome, the timeouts still happen even when cleared,
// if the clear happens after their scheduled firing time).
var counter = 0;
function extend(e) {
var curCount = ++counter;
var cur = posFromMouse(cm, e, true);
if (!cur) return;
if (!posEq(cur, last)) {
- if (!view.focused) onFocus(cm);
+ if (!cm.state.focused) onFocus(cm);
last = cur;
doSelect(cur);
var visible = visibleLines(display, doc);
if (cur.line >= visible.to || cur.line < visible.from)
setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
} else {
var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
if (outside) setTimeout(operation(cm, function() {
if (counter != curCount) return;
display.scroller.scrollTop += outside;
extend(e);
}), 50);
}
}
function done(e) {
counter = Infinity;
- var cur = posFromMouse(cm, e);
- if (cur) doSelect(cur);
e_preventDefault(e);
focusInput(cm);
off(document, "mousemove", move);
off(document, "mouseup", up);
}
var move = operation(cm, function(e) {
if (!ie && !e_button(e)) done(e);
else extend(e);
});
var up = operation(cm, done);
on(document, "mousemove", move);
on(document, "mouseup", up);
}
+ function clickInGutter(cm, e) {
+ var display = cm.display;
+ try { var mX = e.clientX, mY = e.clientY; }
+ catch(e) { return false; }
+
+ if (mX >= Math.floor(getRect(display.gutters).right)) return false;
+ e_preventDefault(e);
+ if (!hasHandler(cm, "gutterClick")) return true;
+
+ var lineBox = getRect(display.lineDiv);
+ if (mY > lineBox.bottom) return true;
+ mY -= lineBox.top - display.viewOffset;
+
+ for (var i = 0; i < cm.options.gutters.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && getRect(g).right >= mX) {
+ var line = lineAtHeight(cm.doc, mY);
+ var gutter = cm.options.gutters[i];
+ signalLater(cm, "gutterClick", cm, line, gutter, e);
+ break;
+ }
+ }
+ return true;
+ }
+
+ // Kludge to work around strange IE behavior where it'll sometimes
+ // re-fire a series of drag-related events right after the drop (#1551)
+ var lastDrop = 0;
+
function onDrop(e) {
var cm = this;
- if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
return;
e_preventDefault(e);
+ if (ie) lastDrop = +new Date;
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
if (!pos || isReadOnly(cm)) return;
if (files && files.length && window.FileReader && window.File) {
var n = files.length, text = Array(n), read = 0;
var loadFile = function(file, i) {
var reader = new FileReader;
reader.onload = function() {
text[i] = reader.result;
if (++read == n) {
- pos = clipPos(cm.view.doc, pos);
- operation(cm, function() {
- var end = replaceRange(cm, text.join(""), pos, pos, "paste");
- setSelection(cm, pos, end);
- })();
+ pos = clipPos(cm.doc, pos);
+ makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
}
};
reader.readAsText(file);
};
for (var i = 0; i < n; ++i) loadFile(files[i], i);
} else {
// Don't do a replace if the drop happened inside of the selected text.
- if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) {
- cm.view.draggingText(e);
+ if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
+ cm.state.draggingText(e);
// Ensure the editor is re-focused
setTimeout(bind(focusInput, cm), 20);
return;
}
try {
var text = e.dataTransfer.getData("Text");
if (text) {
- var curFrom = cm.view.sel.from, curTo = cm.view.sel.to;
- setSelection(cm, pos, pos);
- if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste");
+ var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
+ setSelection(cm.doc, pos, pos);
+ if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
cm.replaceSelection(text, null, "paste");
focusInput(cm);
onFocus(cm);
}
}
catch(e){}
}
}
- function clickInGutter(cm, e) {
- var display = cm.display;
- try { var mX = e.clientX, mY = e.clientY; }
- catch(e) { return false; }
-
- if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false;
- e_preventDefault(e);
- if (!hasHandler(cm, "gutterClick")) return true;
-
- var lineBox = display.lineDiv.getBoundingClientRect();
- if (mY > lineBox.bottom) return true;
- mY -= lineBox.top - display.viewOffset;
-
- for (var i = 0; i < cm.options.gutters.length; ++i) {
- var g = display.gutters.childNodes[i];
- if (g && g.getBoundingClientRect().right >= mX) {
- var line = lineAtHeight(cm.view.doc, mY);
- var gutter = cm.options.gutters[i];
- signalLater(cm, cm, "gutterClick", cm, line, gutter, e);
- break;
- }
- }
- return true;
- }
-
function onDragStart(cm, e) {
- if (eventInWidget(cm.display, e)) return;
-
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; }
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
+
var txt = cm.getSelection();
e.dataTransfer.setData("Text", txt);
// Use dummy image instead of default browsers image.
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
if (e.dataTransfer.setDragImage && !safari) {
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
if (opera) {
img.width = img.height = 1;
cm.display.wrapper.appendChild(img);
// Force a relayout, or Opera won't use our image for some obscure reason
img._top = img.offsetTop;
}
e.dataTransfer.setDragImage(img, 0, 0);
if (opera) img.parentNode.removeChild(img);
}
}
function setScrollTop(cm, val) {
- if (Math.abs(cm.view.scrollTop - val) < 2) return;
- cm.view.scrollTop = val;
+ if (Math.abs(cm.doc.scrollTop - val) < 2) return;
+ cm.doc.scrollTop = val;
if (!gecko) updateDisplay(cm, [], val);
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
if (gecko) updateDisplay(cm, []);
+ startWorker(cm, 100);
}
function setScrollLeft(cm, val, isScroller) {
- if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return;
+ if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
- cm.view.scrollLeft = val;
+ cm.doc.scrollLeft = val;
alignHorizontally(cm);
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
}
// Since the delta values reported on mouse wheel events are
// unstandardized between browsers and even browser versions, and
// generally horribly unpredictable, this code starts by measuring
// the scroll effect that the first few mouse wheel events have,
// and, from that, detects the way it can convert deltas to pixel
// offsets afterwards.
//
// The reason we want to know the amount a wheel event will scroll
// is that it gives us a chance to update the display before the
// actual scrolling happens, reducing flickering.
var wheelSamples = 0, wheelPixelsPerUnit = null;
// Fill in a browser-detected starting value on browsers where we
// know one. These don't have to be accurate -- the result of them
// being wrong would just be a slight flicker on the first wheel
// scroll (if it is large enough).
if (ie) wheelPixelsPerUnit = -.53;
else if (gecko) wheelPixelsPerUnit = 15;
else if (chrome) wheelPixelsPerUnit = -.7;
else if (safari) wheelPixelsPerUnit = -1/3;
function onScrollWheel(cm, e) {
var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail;
if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail;
else if (dy == null) dy = e.wheelDelta;
+ var display = cm.display, scroll = display.scroller;
+ // Quit if there's nothing to scroll here
+ if (!(dx && scroll.scrollWidth > scroll.clientWidth ||
+ dy && scroll.scrollHeight > scroll.clientHeight)) return;
+
// Webkit browsers on OS X abort momentum scrolls when the target
// of the scroll event is removed from the scrollable element.
// This hack (see related code in patchDisplay) makes sure the
// element is kept around.
if (dy && mac && webkit) {
for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
if (cur.lineObj) {
cm.display.currentWheelTarget = cur;
break;
}
}
}
- var display = cm.display, scroll = display.scroller;
// On some browsers, horizontal scrolling will cause redraws to
// happen before the gutter has been realigned, causing it to
// wriggle around in a most unseemly way. When we have an
// estimated pixels/delta value, we just handle horizontal
// scrolling entirely here. It'll be slightly off from native, but
// better than glitching out.
if (dx && !gecko && !opera && wheelPixelsPerUnit != null) {
if (dy)
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
e_preventDefault(e);
display.wheelStartX = null; // Abort measurement, if in progress
return;
}
if (dy && wheelPixelsPerUnit != null) {
var pixels = dy * wheelPixelsPerUnit;
- var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight;
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
if (pixels < 0) top = Math.max(0, top + pixels - 50);
- else bot = Math.min(cm.view.doc.height, bot + pixels + 50);
+ else bot = Math.min(cm.doc.height, bot + pixels + 50);
updateDisplay(cm, [], {top: top, bottom: bot});
}
if (wheelSamples < 20) {
if (display.wheelStartX == null) {
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
display.wheelDX = dx; display.wheelDY = dy;
setTimeout(function() {
if (display.wheelStartX == null) return;
var movedX = scroll.scrollLeft - display.wheelStartX;
var movedY = scroll.scrollTop - display.wheelStartY;
var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
(movedX && display.wheelDX && movedX / display.wheelDX);
display.wheelStartX = display.wheelStartY = null;
if (!sample) return;
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
++wheelSamples;
}, 200);
} else {
display.wheelDX += dx; display.wheelDY += dy;
}
}
}
function doHandleBinding(cm, bound, dropShift) {
if (typeof bound == "string") {
bound = commands[bound];
if (!bound) return false;
}
// Ensure previous input has been read, so that the handler sees a
// consistent view of the document
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
- var view = cm.view, prevShift = view.sel.shift;
+ var doc = cm.doc, prevShift = doc.sel.shift, done = false;
try {
- if (isReadOnly(cm)) view.suppressEdits = true;
- if (dropShift) view.sel.shift = false;
- bound(cm);
- } catch(e) {
- if (e != Pass) throw e;
- return false;
+ if (isReadOnly(cm)) cm.state.suppressEdits = true;
+ if (dropShift) doc.sel.shift = false;
+ done = bound(cm) != Pass;
} finally {
- view.sel.shift = prevShift;
- view.suppressEdits = false;
+ doc.sel.shift = prevShift;
+ cm.state.suppressEdits = false;
}
- return true;
+ return done;
}
function allKeyMaps(cm) {
- var maps = cm.view.keyMaps.slice(0);
+ var maps = cm.state.keyMaps.slice(0);
+ if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
maps.push(cm.options.keyMap);
- if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
return maps;
}
var maybeTransition;
function handleKeyBinding(cm, e) {
// Handle auto keymap transitions
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
clearTimeout(maybeTransition);
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
- if (getKeyMap(cm.options.keyMap) == startMap)
+ if (getKeyMap(cm.options.keyMap) == startMap) {
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
+ keyMapChanged(cm);
+ }
}, 50);
- var name = keyNames[e_prop(e, "keyCode")], handled = false;
- if (name == null || e.altGraphKey) return false;
- if (e_prop(e, "altKey")) name = "Alt-" + name;
- if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
- if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
-
- var stopped = false;
- function stop() { stopped = true; }
+ var name = keyName(e, true), handled = false;
+ if (!name) return false;
var keymaps = allKeyMaps(cm);
- if (e_prop(e, "shiftKey")) {
- handled = lookupKey("Shift-" + name, keymaps,
- function(b) {return doHandleBinding(cm, b, true);}, stop)
- || lookupKey(name, keymaps, function(b) {
- if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
- }, stop);
+ if (e.shiftKey) {
+ // First try to resolve full name (including 'Shift-'). Failing
+ // that, see if there is a cursor-motion command (starting with
+ // 'go') bound to the keyname without 'Shift-'.
+ handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
+ || lookupKey(name, keymaps, function(b) {
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
+ return doHandleBinding(cm, b);
+ });
} else {
- handled = lookupKey(name, keymaps,
- function(b) { return doHandleBinding(cm, b); }, stop);
+ handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
}
- if (stopped) handled = false;
+
if (handled) {
e_preventDefault(e);
restartBlink(cm);
if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
+ signalLater(cm, "keyHandled", cm, name, e);
}
return handled;
}
function handleCharBinding(cm, e, ch) {
var handled = lookupKey("'" + ch + "'", allKeyMaps(cm),
function(b) { return doHandleBinding(cm, b, true); });
if (handled) {
e_preventDefault(e);
restartBlink(cm);
+ signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
}
return handled;
}
var lastStoppedKey = null;
function onKeyDown(e) {
var cm = this;
- if (!cm.view.focused) onFocus(cm);
+ if (!cm.state.focused) onFocus(cm);
if (ie && e.keyCode == 27) { e.returnValue = false; }
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
- var code = e_prop(e, "keyCode");
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ var code = e.keyCode;
// IE does strange things with escape.
- cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey");
+ cm.doc.sel.shift = code == 16 || e.shiftKey;
// First give onKeyEvent option a chance to handle this.
var handled = handleKeyBinding(cm, e);
if (opera) {
lastStoppedKey = handled ? code : null;
// Opera has no cut event... we try to at least catch the key combo
- if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey"))
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
cm.replaceSelection("");
}
}
function onKeyPress(e) {
var cm = this;
- if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
- var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
+ if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
+ var keyCode = e.keyCode, charCode = e.charCode;
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
- if (this.options.electricChars && this.view.mode.electricChars &&
+ if (this.options.electricChars && this.doc.mode.electricChars &&
this.options.smartIndent && !isReadOnly(this) &&
- this.view.mode.electricChars.indexOf(ch) > -1)
- setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75);
+ this.doc.mode.electricChars.indexOf(ch) > -1)
+ setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
if (handleCharBinding(cm, e, ch)) return;
+ if (ie && !ie_lt9) cm.display.inputHasSelection = null;
fastPoll(cm);
}
function onFocus(cm) {
if (cm.options.readOnly == "nocursor") return;
- if (!cm.view.focused) {
+ if (!cm.state.focused) {
signal(cm, "focus", cm);
- cm.view.focused = true;
- if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1)
- cm.display.scroller.className += " CodeMirror-focused";
+ cm.state.focused = true;
+ if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
+ cm.display.wrapper.className += " CodeMirror-focused";
resetInput(cm, true);
}
slowPoll(cm);
restartBlink(cm);
}
function onBlur(cm) {
- if (cm.view.focused) {
+ if (cm.state.focused) {
signal(cm, "blur", cm);
- cm.view.focused = false;
- cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", "");
+ cm.state.focused = false;
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
}
clearInterval(cm.display.blinker);
- setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150);
+ setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
}
var detectingSelectAll;
function onContextMenu(cm, e) {
- var display = cm.display;
+ var display = cm.display, sel = cm.doc.sel;
if (eventInWidget(display, e)) return;
-
- var sel = cm.view.sel;
+
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
if (!pos || opera) return; // Opera is difficult.
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
- operation(cm, setSelection)(cm, pos, pos);
+ operation(cm, setSelection)(cm.doc, pos, pos);
var oldCSS = display.input.style.cssText;
display.inputDiv.style.position = "absolute";
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
- "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
focusInput(cm);
resetInput(cm, true);
// Adds "Select all" to context menu in FF
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
+ function prepareSelectAllHack() {
+ if (display.input.selectionStart != null) {
+ var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
+ display.prevInput = " ";
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+ }
+ }
function rehide() {
display.inputDiv.style.position = "relative";
display.input.style.cssText = oldCSS;
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
slowPoll(cm);
- // Try to detect the user choosing select-all
+ // Try to detect the user choosing select-all
if (display.input.selectionStart != null) {
+ if (!ie || ie_lt9) prepareSelectAllHack();
clearTimeout(detectingSelectAll);
- var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
- display.prevInput = " ";
- display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
- detectingSelectAll = setTimeout(function poll(){
+ var i = 0, poll = function(){
if (display.prevInput == " " && display.input.selectionStart == 0)
operation(cm, commands.selectAll)(cm);
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
else resetInput(cm);
- }, 200);
+ };
+ detectingSelectAll = setTimeout(poll, 200);
}
}
- if (gecko) {
+ if (ie && !ie_lt9) prepareSelectAllHack();
+ if (captureMiddleClick) {
e_stop(e);
- on(window, "mouseup", function mouseup() {
+ var mouseup = function() {
off(window, "mouseup", mouseup);
setTimeout(rehide, 20);
- });
+ };
+ on(window, "mouseup", mouseup);
} else {
setTimeout(rehide, 50);
}
}
// UPDATING
- // Replace the range from from to to by the strings in newText.
- // Afterwards, set the selection to selFrom, selTo.
- function updateDoc(cm, from, to, newText, selUpdate, origin) {
+ var changeEnd = CodeMirror.changeEnd = function(change) {
+ if (!change.text) return change.to;
+ return Pos(change.from.line + change.text.length - 1,
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
+ };
+
+ // Make sure a position will be valid after the given change.
+ function clipPostChange(doc, change, pos) {
+ if (!posLess(change.from, pos)) return clipPos(doc, pos);
+ var diff = (change.text.length - 1) - (change.to.line - change.from.line);
+ if (pos.line > change.to.line + diff) {
+ var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
+ if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
+ return clipToLen(pos, getLine(doc, preLine).text.length);
+ }
+ if (pos.line == change.to.line + diff)
+ return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
+ getLine(doc, change.to.line).text.length - change.to.ch);
+ var inside = pos.line - change.from.line;
+ return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
+ }
+
+ // Hint can be null|"end"|"start"|"around"|{anchor,head}
+ function computeSelAfterChange(doc, change, hint) {
+ if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
+ return {anchor: clipPostChange(doc, change, hint.anchor),
+ head: clipPostChange(doc, change, hint.head)};
+
+ if (hint == "start") return {anchor: change.from, head: change.from};
+
+ var end = changeEnd(change);
+ if (hint == "around") return {anchor: change.from, head: end};
+ if (hint == "end") return {anchor: end, head: end};
+
+ // hint is null, leave the selection alone as much as possible
+ var adjustPos = function(pos) {
+ if (posLess(pos, change.from)) return pos;
+ if (!posLess(change.to, pos)) return end;
+
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+ if (pos.line == change.to.line) ch += end.ch - change.to.ch;
+ return Pos(line, ch);
+ };
+ return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
+ }
+
+ function filterChange(doc, change, update) {
+ var obj = {
+ canceled: false,
+ from: change.from,
+ to: change.to,
+ text: change.text,
+ origin: change.origin,
+ cancel: function() { this.canceled = true; }
+ };
+ if (update) obj.update = function(from, to, text, origin) {
+ if (from) this.from = clipPos(doc, from);
+ if (to) this.to = clipPos(doc, to);
+ if (text) this.text = text;
+ if (origin !== undefined) this.origin = origin;
+ };
+ signal(doc, "beforeChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
+
+ if (obj.canceled) return null;
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
+ }
+
+ // Replace the range from from to to by the strings in replacement.
+ // change is a {from, to, text [, origin]} object
+ function makeChange(doc, change, selUpdate, ignoreReadOnly) {
+ if (doc.cm) {
+ if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
+ if (doc.cm.state.suppressEdits) return;
+ }
+
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
+ change = filterChange(doc, change, true);
+ if (!change) return;
+ }
+
// Possibly split or suppress the update based on the presence
// of read-only spans in its range.
- var split = sawReadOnlySpans &&
- removeReadOnlyRanges(cm.view.doc, from, to);
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
if (split) {
for (var i = split.length - 1; i >= 1; --i)
- updateDocInner(cm, split[i].from, split[i].to, [""], origin);
+ makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
if (split.length)
- return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin);
+ makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
} else {
- return updateDocInner(cm, from, to, newText, selUpdate, origin);
+ makeChangeNoReadonly(doc, change, selUpdate);
}
}
- function updateDocInner(cm, from, to, newText, selUpdate, origin) {
- if (cm.view.suppressEdits) return;
+ function makeChangeNoReadonly(doc, change, selUpdate) {
+ var selAfter = computeSelAfterChange(doc, change, selUpdate);
+ addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
- var view = cm.view, doc = view.doc, old = [];
- doc.iter(from.line, to.line + 1, function(line) {
- old.push(newHL(line.text, line.markedSpans));
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
+ var rebased = [];
+
+ linkedDocs(doc, function(doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change);
+ rebased.push(doc.history);
+ }
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
});
- var startSelFrom = view.sel.from, startSelTo = view.sel.to;
- var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText);
- var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin);
- if (view.history) addChange(cm, from.line, newText.length, old, origin,
- startSelFrom, startSelTo, view.sel.from, view.sel.to);
- return retval;
- }
-
- function unredoHelper(cm, type) {
- var doc = cm.view.doc, hist = cm.view.history;
- var set = (type == "undo" ? hist.done : hist.undone).pop();
- if (!set) return;
- var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
- fromAfter: set.fromBefore, toAfter: set.toBefore};
- for (var i = set.events.length - 1; i >= 0; i -= 1) {
- hist.dirtyCounter += type == "undo" ? -1 : 1;
- var change = set.events[i];
- var replaced = [], end = change.start + change.added;
- doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
- anti.events.push({start: change.start, added: change.old.length, old: replaced});
- var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
- updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
- change.old, selPos, type);
- }
+ }
+
+ function makeChangeFromHistory(doc, type) {
+ if (doc.cm && doc.cm.state.suppressEdits) return;
+
+ var hist = doc.history;
+ var event = (type == "undo" ? hist.done : hist.undone).pop();
+ if (!event) return;
+
+ var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
+ anchorAfter: event.anchorBefore, headAfter: event.headBefore,
+ generation: hist.generation};
(type == "undo" ? hist.undone : hist.done).push(anti);
+ hist.generation = event.generation || ++hist.maxGeneration;
+
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
+
+ for (var i = event.changes.length - 1; i >= 0; --i) {
+ var change = event.changes[i];
+ change.origin = type;
+ if (filter && !filterChange(doc, change, false)) {
+ (type == "undo" ? hist.done : hist.undone).length = 0;
+ return;
+ }
+
+ anti.changes.push(historyChangeFromChange(doc, change));
+
+ var after = i ? computeSelAfterChange(doc, change, null)
+ : {anchor: event.anchorBefore, head: event.headBefore};
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
+ var rebased = [];
+
+ linkedDocs(doc, function(doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change);
+ rebased.push(doc.history);
+ }
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
+ });
+ }
+ }
+
+ function shiftDoc(doc, distance) {
+ function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
+ doc.first += distance;
+ if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
+ doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
+ doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
+ }
+
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
+ if (doc.cm && !doc.cm.curOp)
+ return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
+
+ if (change.to.line < doc.first) {
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
+ return;
+ }
+ if (change.from.line > doc.lastLine()) return;
+
+ // Clip the change to the size of this doc
+ if (change.from.line < doc.first) {
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
+ shiftDoc(doc, shift);
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
+ text: [lst(change.text)], origin: change.origin};
+ }
+ var last = doc.lastLine();
+ if (change.to.line > last) {
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
+ text: [change.text[0]], origin: change.origin};
+ }
+
+ change.removed = getBetween(doc, change.from, change.to);
+
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
+ if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
+ else updateDoc(doc, change, spans, selAfter);
}
- function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) {
- var view = cm.view, doc = view.doc, display = cm.display;
- if (view.suppressEdits) return;
+ function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
- var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
var recomputeMaxLength = false, checkWidthStart = from.line;
if (!cm.options.lineWrapping) {
- checkWidthStart = lineNo(visualLine(doc, firstLine));
+ checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
doc.iter(checkWidthStart, to.line + 1, function(line) {
- if (line == view.maxLine) {
+ if (line == display.maxLine) {
recomputeMaxLength = true;
return true;
}
});
}
- var lastHL = lst(lines), th = textHeight(display);
+ if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head))
+ cm.curOp.cursorActivity = true;
- // First adjust the line structure
- if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
- // This is a whole-line replace. Treated specially to make
- // sure line objects move the way they are supposed to.
- var added = [];
- for (var i = 0, e = lines.length - 1; i < e; ++i)
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
- updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
- if (nlines) doc.remove(from.line, nlines, cm);
- if (added.length) doc.insert(from.line, added);
- } else if (firstLine == lastLine) {
- if (lines.length == 1) {
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
- firstLine.text.slice(to.ch), hlSpans(lines[0]));
- } else {
- for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
- added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
- doc.insert(from.line + 1, added);
- }
- } else if (lines.length == 1) {
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
- lastLine.text.slice(to.ch), hlSpans(lines[0]));
- doc.remove(from.line + 1, nlines, cm);
- } else {
- var added = [];
- updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
- updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
- for (var i = 1, e = lines.length - 1; i < e; ++i)
- added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
- if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
- doc.insert(from.line + 1, added);
- }
+ updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
- if (cm.options.lineWrapping) {
- var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3);
- doc.iter(from.line, from.line + lines.length, function(line) {
- if (line.height == 0) return;
- var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
- if (guess != line.height) updateLineHeight(line, guess);
- });
- } else {
- doc.iter(checkWidthStart, from.line + lines.length, function(line) {
+ if (!cm.options.lineWrapping) {
+ doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
var len = lineLength(doc, line);
- if (len > view.maxLineLength) {
- view.maxLine = line;
- view.maxLineLength = len;
- view.maxLineChanged = true;
+ if (len > display.maxLineLength) {
+ display.maxLine = line;
+ display.maxLineLength = len;
+ display.maxLineChanged = true;
recomputeMaxLength = false;
}
});
if (recomputeMaxLength) cm.curOp.updateMaxLine = true;
}
// Adjust frontier, schedule worker
- view.frontier = Math.min(view.frontier, from.line);
+ doc.frontier = Math.min(doc.frontier, from.line);
startWorker(cm, 400);
- var lendiff = lines.length - nlines - 1;
+ var lendiff = change.text.length - (to.line - from.line) - 1;
// Remember that these lines changed, for updating the display
regChange(cm, from.line, to.line + 1, lendiff);
+
if (hasHandler(cm, "change")) {
- // Normalize lines to contain only strings, since that's what
- // the change event handler expects
- for (var i = 0; i < lines.length; ++i)
- if (typeof lines[i] != "string") lines[i] = lines[i].text;
- var changeObj = {from: from, to: to, text: lines, origin: origin};
+ var changeObj = {from: from, to: to,
+ text: change.text,
+ removed: change.removed,
+ origin: change.origin};
if (cm.curOp.textChanged) {
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
cur.next = changeObj;
} else cm.curOp.textChanged = changeObj;
}
-
- // Update the selection
- var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
- ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
- if (selUpdate && typeof selUpdate != "string") {
- if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
- else newSelFrom = newSelTo = selUpdate;
- } else if (selUpdate == "end") {
- newSelFrom = newSelTo = end;
- } else if (selUpdate == "start") {
- newSelFrom = newSelTo = from;
- } else if (selUpdate == "around") {
- newSelFrom = from; newSelTo = end;
- } else {
- var adjustPos = function(pos) {
- if (posLess(pos, from)) return pos;
- if (!posLess(to, pos)) return end;
- var line = pos.line + lendiff;
- var ch = pos.ch;
- if (pos.line == to.line)
- ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
- return {line: line, ch: ch};
- };
- newSelFrom = adjustPos(view.sel.from);
- newSelTo = adjustPos(view.sel.to);
- }
- setSelection(cm, newSelFrom, newSelTo, null, true);
- return end;
}
- function replaceRange(cm, code, from, to, origin) {
+ function replaceRange(doc, code, from, to, origin) {
if (!to) to = from;
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
- return updateDoc(cm, from, to, splitLines(code), null, origin);
+ if (typeof code == "string") code = splitLines(code);
+ makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
}
- // SELECTION
+ // POSITION OBJECT
+
+ function Pos(line, ch) {
+ if (!(this instanceof Pos)) return new Pos(line, ch);
+ this.line = line; this.ch = ch;
+ }
+ CodeMirror.Pos = Pos;
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
- function copyPos(x) {return {line: x.line, ch: x.ch};}
+ function copyPos(x) {return Pos(x.line, x.ch);}
- function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));}
+ // SELECTION
+
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
function clipPos(doc, pos) {
- if (pos.line < 0) return {line: 0, ch: 0};
- if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length};
- var ch = pos.ch, linelen = getLine(doc, pos.line).text.length;
- if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
- else if (ch < 0) return {line: pos.line, ch: 0};
+ if (pos.line < doc.first) return Pos(doc.first, 0);
+ var last = doc.first + doc.size - 1;
+ if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
+ return clipToLen(pos, getLine(doc, pos.line).text.length);
+ }
+ function clipToLen(pos, linelen) {
+ var ch = pos.ch;
+ if (ch == null || ch > linelen) return Pos(pos.line, linelen);
+ else if (ch < 0) return Pos(pos.line, 0);
else return pos;
}
- function isLine(doc, l) {return l >= 0 && l < doc.size;}
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
// If shift is held, this will move the selection anchor. Otherwise,
// it'll set the whole selection.
- function extendSelection(cm, pos, other, bias) {
- var sel = cm.view.sel;
- if (sel.shift || sel.extend) {
- var anchor = sel.anchor;
+ function extendSelection(doc, pos, other, bias) {
+ if (doc.sel.shift || doc.sel.extend) {
+ var anchor = doc.sel.anchor;
if (other) {
var posBefore = posLess(pos, anchor);
if (posBefore != posLess(other, anchor)) {
anchor = pos;
pos = other;
} else if (posBefore != posLess(pos, other)) {
pos = other;
}
}
- setSelection(cm, anchor, pos, bias);
+ setSelection(doc, anchor, pos, bias);
} else {
- setSelection(cm, pos, other || pos, bias);
+ setSelection(doc, pos, other || pos, bias);
}
- cm.curOp.userSelChange = true;
+ if (doc.cm) doc.cm.curOp.userSelChange = true;
+ }
+
+ function filterSelectionChange(doc, anchor, head) {
+ var obj = {anchor: anchor, head: head};
+ signal(doc, "beforeSelectionChange", doc, obj);
+ if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
+ obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
+ return obj;
}
// Update the selection. Last two args are only used by
// updateDoc, since they have to be expressed in the line
// numbers before the update.
- function setSelection(cm, anchor, head, bias, checkAtomic) {
- cm.view.goalColumn = null;
- var sel = cm.view.sel;
+ function setSelection(doc, anchor, head, bias, checkAtomic) {
+ if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
+ var filtered = filterSelectionChange(doc, anchor, head);
+ head = filtered.head;
+ anchor = filtered.anchor;
+ }
+
+ var sel = doc.sel;
+ sel.goalColumn = null;
// Skip over atomic spans.
if (checkAtomic || !posEq(anchor, sel.anchor))
- anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push");
+ anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
if (checkAtomic || !posEq(head, sel.head))
- head = skipAtomic(cm, head, bias, checkAtomic != "push");
+ head = skipAtomic(doc, head, bias, checkAtomic != "push");
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
sel.anchor = anchor; sel.head = head;
var inv = posLess(head, anchor);
sel.from = inv ? head : anchor;
sel.to = inv ? anchor : head;
- cm.curOp.updateInput = true;
- cm.curOp.selectionChanged = true;
+ if (doc.cm)
+ doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
+ doc.cm.curOp.cursorActivity = true;
+
+ signalLater(doc, "cursorActivity", doc);
}
function reCheckSelection(cm) {
- setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push");
+ setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
}
- function skipAtomic(cm, pos, bias, mayClear) {
- var doc = cm.view.doc, flipped = false, curPos = pos;
+ function skipAtomic(doc, pos, bias, mayClear) {
+ var flipped = false, curPos = pos;
var dir = bias || 1;
- cm.view.cantEdit = false;
+ doc.cantEdit = false;
search: for (;;) {
- var line = getLine(doc, curPos.line), toClear;
+ var line = getLine(doc, curPos.line);
if (line.markedSpans) {
for (var i = 0; i < line.markedSpans.length; ++i) {
var sp = line.markedSpans[i], m = sp.marker;
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
- if (mayClear && m.clearOnEnter) {
- (toClear || (toClear = [])).push(m);
- continue;
- } else if (!m.atomic) continue;
+ if (mayClear) {
+ signal(m, "beforeCursorEnter");
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) break;
+ else {--i; continue;}
+ }
+ }
+ if (!m.atomic) continue;
var newPos = m.find()[dir < 0 ? "from" : "to"];
if (posEq(newPos, curPos)) {
newPos.ch += dir;
if (newPos.ch < 0) {
- if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1});
+ if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
else newPos = null;
} else if (newPos.ch > line.text.length) {
- if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0};
+ if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
else newPos = null;
}
if (!newPos) {
if (flipped) {
// Driven in a corner -- no valid cursor position found at all
// -- try again *with* clearing, if we didn't already
- if (!mayClear) return skipAtomic(cm, pos, bias, true);
+ if (!mayClear) return skipAtomic(doc, pos, bias, true);
// Otherwise, turn off editing until further notice, and return the start of the doc
- cm.view.cantEdit = true;
- return {line: 0, ch: 0};
+ doc.cantEdit = true;
+ return Pos(doc.first, 0);
}
flipped = true; newPos = pos; dir = -dir;
}
}
curPos = newPos;
continue search;
}
}
- if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
}
return curPos;
}
}
// SCROLLING
function scrollCursorIntoView(cm) {
- var view = cm.view;
- var coords = scrollPosIntoView(cm, view.sel.head);
- if (!view.focused) return;
- var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
+ if (!cm.state.focused) return;
+ var display = cm.display, box = getRect(display.sizer), doScroll = null;
if (coords.top + box.top < 0) doScroll = true;
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
if (doScroll != null && !phantom) {
var hidden = display.cursor.style.display == "none";
if (hidden) {
display.cursor.style.display = "";
display.cursor.style.left = coords.left + "px";
display.cursor.style.top = (coords.top - display.viewOffset) + "px";
}
display.cursor.scrollIntoView(doScroll);
if (hidden) display.cursor.style.display = "none";
}
}
- function scrollPosIntoView(cm, pos) {
+ function scrollPosIntoView(cm, pos, margin) {
+ if (margin == null) margin = 0;
for (;;) {
var changed = false, coords = cursorCoords(cm, pos);
- var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
- var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft;
+ var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
if (scrollPos.scrollTop != null) {
setScrollTop(cm, scrollPos.scrollTop);
- if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true;
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
}
if (scrollPos.scrollLeft != null) {
setScrollLeft(cm, scrollPos.scrollLeft);
- if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true;
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
}
if (!changed) return coords;
}
}
function scrollIntoView(cm, x1, y1, x2, y2) {
var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
}
function calculateScrollPos(cm, x1, y1, x2, y2) {
- var display = cm.display, pt = paddingTop(display);
- y1 += pt; y2 += pt;
+ var display = cm.display, snapMargin = textHeight(cm.display);
+ if (y1 < 0) y1 = 0;
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
- var docBottom = cm.view.doc.height + 2 * pt;
- var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
- if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
- else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
+ var docBottom = cm.doc.height + paddingVert(display);
+ var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
+ if (y1 < screentop) {
+ result.scrollTop = atTop ? 0 : y1;
+ } else if (y2 > screentop + screen) {
+ var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
+ if (newTop != screentop) result.scrollTop = newTop;
+ }
var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
var gutterw = display.gutters.offsetWidth;
var atLeft = x1 < gutterw + 10;
if (x1 < screenleft + gutterw || atLeft) {
if (atLeft) x1 = 0;
result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
} else if (x2 > screenw + screenleft - 3) {
result.scrollLeft = x2 + 10 - screenw;
}
return result;
}
+ function updateScrollPos(cm, left, top) {
+ cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
+ scrollTop: top == null ? cm.doc.scrollTop : top};
+ }
+
+ function addToScrollPos(cm, left, top) {
+ var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
+ var scroll = cm.display.scroller;
+ pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
+ pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
+ }
+
// API UTILITIES
function indentLine(cm, n, how, aggressive) {
- var doc = cm.view.doc;
- if (!how) how = "add";
+ var doc = cm.doc;
+ if (how == null) how = "add";
if (how == "smart") {
- if (!cm.view.mode.indent) how = "prev";
+ if (!cm.doc.mode.indent) how = "prev";
else var state = getStateBefore(cm, n);
}
var tabSize = cm.options.tabSize;
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
if (how == "smart") {
- indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
if (indentation == Pass) {
if (!aggressive) return;
how = "prev";
}
}
if (how == "prev") {
- if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
+ if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
else indentation = 0;
+ } else if (how == "add") {
+ indentation = curSpace + cm.options.indentUnit;
+ } else if (how == "subtract") {
+ indentation = curSpace - cm.options.indentUnit;
+ } else if (typeof how == "number") {
+ indentation = curSpace + how;
}
- else if (how == "add") indentation = curSpace + cm.options.indentUnit;
- else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
indentation = Math.max(0, indentation);
var indentString = "", pos = 0;
if (cm.options.indentWithTabs)
for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
if (pos < indentation) indentString += spaceStr(indentation - pos);
if (indentString != curSpaceString)
- replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input");
+ replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
line.stateAfter = null;
}
function changeLine(cm, handle, op) {
- var no = handle, line = handle, doc = cm.view.doc;
+ var no = handle, line = handle, doc = cm.doc;
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
else no = lineNo(handle);
if (no == null) return null;
if (op(line, no)) regChange(cm, no, no + 1);
else return null;
return line;
}
- function findPosH(cm, dir, unit, visually) {
- var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch;
+ function findPosH(doc, pos, dir, unit, visually) {
+ var line = pos.line, ch = pos.ch, origDir = dir;
var lineObj = getLine(doc, line);
+ var possible = true;
function findNextLine() {
var l = line + dir;
- if (l < 0 || l == doc.size) return false;
+ if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
line = l;
return lineObj = getLine(doc, l);
}
function moveOnce(boundToLine) {
var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true);
if (next == null) {
if (!boundToLine && findNextLine()) {
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
else ch = dir < 0 ? lineObj.text.length : 0;
- } else return false;
+ } else return (possible = false);
} else ch = next;
return true;
}
+
if (unit == "char") moveOnce();
else if (unit == "column") moveOnce(true);
- else if (unit == "word") {
- var sawWord = false;
- for (;;) {
- if (dir < 0) if (!moveOnce()) break;
- if (isWordChar(lineObj.text.charAt(ch))) sawWord = true;
- else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;}
- if (dir > 0) if (!moveOnce()) break;
+ else if (unit == "word" || unit == "group") {
+ var sawType = null, group = unit == "group";
+ for (var first = true;; first = false) {
+ if (dir < 0 && !moveOnce(!first)) break;
+ var cur = lineObj.text.charAt(ch) || "\n";
+ var type = isWordChar(cur) ? "w"
+ : !group ? null
+ : /\s/.test(cur) ? null
+ : "p";
+ if (sawType && sawType != type) {
+ if (dir < 0) {dir = 1; moveOnce();}
+ break;
+ }
+ if (type) sawType = type;
+ if (dir > 0 && !moveOnce(!first)) break;
}
}
- return skipAtomic(cm, {line: line, ch: ch}, dir, true);
+ var result = skipAtomic(doc, Pos(line, ch), origDir, true);
+ if (!possible) result.hitSide = true;
+ return result;
+ }
+
+ function findPosV(cm, pos, dir, unit) {
+ var doc = cm.doc, x = pos.left, y;
+ if (unit == "page") {
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+ y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ for (;;) {
+ var target = coordsChar(cm, x, y);
+ if (!target.outside) break;
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
+ y += dir * 5;
+ }
+ return target;
}
function findWordAt(line, pos) {
var start = pos.ch, end = pos.ch;
if (line) {
- if (pos.after === false || end == line.length) --start; else ++end;
+ if (pos.xRel < 0 || end == line.length) --start; else ++end;
var startChar = line.charAt(start);
- var check = isWordChar(startChar) ? isWordChar :
- /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} :
- function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
+ var check = isWordChar(startChar) ? isWordChar
+ : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
+ : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
while (start > 0 && check(line.charAt(start - 1))) --start;
while (end < line.length && check(line.charAt(end))) ++end;
}
- return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
}
function selectLine(cm, line) {
- extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0}));
+ extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
}
// PROTOTYPE
// The publicly visible API. Note that operation(null, f) means
// 'wrap f in an operation, performed on its `this` parameter'
CodeMirror.prototype = {
- getValue: function(lineSep) {
- var text = [], doc = this.view.doc;
- doc.iter(0, doc.size, function(line) { text.push(line.text); });
- return text.join(lineSep || "\n");
- },
-
- setValue: operation(null, function(code) {
- var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
- updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
- }),
-
- getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
-
- replaceSelection: operation(null, function(code, collapse, origin) {
- var sel = this.view.sel;
- updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
- }),
-
+ constructor: CodeMirror,
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
setOption: function(option, value) {
var options = this.options, old = options[option];
if (options[option] == value && option != "mode") return;
options[option] = value;
if (optionHandlers.hasOwnProperty(option))
operation(this, optionHandlers[option])(this, value, old);
},
getOption: function(option) {return this.options[option];},
+ getDoc: function() {return this.doc;},
- getMode: function() {return this.view.mode;},
-
- addKeyMap: function(map) {
- this.view.keyMaps.push(map);
+ addKeyMap: function(map, bottom) {
+ this.state.keyMaps[bottom ? "push" : "unshift"](map);
},
-
removeKeyMap: function(map) {
- var maps = this.view.keyMaps;
+ var maps = this.state.keyMaps;
for (var i = 0; i < maps.length; ++i)
if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
maps.splice(i, 1);
return true;
}
},
addOverlay: operation(null, function(spec, options) {
var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
if (mode.startState) throw new Error("Overlays may not be stateful.");
- this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
- this.view.modeGen++;
- regChange(this, 0, this.view.doc.size);
+ this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
+ this.state.modeGen++;
+ regChange(this);
}),
removeOverlay: operation(null, function(spec) {
- var overlays = this.view.overlays;
+ var overlays = this.state.overlays;
for (var i = 0; i < overlays.length; ++i) {
- if (overlays[i].modeSpec == spec) {
+ var cur = overlays[i].modeSpec;
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
overlays.splice(i, 1);
- this.view.modeGen++;
- regChange(this, 0, this.view.doc.size);
+ this.state.modeGen++;
+ regChange(this);
return;
}
}
}),
- undo: operation(null, function() {unredoHelper(this, "undo");}),
- redo: operation(null, function() {unredoHelper(this, "redo");}),
-
indentLine: operation(null, function(n, dir, aggressive) {
- if (typeof dir != "string") {
+ if (typeof dir != "string" && typeof dir != "number") {
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
else dir = dir ? "add" : "subtract";
}
- if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive);
+ if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
}),
-
indentSelection: operation(null, function(how) {
- var sel = this.view.sel;
+ var sel = this.doc.sel;
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
var e = sel.to.line - (sel.to.ch ? 0 : 1);
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
}),
- historySize: function() {
- var hist = this.view.history;
- return {undo: hist.done.length, redo: hist.undone.length};
- },
-
- clearHistory: function() {this.view.history = makeHistory();},
-
- markClean: function() {
- this.view.history.dirtyCounter = 0;
- this.view.history.lastOp = this.view.history.lastOrigin = null;
- },
-
- isClean: function () {return this.view.history.dirtyCounter == 0;},
-
- getHistory: function() {
- var hist = this.view.history;
- function cp(arr) {
- for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
- var set = arr[i];
- nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
- fromAfter: set.fromAfter, toAfter: set.toAfter});
- for (var j = 0, elt = set.events; j < elt.length; ++j) {
- var old = [], cur = elt[j];
- nwelt.push({start: cur.start, added: cur.added, old: old});
- for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
- }
- }
- return nw;
- }
- return {done: cp(hist.done), undone: cp(hist.undone)};
- },
-
- setHistory: function(histData) {
- var hist = this.view.history = makeHistory();
- hist.done = histData.done;
- hist.undone = histData.undone;
- },
-
// Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion).
- getTokenAt: function(pos) {
- var doc = this.view.doc;
+ getTokenAt: function(pos, precise) {
+ var doc = this.doc;
pos = clipPos(doc, pos);
- var state = getStateBefore(this, pos.line), mode = this.view.mode;
+ var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
var line = getLine(doc, pos.line);
var stream = new StringStream(line.text, this.options.tabSize);
while (stream.pos < pos.ch && !stream.eol()) {
stream.start = stream.pos;
var style = mode.token(stream, state);
}
return {start: stream.start,
end: stream.pos,
string: stream.current(),
className: style || null, // Deprecated, use 'type' instead
type: style || null,
state: state};
},
- getStateAfter: function(line) {
- var doc = this.view.doc;
- line = clipLine(doc, line == null ? doc.size - 1: line);
- return getStateBefore(this, line + 1);
+ getTokenTypeAt: function(pos) {
+ pos = clipPos(this.doc, pos);
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
+ for (;;) {
+ var mid = (before + after) >> 1;
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid;
+ else if (styles[mid * 2 + 1] < ch) before = mid + 1;
+ else return styles[mid * 2 + 2];
+ }
+ },
+
+ getStateAfter: function(line, precise) {
+ var doc = this.doc;
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
+ return getStateBefore(this, line + 1, precise);
},
cursorCoords: function(start, mode) {
- var pos, sel = this.view.sel;
+ var pos, sel = this.doc.sel;
if (start == null) pos = sel.head;
- else if (typeof start == "object") pos = clipPos(this.view.doc, start);
+ else if (typeof start == "object") pos = clipPos(this.doc, start);
else pos = start ? sel.from : sel.to;
return cursorCoords(this, pos, mode || "page");
},
charCoords: function(pos, mode) {
- return charCoords(this, clipPos(this.view.doc, pos), mode || "page");
+ return charCoords(this, clipPos(this.doc, pos), mode || "page");
},
- coordsChar: function(coords) {
- var off = this.display.lineSpace.getBoundingClientRect();
- return coordsChar(this, coords.left - off.left, coords.top - off.top);
+ coordsChar: function(coords, mode) {
+ coords = fromCoordSystem(this, coords, mode || "page");
+ return coordsChar(this, coords.left, coords.top);
},
- defaultTextHeight: function() { return textHeight(this.display); },
-
- markText: operation(null, function(from, to, options) {
- return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
- options, "range");
- }),
-
- setBookmark: operation(null, function(pos, widget) {
- pos = clipPos(this.view.doc, pos);
- return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
- }),
-
- findMarksAt: function(pos) {
- var doc = this.view.doc;
- pos = clipPos(doc, pos);
- var markers = [], spans = getLine(doc, pos.line).markedSpans;
- if (spans) for (var i = 0; i < spans.length; ++i) {
- var span = spans[i];
- if ((span.from == null || span.from <= pos.ch) &&
- (span.to == null || span.to >= pos.ch))
- markers.push(span.marker);
- }
- return markers;
+ lineAtHeight: function(height, mode) {
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
+ return lineAtHeight(this.doc, height + this.display.viewOffset);
+ },
+ heightAtLine: function(line, mode) {
+ var end = false, last = this.doc.first + this.doc.size - 1;
+ if (line < this.doc.first) line = this.doc.first;
+ else if (line > last) { line = last; end = true; }
+ var lineObj = getLine(this.doc, line);
+ return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || "page").top +
+ (end ? lineObj.height : 0);
},
+ defaultTextHeight: function() { return textHeight(this.display); },
+ defaultCharWidth: function() { return charWidth(this.display); },
+
setGutterMarker: operation(null, function(line, gutterID, value) {
return changeLine(this, line, function(line) {
var markers = line.gutterMarkers || (line.gutterMarkers = {});
markers[gutterID] = value;
if (!value && isEmpty(markers)) line.gutterMarkers = null;
return true;
});
}),
clearGutter: operation(null, function(gutterID) {
- var i = 0, cm = this, doc = cm.view.doc;
- doc.iter(0, doc.size, function(line) {
+ var cm = this, doc = cm.doc, i = doc.first;
+ doc.iter(function(line) {
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
line.gutterMarkers[gutterID] = null;
regChange(cm, i, i + 1);
if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
}
++i;
});
}),
addLineClass: operation(null, function(handle, where, cls) {
return changeLine(this, handle, function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
if (!line[prop]) line[prop] = cls;
- else if (new RegExp("\\b" + cls + "\\b").test(line[prop])) return false;
+ else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false;
else line[prop] += " " + cls;
return true;
});
}),
removeLineClass: operation(null, function(handle, where, cls) {
return changeLine(this, handle, function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass";
var cur = line[prop];
if (!cur) return false;
else if (cls == null) line[prop] = null;
else {
- var upd = cur.replace(new RegExp("^" + cls + "\\b\\s*|\\s*\\b" + cls + "\\b"), "");
- if (upd == cur) return false;
- line[prop] = upd || null;
+ var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)"));
+ if (!found) return false;
+ var end = found.index + found[0].length;
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
}
return true;
});
}),
addLineWidget: operation(null, function(handle, node, options) {
return addLineWidget(this, handle, node, options);
}),
removeLineWidget: function(widget) { widget.clear(); },
lineInfo: function(line) {
if (typeof line == "number") {
- if (!isLine(this.view.doc, line)) return null;
+ if (!isLine(this.doc, line)) return null;
var n = line;
- line = getLine(this.view.doc, line);
+ line = getLine(this.doc, line);
if (!line) return null;
} else {
var n = lineNo(line);
if (n == null) return null;
}
return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
widgets: line.widgets};
},
getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
addWidget: function(pos, node, scroll, vert, horiz) {
var display = this.display;
- pos = cursorCoords(this, clipPos(this.view.doc, pos));
- var top = pos.top, left = pos.left;
+ pos = cursorCoords(this, clipPos(this.doc, pos));
+ var top = pos.bottom, left = pos.left;
node.style.position = "absolute";
display.sizer.appendChild(node);
- if (vert == "over") top = pos.top;
- else if (vert == "near") {
- var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height),
+ if (vert == "over") {
+ top = pos.top;
+ } else if (vert == "above" || vert == "near") {
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
- if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight)
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
top = pos.top - node.offsetHeight;
+ else if (pos.bottom + node.offsetHeight <= vspace)
+ top = pos.bottom;
if (left + node.offsetWidth > hspace)
left = hspace - node.offsetWidth;
}
- node.style.top = (top + paddingTop(display)) + "px";
+ node.style.top = top + "px";
node.style.left = node.style.right = "";
if (horiz == "right") {
left = display.sizer.clientWidth - node.offsetWidth;
node.style.right = "0px";
} else {
if (horiz == "left") left = 0;
else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2;
node.style.left = left + "px";
}
if (scroll)
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
},
- lineCount: function() {return this.view.doc.size;},
-
- clipPos: function(pos) {return clipPos(this.view.doc, pos);},
-
- getCursor: function(start) {
- var sel = this.view.sel, pos;
- if (start == null || start == "head") pos = sel.head;
- else if (start == "anchor") pos = sel.anchor;
- else if (start == "end" || start === false) pos = sel.to;
- else pos = sel.from;
- return copyPos(pos);
- },
-
- somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
-
- setCursor: operation(null, function(line, ch, extend) {
- var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
- if (extend) extendSelection(this, pos);
- else setSelection(this, pos, pos);
- }),
-
- setSelection: operation(null, function(anchor, head) {
- var doc = this.view.doc;
- setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
- }),
-
- extendSelection: operation(null, function(from, to) {
- var doc = this.view.doc;
- extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
- }),
-
- setExtending: function(val) {this.view.sel.extend = val;},
-
- getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
-
- getLineHandle: function(line) {
- var doc = this.view.doc;
- if (isLine(doc, line)) return getLine(doc, line);
- },
-
- getLineNumber: function(line) {return lineNo(line);},
-
- setLine: operation(null, function(line, text) {
- if (isLine(this.view.doc, line))
- replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
- }),
-
- removeLine: operation(null, function(line) {
- if (isLine(this.view.doc, line))
- replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
- }),
-
- replaceRange: operation(null, function(code, from, to) {
- var doc = this.view.doc;
- from = clipPos(doc, from);
- to = to ? clipPos(doc, to) : from;
- return replaceRange(this, code, from, to);
- }),
-
- getRange: function(from, to, lineSep) {
- var doc = this.view.doc;
- from = clipPos(doc, from); to = clipPos(doc, to);
- var l1 = from.line, l2 = to.line;
- if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
- var code = [getLine(doc, l1).text.slice(from.ch)];
- doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
- code.push(getLine(doc, l2).text.slice(0, to.ch));
- return code.join(lineSep || "\n");
- },
-
triggerOnKeyDown: operation(null, onKeyDown),
execCommand: function(cmd) {return commands[cmd](this);},
- // Stuff used by commands, probably not much use to outside code.
+ findPosH: function(from, amount, unit, visually) {
+ var dir = 1;
+ if (amount < 0) { dir = -1; amount = -amount; }
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually);
+ if (cur.hitSide) break;
+ }
+ return cur;
+ },
+
moveH: operation(null, function(dir, unit) {
- var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to;
+ var sel = this.doc.sel, pos;
if (sel.shift || sel.extend || posEq(sel.from, sel.to))
- pos = findPosH(this, dir, unit, this.options.rtlMoveVisually);
- extendSelection(this, pos, pos, dir);
+ pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
+ else
+ pos = dir < 0 ? sel.from : sel.to;
+ extendSelection(this.doc, pos, pos, dir);
}),
deleteH: operation(null, function(dir, unit) {
- var sel = this.view.sel;
- if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
- else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
+ var sel = this.doc.sel;
+ if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
+ else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
this.curOp.userSelChange = true;
}),
- moveV: operation(null, function(dir, unit) {
- var view = this.view, doc = view.doc, display = this.display;
- var cur = view.sel.head, pos = cursorCoords(this, cur, "div");
- var x = pos.left, y;
- if (view.goalColumn != null) x = view.goalColumn;
- if (unit == "page") {
- var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
- y = pos.top + dir * pageSize;
- } else if (unit == "line") {
- y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ findPosV: function(from, amount, unit, goalColumn) {
+ var dir = 1, x = goalColumn;
+ if (amount < 0) { dir = -1; amount = -amount; }
+ for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
+ var coords = cursorCoords(this, cur, "div");
+ if (x == null) x = coords.left;
+ else coords.left = x;
+ cur = findPosV(this, coords, dir, unit);
+ if (cur.hitSide) break;
}
- do {
- var target = coordsChar(this, x, y);
- y += dir * 5;
- } while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
-
- if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top;
- extendSelection(this, target, target, dir);
- view.goalColumn = x;
+ return cur;
+ },
+
+ moveV: operation(null, function(dir, unit) {
+ var sel = this.doc.sel;
+ var pos = cursorCoords(this, sel.head, "div");
+ if (sel.goalColumn != null) pos.left = sel.goalColumn;
+ var target = findPosV(this, pos, dir, unit);
+
+ if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
+ extendSelection(this.doc, target, target, dir);
+ sel.goalColumn = pos.left;
}),
- toggleOverwrite: function() {
- if (this.view.overwrite = !this.view.overwrite)
+ toggleOverwrite: function(value) {
+ if (value != null && value == this.state.overwrite) return;
+ if (this.state.overwrite = !this.state.overwrite)
this.display.cursor.className += " CodeMirror-overwrite";
else
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
},
+ hasFocus: function() { return this.state.focused; },
- posFromIndex: function(off) {
- var lineNo = 0, ch, doc = this.view.doc;
- doc.iter(0, doc.size, function(line) {
- var sz = line.text.length + 1;
- if (sz > off) { ch = off; return true; }
- off -= sz;
- ++lineNo;
- });
- return clipPos(doc, {line: lineNo, ch: ch});
- },
- indexFromPos: function (coords) {
- coords = clipPos(this.view.doc, coords);
- var index = coords.ch;
- this.view.doc.iter(0, coords.line, function (line) {
- index += line.text.length + 1;
- });
- return index;
- },
-
- scrollTo: function(x, y) {
- if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
- if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
- updateDisplay(this, []);
- },
+ scrollTo: operation(null, function(x, y) {
+ updateScrollPos(this, x, y);
+ }),
getScrollInfo: function() {
var scroller = this.display.scroller, co = scrollerCutOff;
return {left: scroller.scrollLeft, top: scroller.scrollTop,
height: scroller.scrollHeight - co, width: scroller.scrollWidth - co,
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
},
- scrollIntoView: function(pos) {
- if (typeof pos == "number") pos = {line: pos, ch: 0};
+ scrollIntoView: operation(null, function(pos, margin) {
+ if (typeof pos == "number") pos = Pos(pos, 0);
+ if (!margin) margin = 0;
+ var coords = pos;
+
if (!pos || pos.line != null) {
- pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head;
- scrollPosIntoView(this, pos);
- } else {
- scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom);
+ this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
+ this.curOp.scrollToPosMargin = margin;
+ coords = cursorCoords(this, this.curOp.scrollToPos);
}
- },
+ var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
+ updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
+ }),
setSize: function(width, height) {
function interpret(val) {
return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val;
}
if (width != null) this.display.wrapper.style.width = interpret(width);
if (height != null) this.display.wrapper.style.height = interpret(height);
this.refresh();
},
on: function(type, f) {on(this, type, f);},
off: function(type, f) {off(this, type, f);},
- operation: function(f){return operation(this, f)();},
+ operation: function(f){return runInOp(this, f);},
- refresh: function() {
+ refresh: operation(null, function() {
clearCaches(this);
- var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft;
- if (this.display.scroller.scrollHeight > sTop)
- this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop;
- if (this.display.scroller.scrollWidth > sLeft)
- this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft;
- updateDisplay(this, true);
- },
+ updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
+ regChange(this);
+ }),
+
+ swapDoc: operation(null, function(doc) {
+ var old = this.doc;
+ old.cm = null;
+ attachDoc(this, doc);
+ clearCaches(this);
+ resetInput(this, true);
+ updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
+ return old;
+ }),
getInputField: function(){return this.display.input;},
getWrapperElement: function(){return this.display.wrapper;},
getScrollerElement: function(){return this.display.scroller;},
getGutterElement: function(){return this.display.gutters;}
};
// OPTION DEFAULTS
var optionHandlers = CodeMirror.optionHandlers = {};
// The default configuration options.
var defaults = CodeMirror.defaults = {};
function option(name, deflt, handle, notOnInit) {
CodeMirror.defaults[name] = deflt;
if (handle) optionHandlers[name] =
notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
}
var Init = CodeMirror.Init = {toString: function(){return "CodeMirror.Init";}};
// These two are, on init, called from the constructor because they
// have to be initialized before the editor can start at all.
- option("value", "", function(cm, val) {cm.setValue(val);}, true);
- option("mode", null, loadMode, true);
+ option("value", "", function(cm, val) {
+ cm.setValue(val);
+ }, true);
+ option("mode", null, function(cm, val) {
+ cm.doc.modeOption = val;
+ loadMode(cm);
+ }, true);
option("indentUnit", 2, loadMode, true);
option("indentWithTabs", false);
option("smartIndent", true);
option("tabSize", 4, function(cm) {
loadMode(cm);
clearCaches(cm);
- updateDisplay(cm, true);
+ regChange(cm);
}, true);
option("electricChars", true);
option("rtlMoveVisually", !windows);
option("theme", "default", function(cm) {
themeChanged(cm);
guttersChanged(cm);
}, true);
option("keyMap", "default", keyMapChanged);
option("extraKeys", null);
option("onKeyEvent", null);
option("onDragEvent", null);
option("lineWrapping", false, wrappingChanged, true);
option("gutters", [], function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
}, true);
option("fixedGutter", true, function(cm, val) {
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
cm.refresh();
}, true);
+ option("coverGutterNextToScrollbar", false, updateScrollbars, true);
option("lineNumbers", false, function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
}, true);
option("firstLineNumber", 1, guttersChanged, true);
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
option("showCursorWhenSelecting", false, updateSelection, true);
-
+
option("readOnly", false, function(cm, val) {
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
else if (!val) resetInput(cm, true);
});
option("dragDrop", true);
option("cursorBlinkRate", 530);
+ option("cursorScrollMargin", 0);
option("cursorHeight", 1);
option("workTime", 100);
option("workDelay", 100);
option("flattenSpans", true);
option("pollInterval", 100);
- option("undoDepth", 40);
+ option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
+ option("historyEventDelay", 500);
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
+ option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true);
+ option("moveInputWithCursor", true, function(cm, val) {
+ if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
+ });
option("tabindex", null, function(cm, val) {
cm.display.input.tabIndex = val || "";
});
option("autofocus", null);
// MODE DEFINITION AND QUERYING
// Known modes, by name and by MIME
var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
CodeMirror.defineMode = function(name, mode) {
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
if (arguments.length > 2) {
mode.dependencies = [];
for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
}
modes[name] = mode;
};
CodeMirror.defineMIME = function(mime, spec) {
mimeModes[mime] = spec;
};
CodeMirror.resolveMode = function(spec) {
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
spec = mimeModes[spec];
- else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+ var found = mimeModes[spec.name];
+ spec = createObj(found, spec);
+ spec.name = found.name;
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
return CodeMirror.resolveMode("application/xml");
+ }
if (typeof spec == "string") return {name: spec};
else return spec || {name: "null"};
};
CodeMirror.getMode = function(options, spec) {
spec = CodeMirror.resolveMode(spec);
var mfactory = modes[spec.name];
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
var modeObj = mfactory(options, spec);
if (modeExtensions.hasOwnProperty(spec.name)) {
var exts = modeExtensions[spec.name];
for (var prop in exts) {
if (!exts.hasOwnProperty(prop)) continue;
if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
modeObj[prop] = exts[prop];
}
}
modeObj.name = spec.name;
return modeObj;
};
CodeMirror.defineMode("null", function() {
return {token: function(stream) {stream.skipToEnd();}};
});
CodeMirror.defineMIME("text/plain", "null");
var modeExtensions = CodeMirror.modeExtensions = {};
CodeMirror.extendMode = function(mode, properties) {
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
- for (var prop in properties) if (properties.hasOwnProperty(prop))
- exts[prop] = properties[prop];
+ copyObj(properties, exts);
};
// EXTENSIONS
CodeMirror.defineExtension = function(name, func) {
CodeMirror.prototype[name] = func;
};
-
+ CodeMirror.defineDocExtension = function(name, func) {
+ Doc.prototype[name] = func;
+ };
CodeMirror.defineOption = option;
var initHooks = [];
CodeMirror.defineInitHook = function(f) {initHooks.push(f);};
// MODE STATE HANDLING
// Utility functions for working with state. Exported because modes
// sometimes need to do this.
function copyState(mode, state) {
if (state === true) return state;
if (mode.copyState) return mode.copyState(state);
var nstate = {};
for (var n in state) {
var val = state[n];
if (val instanceof Array) val = val.concat([]);
nstate[n] = val;
}
return nstate;
}
CodeMirror.copyState = copyState;
function startState(mode, a1, a2) {
return mode.startState ? mode.startState(a1, a2) : true;
}
CodeMirror.startState = startState;
CodeMirror.innerMode = function(mode, state) {
while (mode.innerMode) {
var info = mode.innerMode(state);
state = info.state;
mode = info.mode;
}
return info || {mode: mode, state: state};
};
// STANDARD COMMANDS
var commands = CodeMirror.commands = {
- selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
+ selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
killLine: function(cm) {
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
if (!sel && cm.getLine(from.line).length == from.ch)
- cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "delete");
- else cm.replaceRange("", from, sel ? to : {line: from.line}, "delete");
+ cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
+ else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
},
deleteLine: function(cm) {
var l = cm.getCursor().line;
- cm.replaceRange("", {line: l, ch: 0}, {line: l}, "delete");
+ cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
+ },
+ delLineLeft: function(cm) {
+ var cur = cm.getCursor();
+ cm.replaceRange("", Pos(cur.line, 0), cur, "+delete");
},
undo: function(cm) {cm.undo();},
redo: function(cm) {cm.redo();},
- goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});},
- goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});},
+ goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
+ goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
goLineStart: function(cm) {
cm.extendSelection(lineStart(cm, cm.getCursor().line));
},
goLineStartSmart: function(cm) {
var cur = cm.getCursor(), start = lineStart(cm, cur.line);
var line = cm.getLineHandle(start.line);
var order = getOrder(line);
if (!order || order[0].level == 0) {
var firstNonWS = Math.max(0, line.text.search(/\S/));
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
- cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS});
+ cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
} else cm.extendSelection(start);
},
goLineEnd: function(cm) {
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
},
+ goLineRight: function(cm) {
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+ cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
+ },
+ goLineLeft: function(cm) {
+ var top = cm.charCoords(cm.getCursor(), "div").top + 5;
+ cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
+ },
goLineUp: function(cm) {cm.moveV(-1, "line");},
goLineDown: function(cm) {cm.moveV(1, "line");},
goPageUp: function(cm) {cm.moveV(-1, "page");},
goPageDown: function(cm) {cm.moveV(1, "page");},
goCharLeft: function(cm) {cm.moveH(-1, "char");},
goCharRight: function(cm) {cm.moveH(1, "char");},
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
goColumnRight: function(cm) {cm.moveH(1, "column");},
goWordLeft: function(cm) {cm.moveH(-1, "word");},
+ goGroupRight: function(cm) {cm.moveH(1, "group");},
+ goGroupLeft: function(cm) {cm.moveH(-1, "group");},
goWordRight: function(cm) {cm.moveH(1, "word");},
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
delCharAfter: function(cm) {cm.deleteH(1, "char");},
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
delWordAfter: function(cm) {cm.deleteH(1, "word");},
+ delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
+ delGroupAfter: function(cm) {cm.deleteH(1, "group");},
indentAuto: function(cm) {cm.indentSelection("smart");},
indentMore: function(cm) {cm.indentSelection("add");},
indentLess: function(cm) {cm.indentSelection("subtract");},
- insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
+ insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
defaultTab: function(cm) {
if (cm.somethingSelected()) cm.indentSelection("add");
- else cm.replaceSelection("\t", "end", "input");
+ else cm.replaceSelection("\t", "end", "+input");
},
transposeChars: function(cm) {
var cur = cm.getCursor(), line = cm.getLine(cur.line);
if (cur.ch > 0 && cur.ch < line.length - 1)
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
- {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1});
+ Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
},
newlineAndIndent: function(cm) {
operation(cm, function() {
- cm.replaceSelection("\n", "end", "input");
+ cm.replaceSelection("\n", "end", "+input");
cm.indentLine(cm.getCursor().line, null, true);
})();
},
toggleOverwrite: function(cm) {cm.toggleOverwrite();}
};
// STANDARD KEYMAPS
var keyMap = CodeMirror.keyMap = {};
keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
"Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
};
// Note that the save and find-related commands aren't defined by
// default. Unknown commands are simply ignored.
keyMap.pcDefault = {
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
- "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
- "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
fallthrough: "basic"
};
keyMap.macDefault = {
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft",
- "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore",
- "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
- "Cmd-[": "indentLess", "Cmd-]": "indentMore",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
fallthrough: ["basic", "emacsy"]
};
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
keyMap.emacsy = {
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
};
// KEYMAP DISPATCH
function getKeyMap(val) {
if (typeof val == "string") return keyMap[val];
else return val;
}
- function lookupKey(name, maps, handle, stop) {
+ function lookupKey(name, maps, handle) {
function lookup(map) {
map = getKeyMap(map);
var found = map[name];
- if (found === false) {
- if (stop) stop();
- return true;
- }
+ if (found === false) return "stop";
if (found != null && handle(found)) return true;
- if (map.nofallthrough) {
- if (stop) stop();
- return true;
- }
+ if (map.nofallthrough) return "stop";
+
var fallthrough = map.fallthrough;
if (fallthrough == null) return false;
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
return lookup(fallthrough);
for (var i = 0, e = fallthrough.length; i < e; ++i) {
- if (lookup(fallthrough[i])) return true;
+ var done = lookup(fallthrough[i]);
+ if (done) return done;
}
return false;
}
- for (var i = 0; i < maps.length; ++i)
- if (lookup(maps[i])) return true;
+ for (var i = 0; i < maps.length; ++i) {
+ var done = lookup(maps[i]);
+ if (done) return done != "stop";
+ }
}
function isModifierKey(event) {
- var name = keyNames[e_prop(event, "keyCode")];
+ var name = keyNames[event.keyCode];
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
}
+ function keyName(event, noShift) {
+ if (opera && event.keyCode == 34 && event["char"]) return false;
+ var name = keyNames[event.keyCode];
+ if (name == null || event.altGraphKey) return false;
+ if (event.altKey) name = "Alt-" + name;
+ if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
+ if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
+ if (!noShift && event.shiftKey) name = "Shift-" + name;
+ return name;
+ }
+ CodeMirror.lookupKey = lookupKey;
CodeMirror.isModifierKey = isModifierKey;
+ CodeMirror.keyName = keyName;
// FROMTEXTAREA
CodeMirror.fromTextArea = function(textarea, options) {
if (!options) options = {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabindex)
options.tabindex = textarea.tabindex;
+ if (!options.placeholder && textarea.placeholder)
+ options.placeholder = textarea.placeholder;
// Set autofocus to true if this textarea is focused, or if it has
// autofocus and no other element is focused.
if (options.autofocus == null) {
var hasFocus = document.body;
// doc.activeElement occasionally throws on IE
try { hasFocus = document.activeElement; } catch(e) {}
options.autofocus = hasFocus == textarea ||
textarea.getAttribute("autofocus") != null && hasFocus == document.body;
}
function save() {textarea.value = cm.getValue();}
if (textarea.form) {
- // Deplorable hack to make the submit method do the right thing.
on(textarea.form, "submit", save);
- var form = textarea.form, realSubmit = form.submit;
- try {
- form.submit = function wrappedSubmit() {
- save();
- form.submit = realSubmit;
- form.submit();
- form.submit = wrappedSubmit;
- };
- } catch(e) {}
+ // Deplorable hack to make the submit method do the right thing.
+ if (!options.leaveSubmitMethodAlone) {
+ var form = textarea.form, realSubmit = form.submit;
+ try {
+ var wrappedSubmit = form.submit = function() {
+ save();
+ form.submit = realSubmit;
+ form.submit();
+ form.submit = wrappedSubmit;
+ };
+ } catch(e) {}
+ }
}
textarea.style.display = "none";
var cm = CodeMirror(function(node) {
textarea.parentNode.insertBefore(node, textarea.nextSibling);
}, options);
cm.save = save;
cm.getTextArea = function() { return textarea; };
cm.toTextArea = function() {
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (typeof textarea.form.submit == "function")
textarea.form.submit = realSubmit;
}
};
return cm;
};
// STRING STREAM
// Fed to the mode parsers, provides helper functions to make
// parsers more succinct.
// The character stream used by a mode's parser.
function StringStream(string, tabSize) {
this.pos = this.start = 0;
this.string = string;
this.tabSize = tabSize || 8;
+ this.lastColumnPos = this.lastColumnValue = 0;
}
StringStream.prototype = {
eol: function() {return this.pos >= this.string.length;},
sol: function() {return this.pos == 0;},
peek: function() {return this.string.charAt(this.pos) || undefined;},
next: function() {
if (this.pos < this.string.length)
return this.string.charAt(this.pos++);
},
eat: function(match) {
var ch = this.string.charAt(this.pos);
if (typeof match == "string") var ok = ch == match;
else var ok = ch && (match.test ? match.test(ch) : match(ch));
if (ok) {++this.pos; return ch;}
},
eatWhile: function(match) {
var start = this.pos;
while (this.eat(match)){}
return this.pos > start;
},
eatSpace: function() {
var start = this.pos;
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
return this.pos > start;
},
skipToEnd: function() {this.pos = this.string.length;},
skipTo: function(ch) {
var found = this.string.indexOf(ch, this.pos);
if (found > -1) {this.pos = found; return true;}
},
backUp: function(n) {this.pos -= n;},
- column: function() {return countColumn(this.string, this.start, this.tabSize);},
+ column: function() {
+ if (this.lastColumnPos < this.start) {
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+ this.lastColumnPos = this.start;
+ }
+ return this.lastColumnValue;
+ },
indentation: function() {return countColumn(this.string, null, this.tabSize);},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
- if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+ var substr = this.string.substr(this.pos, pattern.length);
+ if (cased(substr) == cased(pattern)) {
if (consume !== false) this.pos += pattern.length;
return true;
}
} else {
var match = this.string.slice(this.pos).match(pattern);
if (match && match.index > 0) return null;
if (match && consume !== false) this.pos += match[0].length;
return match;
}
},
current: function(){return this.string.slice(this.start, this.pos);}
};
CodeMirror.StringStream = StringStream;
// TEXTMARKERS
- function TextMarker(cm, type) {
+ function TextMarker(doc, type) {
this.lines = [];
this.type = type;
- this.cm = cm;
+ this.doc = doc;
}
CodeMirror.TextMarker = TextMarker;
TextMarker.prototype.clear = function() {
if (this.explicitlyCleared) return;
- startOperation(this.cm);
- var view = this.cm.view, min = null, max = null;
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
+ if (withOp) startOperation(cm);
+ var min = null, max = null;
for (var i = 0; i < this.lines.length; ++i) {
var line = this.lines[i];
var span = getMarkedSpanFor(line.markedSpans, this);
if (span.to != null) max = lineNo(line);
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
if (span.from != null)
min = lineNo(line);
- else if (this.collapsed && !lineIsHidden(line))
- updateLineHeight(line, textHeight(this.cm.display));
+ else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
+ updateLineHeight(line, textHeight(cm.display));
}
- if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
- var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual);
- if (len > view.maxLineLength) {
- view.maxLine = visual;
- view.maxLineLength = len;
- view.maxLineChanged = true;
+ if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
+ var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
+ if (len > cm.display.maxLineLength) {
+ cm.display.maxLine = visual;
+ cm.display.maxLineLength = len;
+ cm.display.maxLineChanged = true;
}
}
- if (min != null) regChange(this.cm, min, max + 1);
+ if (min != null && cm) regChange(cm, min, max + 1);
this.lines.length = 0;
this.explicitlyCleared = true;
- if (this.collapsed && this.cm.view.cantEdit) {
- this.cm.view.cantEdit = false;
- reCheckSelection(this.cm);
+ if (this.atomic && this.doc.cantEdit) {
+ this.doc.cantEdit = false;
+ if (cm) reCheckSelection(cm);
}
- endOperation(this.cm);
- signalLater(this.cm, this, "clear");
+ if (withOp) endOperation(cm);
+ signalLater(this, "clear");
};
TextMarker.prototype.find = function() {
var from, to;
for (var i = 0; i < this.lines.length; ++i) {
var line = this.lines[i];
var span = getMarkedSpanFor(line.markedSpans, this);
if (span.from != null || span.to != null) {
var found = lineNo(line);
- if (span.from != null) from = {line: found, ch: span.from};
- if (span.to != null) to = {line: found, ch: span.to};
+ if (span.from != null) from = Pos(found, span.from);
+ if (span.to != null) to = Pos(found, span.to);
}
}
if (this.type == "bookmark") return from;
return from && {from: from, to: to};
};
- TextMarker.prototype.getOptions = function(copyWidget) {
- var repl = this.replacedWith;
- return {className: this.className,
- inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
- atomic: this.atomic,
- collapsed: this.collapsed,
- clearOnEnter: this.clearOnEnter,
- replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
- readOnly: this.readOnly,
- startStyle: this.startStyle, endStyle: this.endStyle};
+ TextMarker.prototype.changed = function() {
+ var pos = this.find(), cm = this.doc.cm;
+ if (!pos || !cm) return;
+ var line = getLine(this.doc, pos.from.line);
+ clearCachedMeasurement(cm, line);
+ if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
+ for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
+ if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
+ break;
+ }
+ runInOp(cm, function() { cm.curOp.selectionChanged = true; });
+ }
};
- function markText(cm, from, to, options, type) {
- var doc = cm.view.doc;
- var marker = new TextMarker(cm, type);
+ TextMarker.prototype.attachLine = function(line) {
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp;
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
+ (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
+ }
+ this.lines.push(line);
+ };
+ TextMarker.prototype.detachLine = function(line) {
+ this.lines.splice(indexOf(this.lines, line), 1);
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp;
+ (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
+ }
+ };
+
+ function markText(doc, from, to, options, type) {
+ if (options && options.shared) return markTextShared(doc, from, to, options, type);
+ if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
+
+ var marker = new TextMarker(doc, type);
if (type == "range" && !posLess(from, to)) return marker;
- if (options) for (var opt in options) if (options.hasOwnProperty(opt))
- marker[opt] = options[opt];
+ if (options) copyObj(options, marker);
if (marker.replacedWith) {
marker.collapsed = true;
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
+ if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
}
if (marker.collapsed) sawCollapsedSpans = true;
- var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
+ if (marker.addToHistory)
+ addToHistory(doc, {from: from, to: to, origin: "markText"},
+ {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
+
+ var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
doc.iter(curLine, to.line + 1, function(line) {
- if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine)
- cm.curOp.updateMaxLine = true;
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
+ updateMaxLine = true;
var span = {from: null, to: null, marker: marker};
size += line.text.length;
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
if (marker.collapsed) {
if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
else updateLineHeight(line, 0);
}
addMarkedSpan(line, span);
++curLine;
});
if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
- if (lineIsHidden(line)) updateLineHeight(line, 0);
+ if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
});
+ if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
+
if (marker.readOnly) {
sawReadOnlySpans = true;
- if (cm.view.history.done.length || cm.view.history.undone.length)
- cm.clearHistory();
+ if (doc.history.done.length || doc.history.undone.length)
+ doc.clearHistory();
}
if (marker.collapsed) {
if (collapsedAtStart != collapsedAtEnd)
throw new Error("Inserting collapsed marker overlapping an existing one");
marker.size = size;
marker.atomic = true;
}
- if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
- regChange(cm, from.line, to.line + 1);
- if (marker.atomic) reCheckSelection(cm);
+ if (cm) {
+ if (updateMaxLine) cm.curOp.updateMaxLine = true;
+ if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
+ regChange(cm, from.line, to.line + 1);
+ if (marker.atomic) reCheckSelection(cm);
+ }
return marker;
}
+ // SHARED TEXTMARKERS
+
+ function SharedTextMarker(markers, primary) {
+ this.markers = markers;
+ this.primary = primary;
+ for (var i = 0, me = this; i < markers.length; ++i) {
+ markers[i].parent = this;
+ on(markers[i], "clear", function(){me.clear();});
+ }
+ }
+ CodeMirror.SharedTextMarker = SharedTextMarker;
+
+ SharedTextMarker.prototype.clear = function() {
+ if (this.explicitlyCleared) return;
+ this.explicitlyCleared = true;
+ for (var i = 0; i < this.markers.length; ++i)
+ this.markers[i].clear();
+ signalLater(this, "clear");
+ };
+ SharedTextMarker.prototype.find = function() {
+ return this.primary.find();
+ };
+
+ function markTextShared(doc, from, to, options, type) {
+ options = copyObj(options);
+ options.shared = false;
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
+ var widget = options.replacedWith;
+ linkedDocs(doc, function(doc) {
+ if (widget) options.replacedWith = widget.cloneNode(true);
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
+ for (var i = 0; i < doc.linked.length; ++i)
+ if (doc.linked[i].isParent) return;
+ primary = lst(markers);
+ });
+ return new SharedTextMarker(markers, primary);
+ }
+
// TEXTMARKER SPANS
function getMarkedSpanFor(spans, marker) {
if (spans) for (var i = 0; i < spans.length; ++i) {
var span = spans[i];
if (span.marker == marker) return span;
}
}
function removeMarkedSpan(spans, span) {
for (var r, i = 0; i < spans.length; ++i)
if (spans[i] != span) (r || (r = [])).push(spans[i]);
return r;
}
function addMarkedSpan(line, span) {
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
- span.marker.lines.push(line);
+ span.marker.attachLine(line);
}
- function markedSpansBefore(old, startCh) {
+ function markedSpansBefore(old, startCh, isInsert) {
if (old) for (var i = 0, nw; i < old.length; ++i) {
var span = old[i], marker = span.marker;
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
- if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
+ if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
(nw || (nw = [])).push({from: span.from,
to: endsAfter ? null : span.to,
marker: marker});
}
}
return nw;
}
- function markedSpansAfter(old, startCh, endCh) {
+ function markedSpansAfter(old, endCh, isInsert) {
if (old) for (var i = 0, nw; i < old.length; ++i) {
var span = old[i], marker = span.marker;
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
- if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.from != startCh) {
+ if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
to: span.to == null ? null : span.to - endCh,
marker: marker});
}
}
return nw;
}
- function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) {
- if (!oldFirst && !oldLast) return newText;
+ function stretchSpansOverChange(doc, change) {
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
+ if (!oldFirst && !oldLast) return null;
+
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
// Get the spans that 'stick out' on both sides
- var first = markedSpansBefore(oldFirst, startCh);
- var last = markedSpansAfter(oldLast, startCh, endCh);
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
// Next, merge those two ends
- var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0);
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
if (first) {
// Fix up .to properties of first
for (var i = 0; i < first.length; ++i) {
var span = first[i];
if (span.to == null) {
var found = getMarkedSpanFor(last, span.marker);
if (!found) span.to = startCh;
else if (sameLine) span.to = found.to == null ? null : found.to + offset;
}
}
}
if (last) {
// Fix up .from in last (or move them into first in case of sameLine)
for (var i = 0; i < last.length; ++i) {
var span = last[i];
if (span.to != null) span.to += offset;
if (span.from == null) {
var found = getMarkedSpanFor(first, span.marker);
if (!found) {
span.from = offset;
if (sameLine) (first || (first = [])).push(span);
}
} else {
span.from += offset;
if (sameLine) (first || (first = [])).push(span);
}
}
}
+ if (sameLine && first) {
+ // Make sure we didn't create any zero-length spans
+ for (var i = 0; i < first.length; ++i)
+ if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark")
+ first.splice(i--, 1);
+ if (!first.length) first = null;
+ }
- var newMarkers = [newHL(newText[0], first)];
+ var newMarkers = [first];
if (!sameLine) {
// Fill gap with whole-line-spans
- var gap = newText.length - 2, gapMarkers;
+ var gap = change.text.length - 2, gapMarkers;
if (gap > 0 && first)
for (var i = 0; i < first.length; ++i)
if (first[i].to == null)
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
for (var i = 0; i < gap; ++i)
- newMarkers.push(newHL(newText[i+1], gapMarkers));
- newMarkers.push(newHL(lst(newText), last));
+ newMarkers.push(gapMarkers);
+ newMarkers.push(last);
}
return newMarkers;
}
+ function mergeOldSpans(doc, change) {
+ var old = getOldSpans(doc, change);
+ var stretched = stretchSpansOverChange(doc, change);
+ if (!old) return stretched;
+ if (!stretched) return old;
+
+ for (var i = 0; i < old.length; ++i) {
+ var oldCur = old[i], stretchCur = stretched[i];
+ if (oldCur && stretchCur) {
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
+ var span = stretchCur[j];
+ for (var k = 0; k < oldCur.length; ++k)
+ if (oldCur[k].marker == span.marker) continue spans;
+ oldCur.push(span);
+ }
+ } else if (stretchCur) {
+ old[i] = stretchCur;
+ }
+ }
+ return old;
+ }
+
function removeReadOnlyRanges(doc, from, to) {
var markers = null;
doc.iter(from.line, to.line + 1, function(line) {
if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) {
var mark = line.markedSpans[i].marker;
if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
(markers || (markers = [])).push(mark);
}
});
if (!markers) return null;
var parts = [{from: from, to: to}];
for (var i = 0; i < markers.length; ++i) {
- var m = markers[i].find();
+ var mk = markers[i], m = mk.find();
for (var j = 0; j < parts.length; ++j) {
var p = parts[j];
- if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue;
+ if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
var newParts = [j, 1];
- if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from});
- if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to});
+ if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
+ newParts.push({from: p.from, to: m.from});
+ if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
+ newParts.push({from: m.to, to: p.to});
parts.splice.apply(parts, newParts);
j += newParts.length - 1;
}
}
return parts;
}
function collapsedSpanAt(line, ch) {
var sps = sawCollapsedSpans && line.markedSpans, found;
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
sp = sps[i];
if (!sp.marker.collapsed) continue;
if ((sp.from == null || sp.from < ch) &&
(sp.to == null || sp.to > ch) &&
(!found || found.width < sp.marker.width))
found = sp.marker;
}
return found;
}
function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
function visualLine(doc, line) {
var merged;
while (merged = collapsedSpanAtStart(line))
line = getLine(doc, merged.find().from.line);
return line;
}
- function lineIsHidden(line) {
+ function lineIsHidden(doc, line) {
var sps = sawCollapsedSpans && line.markedSpans;
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
sp = sps[i];
if (!sp.marker.collapsed) continue;
if (sp.from == null) return true;
- if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
+ if (sp.marker.replacedWith) continue;
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
return true;
}
}
- function lineIsHiddenInner(line, span) {
+ function lineIsHiddenInner(doc, line, span) {
if (span.to == null) {
- var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line);
- return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
+ var end = span.marker.find().to, endLine = getLine(doc, end.line);
+ return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
}
if (span.marker.inclusiveRight && span.to == line.text.length)
return true;
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
sp = line.markedSpans[i];
- if (sp.marker.collapsed && sp.from == span.to &&
+ if (sp.marker.collapsed && !sp.marker.replacedWith && sp.from == span.to &&
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
- lineIsHiddenInner(line, sp)) return true;
- }
- }
-
- // hl stands for history-line, a data structure that can be either a
- // string (line without markers) or a {text, markedSpans} object.
- function hlText(val) { return typeof val == "string" ? val : val.text; }
- function hlSpans(val) {
- if (typeof val == "string") return null;
- var spans = val.markedSpans, out = null;
- for (var i = 0; i < spans.length; ++i) {
- if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
- else if (out) out.push(spans[i]);
+ lineIsHiddenInner(doc, line, sp)) return true;
}
- return !out ? spans : out.length ? out : null;
}
- function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
function detachMarkedSpans(line) {
var spans = line.markedSpans;
if (!spans) return;
- for (var i = 0; i < spans.length; ++i) {
- var lines = spans[i].marker.lines;
- var ix = indexOf(lines, line);
- lines.splice(ix, 1);
- }
+ for (var i = 0; i < spans.length; ++i)
+ spans[i].marker.detachLine(line);
line.markedSpans = null;
}
function attachMarkedSpans(line, spans) {
if (!spans) return;
for (var i = 0; i < spans.length; ++i)
- spans[i].marker.lines.push(line);
+ spans[i].marker.attachLine(line);
line.markedSpans = spans;
}
// LINE WIDGETS
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
for (var opt in options) if (options.hasOwnProperty(opt))
this[opt] = options[opt];
this.cm = cm;
this.node = node;
};
function widgetOperation(f) {
return function() {
- startOperation(this.cm);
+ var withOp = !this.cm.curOp;
+ if (withOp) startOperation(this.cm);
try {var result = f.apply(this, arguments);}
- finally {endOperation(this.cm);}
+ finally {if (withOp) endOperation(this.cm);}
return result;
};
}
LineWidget.prototype.clear = widgetOperation(function() {
var ws = this.line.widgets, no = lineNo(this.line);
if (no == null || !ws) return;
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
+ if (!ws.length) this.line.widgets = null;
updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
regChange(this.cm, no, no + 1);
});
LineWidget.prototype.changed = widgetOperation(function() {
var oldH = this.height;
this.height = null;
var diff = widgetHeight(this) - oldH;
if (!diff) return;
updateLineHeight(this.line, this.line.height + diff);
var no = lineNo(this.line);
regChange(this.cm, no, no + 1);
});
function widgetHeight(widget) {
if (widget.height != null) return widget.height;
if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
return widget.height = widget.node.offsetHeight;
}
function addLineWidget(cm, handle, node, options) {
var widget = new LineWidget(cm, node, options);
if (widget.noHScroll) cm.display.alignWidgets = true;
changeLine(cm, handle, function(line) {
(line.widgets || (line.widgets = [])).push(widget);
widget.line = line;
- if (!lineIsHidden(line) || widget.showIfHidden) {
+ if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
updateLineHeight(line, line.height + widgetHeight(widget));
- if (aboveVisible)
- setTimeout(function() {cm.display.scroller.scrollTop += widget.height;});
+ if (aboveVisible) addToScrollPos(cm, 0, widget.height);
}
return true;
});
return widget;
}
// LINE DATA STRUCTURE
// Line objects. These hold state related to a line, including
// highlighting info (the styles array).
- function makeLine(text, markedSpans, height) {
- var line = {text: text, height: height};
+ function makeLine(text, markedSpans, estimateHeight) {
+ var line = {text: text};
attachMarkedSpans(line, markedSpans);
- if (lineIsHidden(line)) line.height = 0;
+ line.height = estimateHeight ? estimateHeight(line) : 1;
return line;
}
- function updateLine(cm, line, text, markedSpans) {
+ function updateLine(line, text, markedSpans, estimateHeight) {
line.text = text;
if (line.stateAfter) line.stateAfter = null;
if (line.styles) line.styles = null;
if (line.order != null) line.order = null;
detachMarkedSpans(line);
attachMarkedSpans(line, markedSpans);
- if (lineIsHidden(line)) line.height = 0;
- else if (!line.height) line.height = textHeight(cm.display);
- signalLater(cm, line, "change");
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
+ if (estHeight != line.height) updateLineHeight(line, estHeight);
}
function cleanUpLine(line) {
line.parent = null;
detachMarkedSpans(line);
}
// Run the given mode's parser over a line, update the styles
// array, which contains alternating fragments of text and CSS
// classes.
function runMode(cm, text, mode, state, f) {
- var flattenSpans = cm.options.flattenSpans;
- var curText = "", curStyle = null;
- var stream = new StringStream(text, cm.options.tabSize);
+ var flattenSpans = mode.flattenSpans;
+ if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
+ var curStart = 0, curStyle = null;
+ var stream = new StringStream(text, cm.options.tabSize), style;
if (text == "" && mode.blankLine) mode.blankLine(state);
while (!stream.eol()) {
- var style = mode.token(stream, state);
- if (stream.pos > 5000) {
+ if (stream.pos > cm.options.maxHighlightLength) {
flattenSpans = false;
// Webkit seems to refuse to render text nodes longer than 57444 characters
stream.pos = Math.min(text.length, stream.start + 50000);
style = null;
+ } else {
+ style = mode.token(stream, state);
}
- var substr = stream.current();
- stream.start = stream.pos;
if (!flattenSpans || curStyle != style) {
- if (curText) f(curText, curStyle);
- curText = substr; curStyle = style;
- } else curText = curText + substr;
+ if (curStart < stream.start) f(stream.start, curStyle);
+ curStart = stream.start; curStyle = style;
+ }
+ stream.start = stream.pos;
}
- if (curText) f(curText, curStyle);
+ if (curStart < stream.pos) f(stream.pos, curStyle);
}
function highlightLine(cm, line, state) {
// A styles array always starts with a number identifying the
// mode/overlays that it is based on (for easy invalidation).
- var st = [cm.view.modeGen];
+ var st = [cm.state.modeGen];
// Compute the base array of styles
- runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);});
+ runMode(cm, line.text, cm.doc.mode, state, function(end, style) {st.push(end, style);});
// Run overlays, adjust style array.
- for (var o = 0; o < cm.view.overlays.length; ++o) {
- var overlay = cm.view.overlays[o], i = 1;
- runMode(cm, line.text, overlay.mode, true, function(txt, style) {
- var start = i, len = txt.length;
+ for (var o = 0; o < cm.state.overlays.length; ++o) {
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
+ runMode(cm, line.text, overlay.mode, true, function(end, style) {
+ var start = i;
// Ensure there's a token end at the current position, and that i points at it
- while (len) {
- var cur = st[i], len_ = cur.length;
- if (len_ <= len) {
- len -= len_;
- } else {
- st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
- len = 0;
- }
+ while (at < end) {
+ var i_end = st[i];
+ if (i_end > end)
+ st.splice(i, 1, end, st[i+1], i_end);
i += 2;
+ at = Math.min(end, i_end);
}
if (!style) return;
if (overlay.opaque) {
- st.splice(start, i - start, txt, style);
+ st.splice(start, i - start, end, style);
i = start + 2;
} else {
for (; start < i; start += 2) {
var cur = st[start+1];
st[start+1] = cur ? cur + " " + style : style;
}
}
});
}
return st;
}
function getLineStyles(cm, line) {
- if (!line.styles || line.styles[0] != cm.view.modeGen)
+ if (!line.styles || line.styles[0] != cm.state.modeGen)
line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
return line.styles;
}
// Lightweight form of highlight -- proceed over this line and
// update state, but don't save a style array.
function processLine(cm, line, state) {
- var mode = cm.view.mode;
+ var mode = cm.doc.mode;
var stream = new StringStream(line.text, cm.options.tabSize);
if (line.text == "" && mode.blankLine) mode.blankLine(state);
- while (!stream.eol() && stream.pos <= 5000) {
+ while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) {
mode.token(stream, state);
stream.start = stream.pos;
}
}
var styleToClassCache = {};
function styleToClass(style) {
if (!style) return null;
return styleToClassCache[style] ||
(styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-"));
}
function lineContent(cm, realLine, measure) {
- var merged, line = realLine, lineBefore, sawBefore, simple = true;
- while (merged = collapsedSpanAtStart(line)) {
- simple = false;
- line = getLine(cm.view.doc, merged.find().from.line);
- if (!lineBefore) lineBefore = line;
- }
+ var merged, line = realLine, empty = true;
+ while (merged = collapsedSpanAtStart(line))
+ line = getLine(cm.doc, merged.find().from.line);
var builder = {pre: elt("pre"), col: 0, pos: 0, display: !measure,
- measure: null, addedOne: false, cm: cm};
+ measure: null, measuredSomething: false, cm: cm};
if (line.textClass) builder.pre.className = line.textClass;
do {
+ if (line.text) empty = false;
builder.measure = line == realLine && measure;
builder.pos = 0;
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
- if (measure && sawBefore && line != realLine && !builder.addedOne) {
- measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
- builder.addedOne = true;
- }
+ if ((ie || webkit) && cm.getOption("lineWrapping"))
+ builder.addToken = buildTokenSplitSpaces(builder.addToken);
var next = insertLineContent(line, builder, getLineStyles(cm, line));
- sawBefore = line == lineBefore;
- if (next) {
- line = getLine(cm.view.doc, next.to.line);
- simple = false;
+ if (measure && line == realLine && !builder.measuredSomething) {
+ measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
+ builder.measuredSomething = true;
}
+ if (next) line = getLine(cm.doc, next.to.line);
} while (next);
- if (measure && !builder.addedOne)
- measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
- if (!builder.pre.firstChild && !lineIsHidden(realLine))
+ if (measure && !builder.measuredSomething && !measure[0])
+ measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
+ if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
builder.pre.appendChild(document.createTextNode("\u00a0"));
+ var order;
+ // Work around problem with the reported dimensions of single-char
+ // direction spans on IE (issue #1129). See also the comment in
+ // cursorCoords.
+ if (measure && ie && (order = getOrder(line))) {
+ var l = order.length - 1;
+ if (order[l].from == order[l].to) --l;
+ var last = order[l], prev = order[l - 1];
+ if (last.from + 1 == last.to && prev && last.level < prev.level) {
+ var span = measure[builder.pos - 1];
+ if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
+ span.nextSibling);
+ }
+ }
+
+ signal(cm, "renderLine", cm, realLine, builder.pre);
return builder.pre;
}
- var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
+ var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
function buildToken(builder, text, style, startStyle, endStyle) {
if (!text) return;
if (!tokenSpecialChars.test(text)) {
builder.col += text.length;
var content = document.createTextNode(text);
} else {
var content = document.createDocumentFragment(), pos = 0;
while (true) {
tokenSpecialChars.lastIndex = pos;
var m = tokenSpecialChars.exec(text);
var skipped = m ? m.index - pos : text.length - pos;
if (skipped) {
content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
builder.col += skipped;
}
if (!m) break;
pos += skipped + 1;
if (m[0] == "\t") {
var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
builder.col += tabWidth;
} else {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + m[0].charCodeAt(0).toString(16);
content.appendChild(token);
builder.col += 1;
}
}
}
if (style || startStyle || endStyle || builder.measure) {
var fullStyle = style || "";
if (startStyle) fullStyle += startStyle;
if (endStyle) fullStyle += endStyle;
return builder.pre.appendChild(elt("span", [content], fullStyle));
}
builder.pre.appendChild(content);
}
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
+ var wrapping = builder.cm.options.lineWrapping;
for (var i = 0; i < text.length; ++i) {
- if (i && i < text.length &&
- builder.cm.options.lineWrapping &&
- spanAffectsWrapping.test(text.slice(i - 1, i + 1)))
+ var ch = text.charAt(i), start = i == 0;
+ if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
+ ch = text.slice(i, i + 2);
+ ++i;
+ } else if (i && wrapping && spanAffectsWrapping(text, i)) {
builder.pre.appendChild(elt("wbr"));
- builder.measure[builder.pos++] =
- buildToken(builder, text.charAt(i), style,
- i == 0 && startStyle, i == text.length - 1 && endStyle);
+ }
+ var span = builder.measure[builder.pos] =
+ buildToken(builder, ch, style,
+ start && startStyle, i == text.length - 1 && endStyle);
+ // In IE single-space nodes wrap differently than spaces
+ // embedded in larger text nodes, except when set to
+ // white-space: normal (issue #1268).
+ if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
+ i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
+ span.style.whiteSpace = "normal";
+ builder.pos += ch.length;
+ }
+ if (text.length) builder.measuredSomething = true;
+ }
+
+ function buildTokenSplitSpaces(inner) {
+ function split(old) {
+ var out = " ";
+ for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
+ out += " ";
+ return out;
}
- if (text.length) builder.addedOne = true;
+ return function(builder, text, style, startStyle, endStyle) {
+ return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
+ };
}
function buildCollapsedSpan(builder, size, widget) {
if (widget) {
if (!builder.display) widget = widget.cloneNode(true);
- builder.pre.appendChild(widget);
- if (builder.measure && size) {
- builder.measure[builder.pos] = widget;
- builder.addedOne = true;
+ if (builder.measure) {
+ builder.measure[builder.pos] = size ? widget
+ : builder.pre.appendChild(zeroWidthElement(builder.cm.display.measure));
+ builder.measuredSomething = true;
}
+ builder.pre.appendChild(widget);
}
builder.pos += size;
}
// Outputs a number of spans to make up a line, taking highlighting
// and marked text into account.
function insertLineContent(line, builder, styles) {
- var spans = line.markedSpans;
+ var spans = line.markedSpans, allText = line.text, at = 0;
if (!spans) {
for (var i = 1; i < styles.length; i+=2)
- builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
+ builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1]));
return;
}
- var allText = line.text, len = allText.length;
- var pos = 0, i = 1, text = "", style;
+ var len = allText.length, pos = 0, i = 1, text = "", style;
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
for (;;) {
if (nextChange == pos) { // Update current marker set
spanStyle = spanEndStyle = spanStartStyle = "";
collapsed = null; nextChange = Infinity;
var foundBookmark = null;
for (var j = 0; j < spans.length; ++j) {
var sp = spans[j], m = sp.marker;
if (sp.from <= pos && (sp.to == null || sp.to > pos)) {
if (sp.to != null && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; }
if (m.className) spanStyle += " " + m.className;
if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle;
if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle;
- if (m.collapsed && (!collapsed || collapsed.marker.width < m.width))
+ if (m.collapsed && (!collapsed || collapsed.marker.size < m.size))
collapsed = sp;
} else if (sp.from > pos && nextChange > sp.from) {
nextChange = sp.from;
}
if (m.type == "bookmark" && sp.from == pos && m.replacedWith)
foundBookmark = m.replacedWith;
}
if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
collapsed.from != null && collapsed.marker.replacedWith);
if (collapsed.to == null) return collapsed.marker.find();
}
if (foundBookmark && !collapsed) buildCollapsedSpan(builder, 0, foundBookmark);
}
if (pos >= len) break;
var upto = Math.min(len, nextChange);
while (true) {
if (text) {
var end = pos + text.length;
if (!collapsed) {
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
- builder.addToken(builder, tokenText, style + spanStyle,
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
}
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
pos = end;
spanStartStyle = "";
}
- text = styles[i++]; style = styleToClass(styles[i++]);
+ text = allText.slice(at, at = styles[i++]);
+ style = styleToClass(styles[i++]);
}
}
}
// DOCUMENT DATA STRUCTURE
+ function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
+ function update(line, text, spans) {
+ updateLine(line, text, spans, estimateHeight);
+ signalLater(line, "change", line, change);
+ }
+
+ var from = change.from, to = change.to, text = change.text;
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
+
+ // First adjust the line structure
+ if (from.ch == 0 && to.ch == 0 && lastText == "") {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
+ update(lastLine, lastLine.text, lastSpans);
+ if (nlines) doc.remove(from.line, nlines);
+ if (added.length) doc.insert(from.line, added);
+ } else if (firstLine == lastLine) {
+ if (text.length == 1) {
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
+ } else {
+ for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
+ added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+ doc.insert(from.line + 1, added);
+ }
+ } else if (text.length == 1) {
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
+ doc.remove(from.line + 1, nlines);
+ } else {
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
+ for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
+ added.push(makeLine(text[i], spansFor(i), estimateHeight));
+ if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
+ doc.insert(from.line + 1, added);
+ }
+
+ signalLater(doc, "change", doc, change);
+ setSelection(doc, selAfter.anchor, selAfter.head, null, true);
+ }
+
function LeafChunk(lines) {
this.lines = lines;
this.parent = null;
for (var i = 0, e = lines.length, height = 0; i < e; ++i) {
lines[i].parent = this;
height += lines[i].height;
}
this.height = height;
}
LeafChunk.prototype = {
chunkSize: function() { return this.lines.length; },
- remove: function(at, n, cm) {
+ removeInner: function(at, n) {
for (var i = at, e = at + n; i < e; ++i) {
var line = this.lines[i];
this.height -= line.height;
cleanUpLine(line);
- signalLater(cm, line, "delete");
+ signalLater(line, "delete");
}
this.lines.splice(at, n);
},
collapse: function(lines) {
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
},
- insertHeight: function(at, lines, height) {
+ insertInner: function(at, lines, height) {
this.height += height;
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
},
iterN: function(at, n, op) {
for (var e = at + n; at < e; ++at)
if (op(this.lines[at])) return true;
}
};
function BranchChunk(children) {
this.children = children;
var size = 0, height = 0;
for (var i = 0, e = children.length; i < e; ++i) {
var ch = children[i];
size += ch.chunkSize(); height += ch.height;
ch.parent = this;
}
this.size = size;
this.height = height;
this.parent = null;
}
BranchChunk.prototype = {
chunkSize: function() { return this.size; },
- remove: function(at, n, callbacks) {
+ removeInner: function(at, n) {
this.size -= n;
for (var i = 0; i < this.children.length; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at < sz) {
var rm = Math.min(n, sz - at), oldHeight = child.height;
- child.remove(at, rm, callbacks);
+ child.removeInner(at, rm);
this.height -= oldHeight - child.height;
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
if ((n -= rm) == 0) break;
at = 0;
} else at -= sz;
}
if (this.size - n < 25) {
var lines = [];
this.collapse(lines);
this.children = [new LeafChunk(lines)];
this.children[0].parent = this;
}
},
collapse: function(lines) {
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
},
- insert: function(at, lines) {
- var height = 0;
- for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
- this.insertHeight(at, lines, height);
- },
- insertHeight: function(at, lines, height) {
+ insertInner: function(at, lines, height) {
this.size += lines.length;
this.height += height;
for (var i = 0, e = this.children.length; i < e; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at <= sz) {
- child.insertHeight(at, lines, height);
+ child.insertInner(at, lines, height);
if (child.lines && child.lines.length > 50) {
while (child.lines.length > 50) {
var spilled = child.lines.splice(child.lines.length - 25, 25);
var newleaf = new LeafChunk(spilled);
child.height -= newleaf.height;
this.children.splice(i + 1, 0, newleaf);
newleaf.parent = this;
}
this.maybeSpill();
}
break;
}
at -= sz;
}
},
maybeSpill: function() {
if (this.children.length <= 10) return;
var me = this;
do {
var spilled = me.children.splice(me.children.length - 5, 5);
var sibling = new BranchChunk(spilled);
if (!me.parent) { // Become the parent node
var copy = new BranchChunk(me.children);
copy.parent = me;
me.children = [copy, sibling];
me = copy;
} else {
me.size -= sibling.size;
me.height -= sibling.height;
var myIndex = indexOf(me.parent.children, me);
me.parent.children.splice(myIndex + 1, 0, sibling);
}
sibling.parent = me.parent;
} while (me.children.length > 10);
me.parent.maybeSpill();
},
- iter: function(from, to, op) { this.iterN(from, to - from, op); },
iterN: function(at, n, op) {
for (var i = 0, e = this.children.length; i < e; ++i) {
var child = this.children[i], sz = child.chunkSize();
if (at < sz) {
var used = Math.min(n, sz - at);
if (child.iterN(at, used, op)) return true;
if ((n -= used) == 0) break;
at = 0;
} else at -= sz;
}
}
};
+ var nextDocId = 0;
+ var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
+ if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
+ if (firstLine == null) firstLine = 0;
+
+ BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
+ this.first = firstLine;
+ this.scrollTop = this.scrollLeft = 0;
+ this.cantEdit = false;
+ this.history = makeHistory();
+ this.cleanGeneration = 1;
+ this.frontier = firstLine;
+ var start = Pos(firstLine, 0);
+ this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
+ this.id = ++nextDocId;
+ this.modeOption = mode;
+
+ if (typeof text == "string") text = splitLines(text);
+ updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
+ };
+
+ Doc.prototype = createObj(BranchChunk.prototype, {
+ constructor: Doc,
+ iter: function(from, to, op) {
+ if (op) this.iterN(from - this.first, to - from, op);
+ else this.iterN(this.first, this.first + this.size, from);
+ },
+
+ insert: function(at, lines) {
+ var height = 0;
+ for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
+ this.insertInner(at - this.first, lines, height);
+ },
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
+
+ getValue: function(lineSep) {
+ var lines = getLines(this, this.first, this.first + this.size);
+ if (lineSep === false) return lines;
+ return lines.join(lineSep || "\n");
+ },
+ setValue: function(code) {
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
+ text: splitLines(code), origin: "setValue"},
+ {head: top, anchor: top}, true);
+ },
+ replaceRange: function(code, from, to, origin) {
+ from = clipPos(this, from);
+ to = to ? clipPos(this, to) : from;
+ replaceRange(this, code, from, to, origin);
+ },
+ getRange: function(from, to, lineSep) {
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
+ if (lineSep === false) return lines;
+ return lines.join(lineSep || "\n");
+ },
+
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
+ setLine: function(line, text) {
+ if (isLine(this, line))
+ replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
+ },
+ removeLine: function(line) {
+ if (line) replaceRange(this, "", clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
+ else replaceRange(this, "", Pos(0, 0), clipPos(this, Pos(1, 0)));
+ },
+
+ getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
+ getLineNumber: function(line) {return lineNo(line);},
+
+ lineCount: function() {return this.size;},
+ firstLine: function() {return this.first;},
+ lastLine: function() {return this.first + this.size - 1;},
+
+ clipPos: function(pos) {return clipPos(this, pos);},
+
+ getCursor: function(start) {
+ var sel = this.sel, pos;
+ if (start == null || start == "head") pos = sel.head;
+ else if (start == "anchor") pos = sel.anchor;
+ else if (start == "end" || start === false) pos = sel.to;
+ else pos = sel.from;
+ return copyPos(pos);
+ },
+ somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
+
+ setCursor: docOperation(function(line, ch, extend) {
+ var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
+ if (extend) extendSelection(this, pos);
+ else setSelection(this, pos, pos);
+ }),
+ setSelection: docOperation(function(anchor, head) {
+ setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
+ }),
+ extendSelection: docOperation(function(from, to) {
+ extendSelection(this, clipPos(this, from), to && clipPos(this, to));
+ }),
+
+ getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
+ replaceSelection: function(code, collapse, origin) {
+ makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
+ },
+ undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
+ redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
+
+ setExtending: function(val) {this.sel.extend = val;},
+
+ historySize: function() {
+ var hist = this.history;
+ return {undo: hist.done.length, redo: hist.undone.length};
+ },
+ clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
+
+ markClean: function() {
+ this.cleanGeneration = this.changeGeneration();
+ },
+ changeGeneration: function() {
+ this.history.lastOp = this.history.lastOrigin = null;
+ return this.history.generation;
+ },
+ isClean: function (gen) {
+ return this.history.generation == (gen || this.cleanGeneration);
+ },
+
+ getHistory: function() {
+ return {done: copyHistoryArray(this.history.done),
+ undone: copyHistoryArray(this.history.undone)};
+ },
+ setHistory: function(histData) {
+ var hist = this.history = makeHistory(this.history.maxGeneration);
+ hist.done = histData.done.slice(0);
+ hist.undone = histData.undone.slice(0);
+ },
+
+ markText: function(from, to, options) {
+ return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
+ },
+ setBookmark: function(pos, options) {
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
+ insertLeft: options && options.insertLeft};
+ pos = clipPos(this, pos);
+ return markText(this, pos, pos, realOpts, "bookmark");
+ },
+ findMarksAt: function(pos) {
+ pos = clipPos(this, pos);
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
+ if (spans) for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if ((span.from == null || span.from <= pos.ch) &&
+ (span.to == null || span.to >= pos.ch))
+ markers.push(span.marker.parent || span.marker);
+ }
+ return markers;
+ },
+ getAllMarks: function() {
+ var markers = [];
+ this.iter(function(line) {
+ var sps = line.markedSpans;
+ if (sps) for (var i = 0; i < sps.length; ++i)
+ if (sps[i].from != null) markers.push(sps[i].marker);
+ });
+ return markers;
+ },
+
+ posFromIndex: function(off) {
+ var ch, lineNo = this.first;
+ this.iter(function(line) {
+ var sz = line.text.length + 1;
+ if (sz > off) { ch = off; return true; }
+ off -= sz;
+ ++lineNo;
+ });
+ return clipPos(this, Pos(lineNo, ch));
+ },
+ indexFromPos: function (coords) {
+ coords = clipPos(this, coords);
+ var index = coords.ch;
+ if (coords.line < this.first || coords.ch < 0) return 0;
+ this.iter(this.first, coords.line, function (line) {
+ index += line.text.length + 1;
+ });
+ return index;
+ },
+
+ copy: function(copyHistory) {
+ var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
+ doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
+ shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
+ if (copyHistory) {
+ doc.history.undoDepth = this.history.undoDepth;
+ doc.setHistory(this.getHistory());
+ }
+ return doc;
+ },
+
+ linkedDoc: function(options) {
+ if (!options) options = {};
+ var from = this.first, to = this.first + this.size;
+ if (options.from != null && options.from > from) from = options.from;
+ if (options.to != null && options.to < to) to = options.to;
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
+ if (options.sharedHist) copy.history = this.history;
+ (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
+ return copy;
+ },
+ unlinkDoc: function(other) {
+ if (other instanceof CodeMirror) other = other.doc;
+ if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
+ var link = this.linked[i];
+ if (link.doc != other) continue;
+ this.linked.splice(i, 1);
+ other.unlinkDoc(this);
+ break;
+ }
+ // If the histories were shared, split them again
+ if (other.history == this.history) {
+ var splitIds = [other.id];
+ linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
+ other.history = makeHistory();
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
+ }
+ },
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
+
+ getMode: function() {return this.mode;},
+ getEditor: function() {return this.cm;}
+ });
+
+ Doc.prototype.eachLine = Doc.prototype.iter;
+
+ // The Doc methods that should be available on CodeMirror instances
+ var dontDelegate = "iter insert remove copy getEditor".split(" ");
+ for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
+ CodeMirror.prototype[prop] = (function(method) {
+ return function() {return method.apply(this.doc, arguments);};
+ })(Doc.prototype[prop]);
+
+ function linkedDocs(doc, f, sharedHistOnly) {
+ function propagate(doc, skip, sharedHist) {
+ if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
+ var rel = doc.linked[i];
+ if (rel.doc == skip) continue;
+ var shared = sharedHist && rel.sharedHist;
+ if (sharedHistOnly && !shared) continue;
+ f(rel.doc, shared);
+ propagate(rel.doc, doc, shared);
+ }
+ }
+ propagate(doc, null, true);
+ }
+
+ function attachDoc(cm, doc) {
+ if (doc.cm) throw new Error("This document is already in use.");
+ cm.doc = doc;
+ doc.cm = cm;
+ estimateLineHeights(cm);
+ loadMode(cm);
+ if (!cm.options.lineWrapping) computeMaxLength(cm);
+ cm.options.mode = doc.modeOption;
+ regChange(cm);
+ }
+
// LINE UTILITIES
function getLine(chunk, n) {
+ n -= chunk.first;
while (!chunk.lines) {
for (var i = 0;; ++i) {
var child = chunk.children[i], sz = child.chunkSize();
if (n < sz) { chunk = child; break; }
n -= sz;
}
}
return chunk.lines[n];
}
+ function getBetween(doc, start, end) {
+ var out = [], n = start.line;
+ doc.iter(start.line, end.line + 1, function(line) {
+ var text = line.text;
+ if (n == end.line) text = text.slice(0, end.ch);
+ if (n == start.line) text = text.slice(start.ch);
+ out.push(text);
+ ++n;
+ });
+ return out;
+ }
+ function getLines(doc, from, to) {
+ var out = [];
+ doc.iter(from, to, function(line) { out.push(line.text); });
+ return out;
+ }
+
function updateLineHeight(line, height) {
var diff = height - line.height;
for (var n = line; n; n = n.parent) n.height += diff;
}
function lineNo(line) {
if (line.parent == null) return null;
var cur = line.parent, no = indexOf(cur.lines, line);
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
for (var i = 0;; ++i) {
if (chunk.children[i] == cur) break;
no += chunk.children[i].chunkSize();
}
}
- return no;
- }
-
- function lineDoc(line) {
- for (var d = line.parent; d.parent; d = d.parent) {}
- return d;
+ return no + cur.first;
}
function lineAtHeight(chunk, h) {
- var n = 0;
+ var n = chunk.first;
outer: do {
for (var i = 0, e = chunk.children.length; i < e; ++i) {
var child = chunk.children[i], ch = child.height;
if (h < ch) { chunk = child; continue outer; }
h -= ch;
n += child.chunkSize();
}
return n;
} while (!chunk.lines);
for (var i = 0, e = chunk.lines.length; i < e; ++i) {
var line = chunk.lines[i], lh = line.height;
if (h < lh) break;
h -= lh;
}
return n + i;
}
function heightAtLine(cm, lineObj) {
- lineObj = visualLine(cm.view.doc, lineObj);
+ lineObj = visualLine(cm.doc, lineObj);
var h = 0, chunk = lineObj.parent;
for (var i = 0; i < chunk.lines.length; ++i) {
var line = chunk.lines[i];
if (line == lineObj) break;
else h += line.height;
}
for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
for (var i = 0; i < p.children.length; ++i) {
var cur = p.children[i];
if (cur == chunk) break;
else h += cur.height;
}
}
return h;
}
function getOrder(line) {
var order = line.order;
if (order == null) order = line.order = bidiOrdering(line.text);
return order;
}
// HISTORY
- function makeHistory() {
+ function makeHistory(startGen) {
return {
// Arrays of history events. Doing something adds an event to
// done and clears undo. Undoing moves events from done to
// undone, redoing moves them in the other direction.
- done: [], undone: [],
+ done: [], undone: [], undoDepth: Infinity,
// Used to track when changes can be merged into a single undo
// event
lastTime: 0, lastOp: null, lastOrigin: null,
// Used by the isClean() method
- dirtyCounter: 0
+ generation: startGen || 1, maxGeneration: startGen || 1
};
}
- function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) {
- var history = cm.view.history;
- history.undone.length = 0;
- var time = +new Date, cur = lst(history.done);
-
+ function attachLocalSpans(doc, change, from, to) {
+ var existing = change["spans_" + doc.id], n = 0;
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
+ if (line.markedSpans)
+ (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
+ ++n;
+ });
+ }
+
+ function historyChangeFromChange(doc, change) {
+ var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
+ linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
+ return histChange;
+ }
+
+ function addToHistory(doc, change, selAfter, opId) {
+ var hist = doc.history;
+ hist.undone.length = 0;
+ var time = +new Date, cur = lst(hist.done);
+
if (cur &&
- (history.lastOp == cm.curOp.id ||
- history.lastOrigin == origin && (origin == "input" || origin == "delete") &&
- history.lastTime > time - 600)) {
+ (hist.lastOp == opId ||
+ hist.lastOrigin == change.origin && change.origin &&
+ ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) ||
+ change.origin.charAt(0) == "*"))) {
// Merge this change into the last event
- var last = lst(cur.events);
- if (last.start > start + old.length || last.start + last.added < start) {
- // Doesn't intersect with last sub-event, add new sub-event
- cur.events.push({start: start, added: added, old: old});
+ var last = lst(cur.changes);
+ if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
+ // Optimized case for simple insertion -- don't want to add
+ // new changesets for every character typed
+ last.to = changeEnd(change);
} else {
- // Patch up the last sub-event
- var startBefore = Math.max(0, last.start - start),
- endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
- for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
- for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
- if (startBefore) last.start = start;
- last.added += added - (old.length - startBefore - endAfter);
+ // Add new sub-event
+ cur.changes.push(historyChangeFromChange(doc, change));
}
- cur.fromAfter = fromAfter; cur.toAfter = toAfter;
+ cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
} else {
// Can not be merged, start a new event.
- cur = {events: [{start: start, added: added, old: old}],
- fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter};
- history.done.push(cur);
- while (history.done.length > cm.options.undoDepth)
- history.done.shift();
- if (history.dirtyCounter < 0)
- // The user has made a change after undoing past the last clean state.
- // We can never get back to a clean state now until markClean() is called.
- history.dirtyCounter = NaN;
- else
- history.dirtyCounter++;
+ cur = {changes: [historyChangeFromChange(doc, change)],
+ generation: hist.generation,
+ anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
+ anchorAfter: selAfter.anchor, headAfter: selAfter.head};
+ hist.done.push(cur);
+ hist.generation = ++hist.maxGeneration;
+ while (hist.done.length > hist.undoDepth)
+ hist.done.shift();
+ }
+ hist.lastTime = time;
+ hist.lastOp = opId;
+ hist.lastOrigin = change.origin;
+ }
+
+ function removeClearedSpans(spans) {
+ if (!spans) return null;
+ for (var i = 0, out; i < spans.length; ++i) {
+ if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
+ else if (out) out.push(spans[i]);
+ }
+ return !out ? spans : out.length ? out : null;
+ }
+
+ function getOldSpans(doc, change) {
+ var found = change["spans_" + doc.id];
+ if (!found) return null;
+ for (var i = 0, nw = []; i < change.text.length; ++i)
+ nw.push(removeClearedSpans(found[i]));
+ return nw;
+ }
+
+ // Used both to provide a JSON-safe object in .getHistory, and, when
+ // detaching a document, to split the history in two
+ function copyHistoryArray(events, newGroup) {
+ for (var i = 0, copy = []; i < events.length; ++i) {
+ var event = events[i], changes = event.changes, newChanges = [];
+ copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
+ anchorAfter: event.anchorAfter, headAfter: event.headAfter});
+ for (var j = 0; j < changes.length; ++j) {
+ var change = changes[j], m;
+ newChanges.push({from: change.from, to: change.to, text: change.text});
+ if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
+ if (indexOf(newGroup, Number(m[1])) > -1) {
+ lst(newChanges)[prop] = change[prop];
+ delete change[prop];
+ }
+ }
+ }
+ }
+ return copy;
+ }
+
+ // Rebasing/resetting history to deal with externally-sourced changes
+
+ function rebaseHistSel(pos, from, to, diff) {
+ if (to < pos.line) {
+ pos.line += diff;
+ } else if (from < pos.line) {
+ pos.line = from;
+ pos.ch = 0;
+ }
+ }
+
+ // Tries to rebase an array of history events given a change in the
+ // document. If the change touches the same lines as the event, the
+ // event, and everything 'behind' it, is discarded. If the change is
+ // before the event, the event's positions are updated. Uses a
+ // copy-on-write scheme for the positions, to avoid having to
+ // reallocate them all on every rebase, but also avoid problems with
+ // shared position objects being unsafely updated.
+ function rebaseHistArray(array, from, to, diff) {
+ for (var i = 0; i < array.length; ++i) {
+ var sub = array[i], ok = true;
+ for (var j = 0; j < sub.changes.length; ++j) {
+ var cur = sub.changes[j];
+ if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
+ if (to < cur.from.line) {
+ cur.from.line += diff;
+ cur.to.line += diff;
+ } else if (from <= cur.to.line) {
+ ok = false;
+ break;
+ }
+ }
+ if (!sub.copied) {
+ sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
+ sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
+ sub.copied = true;
+ }
+ if (!ok) {
+ array.splice(0, i + 1);
+ i = 0;
+ } else {
+ rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
+ rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
+ }
}
- history.lastTime = time;
- history.lastOp = cm.curOp.id;
- history.lastOrigin = origin;
+ }
+
+ function rebaseHist(hist, change) {
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
+ rebaseHistArray(hist.done, from, to, diff);
+ rebaseHistArray(hist.undone, from, to, diff);
}
// EVENT OPERATORS
function stopMethod() {e_stop(this);}
// Ensure an event has a stop method.
function addStop(event) {
if (!event.stop) event.stop = stopMethod;
return event;
}
function e_preventDefault(e) {
if (e.preventDefault) e.preventDefault();
else e.returnValue = false;
}
function e_stopPropagation(e) {
if (e.stopPropagation) e.stopPropagation();
else e.cancelBubble = true;
}
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
+ }
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
CodeMirror.e_stop = e_stop;
CodeMirror.e_preventDefault = e_preventDefault;
CodeMirror.e_stopPropagation = e_stopPropagation;
function e_target(e) {return e.target || e.srcElement;}
function e_button(e) {
var b = e.which;
if (b == null) {
if (e.button & 1) b = 1;
else if (e.button & 2) b = 3;
else if (e.button & 4) b = 2;
}
if (mac && e.ctrlKey && b == 1) b = 3;
return b;
}
- // Allow 3rd-party code to override event properties by adding an override
- // object to an event object.
- function e_prop(e, prop) {
- var overridden = e.override && e.override.hasOwnProperty(prop);
- return overridden ? e.override[prop] : e[prop];
- }
-
// EVENT HANDLING
function on(emitter, type, f) {
if (emitter.addEventListener)
emitter.addEventListener(type, f, false);
else if (emitter.attachEvent)
emitter.attachEvent("on" + type, f);
else {
var map = emitter._handlers || (emitter._handlers = {});
var arr = map[type] || (map[type] = []);
arr.push(f);
}
}
function off(emitter, type, f) {
if (emitter.removeEventListener)
emitter.removeEventListener(type, f, false);
else if (emitter.detachEvent)
emitter.detachEvent("on" + type, f);
else {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
for (var i = 0; i < arr.length; ++i)
if (arr[i] == f) { arr.splice(i, 1); break; }
}
}
function signal(emitter, type /*, values...*/) {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
var args = Array.prototype.slice.call(arguments, 2);
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
}
- function signalLater(cm, emitter, type /*, values...*/) {
+ var delayedCallbacks, delayedCallbackDepth = 0;
+ function signalLater(emitter, type /*, values...*/) {
var arr = emitter._handlers && emitter._handlers[type];
if (!arr) return;
- var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks;
+ var args = Array.prototype.slice.call(arguments, 2);
+ if (!delayedCallbacks) {
+ ++delayedCallbackDepth;
+ delayedCallbacks = [];
+ setTimeout(fireDelayed, 0);
+ }
function bnd(f) {return function(){f.apply(null, args);};};
for (var i = 0; i < arr.length; ++i)
- if (flist) flist.push(bnd(arr[i]));
- else arr[i].apply(null, args);
+ delayedCallbacks.push(bnd(arr[i]));
+ }
+
+ function signalDOMEvent(cm, e) {
+ signal(cm, e.type, cm, e);
+ return e_defaultPrevented(e);
+ }
+
+ function fireDelayed() {
+ --delayedCallbackDepth;
+ var delayed = delayedCallbacks;
+ delayedCallbacks = null;
+ for (var i = 0; i < delayed.length; ++i) delayed[i]();
}
function hasHandler(emitter, type) {
var arr = emitter._handlers && emitter._handlers[type];
return arr && arr.length > 0;
}
CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
// MISC UTILITIES
// Number of pixels added to scroller and sizer to hide scrollbar
var scrollerCutOff = 30;
// Returned or thrown by various protocols to signal 'I'm not
// handling this'.
var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
function Delayed() {this.id = null;}
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
// Counts the column offset in a string, taking tabs into account.
// Used mostly to find indentation.
- function countColumn(string, end, tabSize) {
+ function countColumn(string, end, tabSize, startIndex, startValue) {
if (end == null) {
end = string.search(/[^\s\u00a0]/);
if (end == -1) end = string.length;
}
- for (var i = 0, n = 0; i < end; ++i) {
+ for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
else ++n;
}
return n;
}
CodeMirror.countColumn = countColumn;
var spaceStrs = [""];
function spaceStr(n) {
while (spaceStrs.length <= n)
spaceStrs.push(lst(spaceStrs) + " ");
return spaceStrs[n];
}
function lst(arr) { return arr[arr.length-1]; }
function selectInput(node) {
if (ios) { // Mobile Safari apparently has a bug where select() is broken.
node.selectionStart = 0;
node.selectionEnd = node.value.length;
- } else node.select();
+ } else {
+ // Suppress mysterious IE10 errors
+ try { node.select(); }
+ catch(_e) {}
+ }
}
function indexOf(collection, elt) {
if (collection.indexOf) return collection.indexOf(elt);
for (var i = 0, e = collection.length; i < e; ++i)
if (collection[i] == elt) return i;
return -1;
}
+ function createObj(base, props) {
+ function Obj() {}
+ Obj.prototype = base;
+ var inst = new Obj();
+ if (props) copyObj(props, inst);
+ return inst;
+ }
+
+ function copyObj(obj, target) {
+ if (!target) target = {};
+ for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
+ return target;
+ }
+
function emptyArray(size) {
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
return a;
}
function bind(f) {
var args = Array.prototype.slice.call(arguments, 1);
return function(){return f.apply(null, args);};
}
- var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
function isWordChar(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
}
function isEmpty(obj) {
- var c = 0;
- for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c;
- return !c;
+ for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
+ return true;
}
- var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
+ var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
// DOM UTILITIES
function elt(tag, content, className, style) {
var e = document.createElement(tag);
if (className) e.className = className;
if (style) e.style.cssText = style;
if (typeof content == "string") setTextContent(e, content);
else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
return e;
}
function removeChildren(e) {
- // IE will break all parent-child relations in subnodes when setting innerHTML
- if (!ie) e.innerHTML = "";
- else while (e.firstChild) e.removeChild(e.firstChild);
+ for (var count = e.childNodes.length; count > 0; --count)
+ e.removeChild(e.firstChild);
return e;
}
function removeChildrenAndAdd(parent, e) {
return removeChildren(parent).appendChild(e);
}
function setTextContent(e, str) {
if (ie_lt9) {
e.innerHTML = "";
e.appendChild(document.createTextNode(str));
} else e.textContent = str;
}
+ function getRect(node) {
+ return node.getBoundingClientRect();
+ }
+ CodeMirror.replaceGetRect = function(f) { getRect = f; };
+
// FEATURE DETECTION
// Detect drag-and-drop
var dragAndDrop = function() {
// There is *some* kind of drag-and-drop support in IE6-8, but I
// couldn't get it to work yet.
if (ie_lt9) return false;
var div = elt('div');
return "draggable" in div || "dragDrop" in div;
}();
// For a reason I have yet to figure out, some browsers disallow
// word wrapping between certain characters *only* if a new inline
// element is started between them. This makes it hard to reliably
// measure the position of things, since that requires inserting an
- // extra span. This terribly fragile set of regexps matches the
+ // extra span. This terribly fragile set of tests matches the
// character combinations that suffer from this phenomenon on the
// various browsers.
- var spanAffectsWrapping = /^$/; // Won't match any two-character string
- if (gecko) spanAffectsWrapping = /$'/;
- else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
- else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
+ function spanAffectsWrapping() { return false; }
+ if (gecko) // Only for "$'"
+ spanAffectsWrapping = function(str, i) {
+ return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
+ };
+ else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
+ spanAffectsWrapping = function(str, i) {
+ return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
+ };
+ else if (webkit)
+ spanAffectsWrapping = function(str, i) {
+ if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
+ return true;
+ return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1));
+ };
var knownScrollbarWidth;
function scrollbarWidth(measure) {
if (knownScrollbarWidth != null) return knownScrollbarWidth;
var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll");
removeChildrenAndAdd(measure, test);
if (test.offsetWidth)
knownScrollbarWidth = test.offsetHeight - test.clientHeight;
return knownScrollbarWidth || 0;
}
var zwspSupported;
function zeroWidthElement(measure) {
if (zwspSupported == null) {
var test = elt("span", "\u200b");
removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
if (measure.firstChild.offsetHeight != 0)
zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8;
}
if (zwspSupported) return elt("span", "\u200b");
else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
}
// See if "".split is the broken IE version, if so, provide an
// alternative way to split lines.
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
var pos = 0, result = [], l = string.length;
while (pos <= l) {
var nl = string.indexOf("\n", pos);
if (nl == -1) nl = string.length;
var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
var rt = line.indexOf("\r");
if (rt != -1) {
result.push(line.slice(0, rt));
pos += rt + 1;
} else {
result.push(line);
pos = nl + 1;
}
}
return result;
} : function(string){return string.split(/\r\n?|\n/);};
CodeMirror.splitLines = splitLines;
var hasSelection = window.getSelection ? function(te) {
try { return te.selectionStart != te.selectionEnd; }
catch(e) { return false; }
} : function(te) {
try {var range = te.ownerDocument.selection.createRange();}
catch(e) {}
if (!range || range.parentElement() != te) return false;
return range.compareEndPoints("StartToEnd", range) != 0;
};
var hasCopyEvent = (function() {
var e = elt("div");
if ("oncopy" in e) return true;
e.setAttribute("oncopy", "return;");
return typeof e.oncopy == 'function';
})();
// KEY NAMING
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
CodeMirror.keyNames = keyNames;
(function() {
// Number keys
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i);
// Alphabetic keys
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i);
// Function keys
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i;
})();
// BIDI HELPERS
function iterateBidiSections(order, from, to, f) {
if (!order) return f(from, to, "ltr");
for (var i = 0; i < order.length; ++i) {
var part = order[i];
if (part.from < to && part.to > from || from == to && part.to == from)
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
}
}
function bidiLeft(part) { return part.level % 2 ? part.to : part.from; }
function bidiRight(part) { return part.level % 2 ? part.from : part.to; }
function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; }
function lineRight(line) {
var order = getOrder(line);
if (!order) return line.text.length;
return bidiRight(lst(order));
}
function lineStart(cm, lineN) {
- var line = getLine(cm.view.doc, lineN);
- var visual = visualLine(cm.view.doc, line);
+ var line = getLine(cm.doc, lineN);
+ var visual = visualLine(cm.doc, line);
if (visual != line) lineN = lineNo(visual);
var order = getOrder(visual);
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
- return {line: lineN, ch: ch};
+ return Pos(lineN, ch);
}
- function lineEnd(cm, lineNo) {
+ function lineEnd(cm, lineN) {
var merged, line;
- while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo)))
- lineNo = merged.find().to.line;
+ while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
+ lineN = merged.find().to.line;
var order = getOrder(line);
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
- return {line: lineNo, ch: ch};
+ return Pos(lineN, ch);
+ }
+
+ function compareBidiLevel(order, a, b) {
+ var linedir = order[0].level;
+ if (a == linedir) return true;
+ if (b == linedir) return false;
+ return a < b;
+ }
+ var bidiOther;
+ function getBidiPartAt(order, pos) {
+ for (var i = 0, found; i < order.length; ++i) {
+ var cur = order[i];
+ if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
+ if (cur.from == pos || cur.to == pos) {
+ if (found == null) {
+ found = i;
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
+ bidiOther = found;
+ return i;
+ } else {
+ bidiOther = i;
+ return found;
+ }
+ }
+ }
+ bidiOther = null;
+ return found;
+ }
+
+ function moveInLine(line, pos, dir, byUnit) {
+ if (!byUnit) return pos + dir;
+ do pos += dir;
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
+ return pos;
}
// This is somewhat involved. It is needed in order to move
// 'visually' through bi-directional text -- i.e., pressing left
// should make the cursor go left, even when in RTL text. The
// tricky part is the 'jumps', where RTL and LTR text touch each
// other. This often requires the cursor offset to move more than
// one unit, in order to visually move one unit.
function moveVisually(line, start, dir, byUnit) {
var bidi = getOrder(line);
if (!bidi) return moveLogically(line, start, dir, byUnit);
- var moveOneUnit = byUnit ? function(pos, dir) {
- do pos += dir;
- while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
- return pos;
- } : function(pos, dir) { return pos + dir; };
- var linedir = bidi[0].level;
- for (var i = 0; i < bidi.length; ++i) {
- var part = bidi[i], sticky = part.level % 2 == linedir;
- if ((part.from < start && part.to > start) ||
- (sticky && (part.from == start || part.to == start))) break;
- }
- var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
-
- while (target != null) {
- if (part.level % 2 == linedir) {
- if (target < part.from || target > part.to) {
- part = bidi[i += dir];
- target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
- } else break;
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
+
+ for (;;) {
+ if (target > part.from && target < part.to) return target;
+ if (target == part.from || target == part.to) {
+ if (getBidiPartAt(bidi, target) == pos) return target;
+ part = bidi[pos += dir];
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
} else {
- if (target == bidiLeft(part)) {
- part = bidi[--i];
- target = part && bidiRight(part);
- } else if (target == bidiRight(part)) {
- part = bidi[++i];
- target = part && bidiLeft(part);
- } else break;
+ part = bidi[pos += dir];
+ if (!part) return null;
+ if ((dir > 0) == part.level % 2)
+ target = moveInLine(line, part.to, -1, byUnit);
+ else
+ target = moveInLine(line, part.from, 1, byUnit);
}
}
-
- return target < 0 || target > line.text.length ? null : target;
}
function moveLogically(line, start, dir, byUnit) {
var target = start + dir;
if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir;
return target < 0 || target > line.text.length ? null : target;
}
// Bidirectional ordering algorithm
// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
// that this (partially) implements.
// One-char codes used for character types:
// L (L): Left-to-Right
// R (R): Right-to-Left
// r (AL): Right-to-Left Arabic
// 1 (EN): European Number
// + (ES): European Number Separator
// % (ET): European Number Terminator
// n (AN): Arabic Number
// , (CS): Common Number Separator
// m (NSM): Non-Spacing Mark
// b (BN): Boundary Neutral
// s (B): Paragraph Separator
// t (S): Segment Separator
// w (WS): Whitespace
// N (ON): Other Neutrals
// Returns null if characters are ordered as they appear
// (left-to-right), or an array of sections ({from, to, level}
// objects) in the order in which they occur visually.
var bidiOrdering = (function() {
// Character types for codepoints 0 to 0xff
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL";
// Character types for codepoints 0x600 to 0x6ff
var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr";
function charType(code) {
if (code <= 0xff) return lowTypes.charAt(code);
else if (0x590 <= code && code <= 0x5f4) return "R";
else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600);
else if (0x700 <= code && code <= 0x8ac) return "r";
else return "L";
}
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
// Browsers seem to always treat the boundaries of block elements as being L.
var outerType = "L";
- return function charOrdering(str) {
+ return function(str) {
if (!bidiRE.test(str)) return false;
var len = str.length, types = [];
for (var i = 0, type; i < len; ++i)
types.push(type = charType(str.charCodeAt(i)));
// W1. Examine each non-spacing mark (NSM) in the level run, and
// change the type of the NSM to the type of the previous
// character. If the NSM is at the start of the level run, it will
// get the type of sor.
for (var i = 0, prev = outerType; i < len; ++i) {
var type = types[i];
if (type == "m") types[i] = prev;
else prev = type;
}
// W2. Search backwards from each instance of a European number
// until the first strong type (R, L, AL, or sor) is found. If an
// AL is found, change the type of the European number to Arabic
// number.
// W3. Change all ALs to R.
for (var i = 0, cur = outerType; i < len; ++i) {
var type = types[i];
if (type == "1" && cur == "r") types[i] = "n";
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
}
// W4. A single European separator between two European numbers
// changes to a European number. A single common separator between
// two numbers of the same type changes to that type.
for (var i = 1, prev = types[0]; i < len - 1; ++i) {
var type = types[i];
if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1";
else if (type == "," && prev == types[i+1] &&
(prev == "1" || prev == "n")) types[i] = prev;
prev = type;
}
// W5. A sequence of European terminators adjacent to European
// numbers changes to all European numbers.
// W6. Otherwise, separators and terminators change to Other
// Neutral.
for (var i = 0; i < len; ++i) {
var type = types[i];
if (type == ",") types[i] = "N";
else if (type == "%") {
for (var end = i + 1; end < len && types[end] == "%"; ++end) {}
var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N";
for (var j = i; j < end; ++j) types[j] = replace;
i = end - 1;
}
}
// W7. Search backwards from each instance of a European number
// until the first strong type (R, L, or sor) is found. If an L is
// found, then change the type of the European number to L.
for (var i = 0, cur = outerType; i < len; ++i) {
var type = types[i];
if (cur == "L" && type == "1") types[i] = "L";
else if (isStrong.test(type)) cur = type;
}
// N1. A sequence of neutrals takes the direction of the
// surrounding strong text if the text on both sides has the same
// direction. European and Arabic numbers act as if they were R in
// terms of their influence on neutrals. Start-of-level-run (sor)
// and end-of-level-run (eor) are used at level run boundaries.
// N2. Any remaining neutrals take the embedding direction.
for (var i = 0; i < len; ++i) {
if (isNeutral.test(types[i])) {
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
var before = (i ? types[i-1] : outerType) == "L";
var after = (end < len - 1 ? types[end] : outerType) == "L";
var replace = before || after ? "L" : "R";
for (var j = i; j < end; ++j) types[j] = replace;
i = end - 1;
}
}
// Here we depart from the documented algorithm, in order to avoid
// building up an actual levels array. Since there are only three
// levels (0, 1, 2) in an implementation that doesn't take
// explicit embedding into account, we can build up the order on
// the fly, without following the level-based algorithm.
var order = [], m;
for (var i = 0; i < len;) {
if (countsAsLeft.test(types[i])) {
var start = i;
for (++i; i < len && countsAsLeft.test(types[i]); ++i) {}
order.push({from: start, to: i, level: 0});
} else {
var pos = i, at = order.length;
for (++i; i < len && types[i] != "L"; ++i) {}
for (var j = pos; j < i;) {
if (countsAsNum.test(types[j])) {
if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1});
var nstart = j;
for (++j; j < i && countsAsNum.test(types[j]); ++j) {}
order.splice(at, 0, {from: nstart, to: j, level: 2});
pos = j;
} else ++j;
}
if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1});
}
}
if (order[0].level == 1 && (m = str.match(/^\s+/))) {
order[0].from = m[0].length;
order.unshift({from: 0, to: m[0].length, level: 0});
}
if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
lst(order).to -= m[0].length;
order.push({from: len - m[0].length, to: len, level: 0});
}
if (order[0].level != lst(order).level)
order.push({from: len, to: len, level: order[0].level});
return order;
};
})();
// THE END
- CodeMirror.version = "3.02";
+ CodeMirror.version = "3.14.0";
return CodeMirror;
})();
Index: branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.css
===================================================================
--- branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.css (revision 15913)
+++ branches/5.3.x/core/admin_templates/incs/code_mirror/lib/codemirror.css (revision 15914)
@@ -1,240 +1,248 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
-.CodeMirror-scrollbar-filler {
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
+ white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
}
/* CURSOR */
.CodeMirror div.CodeMirror-cursor {
border-left: 1px solid black;
+ z-index: 3;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
- background: transparent;
- background: rgba(0, 200, 0, .4);
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
-}
-/* Kludge to turn off filter in ie9+, which also accepts rgba */
-.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor:not(#nonsense_id) {
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ background: #7e7;
+ z-index: 1;
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
+.cm-tab { display: inline-block; }
+
/* DEFAULT THEME */
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
-.cm-emstrong {font-style: italic; font-weight: bold;}
.cm-link {text-decoration: underline;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
+ background: white;
+ color: black;
}
.CodeMirror-scroll {
/* 30px is the magic margin used to hide the element's real scrollbars */
- /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
+ /* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px; padding-right: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
- z-index: 6;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
- height: 100%;
+ padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
+ white-space: normal;
height: 100%;
+ padding-bottom: 30px;
+ margin-bottom: -32px;
display: inline-block;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-lines {
cursor: text;
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
- -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
+.CodeMirror-widget {
+}
+
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror-measure {
position: absolute;
width: 100%; height: 0px;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor {
position: absolute;
visibility: hidden;
border-right: none;
width: 0;
}
.CodeMirror-focused div.CodeMirror-cursor {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursor {
visibility: hidden;
}
}
Index: branches/5.3.x/core/install/upgrades.css
===================================================================
--- branches/5.3.x/core/install/upgrades.css (revision 15913)
+++ branches/5.3.x/core/install/upgrades.css (revision 15914)
@@ -1,857 +1,860 @@
# ===== v 5.0.0 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 1.2.8.6)
+++ style_template.css (working copy)
@@ -14,13 +14,17 @@
body {
font-family: verdana,arial,helvetica,sans-serif;
- font-size: 9pt;
color: #000000;
overflow-x: auto; overflow-y: auto;
margin: 0px 0px 0px 0px;
text-decoration: none;
}
+body, td {
+ /* fix for Firefox, when font-size was not inherited in table cells */
+ font-size: 9pt;
+}
+
a {
color: #006699;
text-decoration: none;
@@ -73,21 +77,36 @@
}
/* Head frame */
+table.head-table {
+ background: url(@@base_url@@/core/admin_templates/img/top_frame/right_background.jpg) top right @@HeadBgColor@@ no-repeat;
+}
+
.head-table tr td {
- background-color: @@HeadBgColor@@;
color: @@HeadColor@@
}
+div#extra_toolbar td.button-active {
+ background: url(@@base_url@@/core/admin_templates/img/top_frame/toolbar_button_background.gif) bottom left repeat-x;
+ height: 22px;
+}
+
+div#extra_toolbar td.button-active a {
+ color: black;
+ text-decoration: none;
+}
+
td.kx-block-header, .head-table tr td.kx-block-header{
color: @@HeadBarColor@@;
- background-color: @@HeadBarBgColor@@;
+ background: url(@@base_url@@/core/admin_templates/img/top_frame/toolbar_background.gif) repeat-x top left;
+ /*background-color: @@HeadBarBgColor@@;*/
padding-left: 7px;
padding-right: 7px;
}
a.kx-header-link {
text-decoration: underline;
- color: #FFFFFF;
+ font-weight: bold;
+ color: #0080C8;
}
a.kx-header-link:hover {
@@ -96,8 +115,8 @@
}
.kx-secondary-foreground {
- color: @@HeadBarColor@@;
- background-color: @@HeadBarBgColor@@;
+ color: #FFFFFF;
+ /*background-color: @@HeadBarBgColor@@;*/
}
.kx-login-button {
@@ -110,7 +129,7 @@
font-size: 12px;
font-weight: normal;
color: #000000;
- background: url(@@base_url@@/proj-base/admin_templates/img/button_back.gif) #f9eeae repeat-x;
+ background: url(@@base_url@@/core/admin_templates/img/button_back.gif) #f9eeae repeat-x;
text-decoration: none;
}
@@ -119,7 +138,7 @@
font-size: 12px;
font-weight: normal;
color: #676767;
- background: url(@@base_url@@/proj-base/admin_templates/img/button_back_disabled.gif) #f9eeae repeat-x;
+ background: url(@@base_url@@/core/admin_templates/img/button_back_disabled.gif) #f9eeae repeat-x;
text-decoration: none;
}
@@ -131,23 +150,48 @@
border-top: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
+ margin-left: 3px !important;
+ white-space: nowrap;
}
.tab-active {
- background-color: #2D79D6;
- border-bottom: 1px solid #2D79D6;
+ background-color: #4487D9;
}
.tab a {
- color: #00659C;
+ color: #4487D9;
font-weight: bold;
}
.tab-active a {
- color: #fff;
+ color: #FFFFFF;
font-weight: bold;
}
+a.scroll-left, a.scroll-right {
+ cursor: pointer;
+ display: block;
+ float: left;
+ height: 18px;
+ margin: 0px 1px;
+ width: 18px;
+}
+
+a.scroll-left {
+ background: transparent url(@@base_url@@/core/admin_templates/img/tabs/left.png) no-repeat scroll 0 0;
+}
+
+a.scroll-right {
+ background: transparent url(@@base_url@@/core/admin_templates/img/tabs/right.png) no-repeat scroll 0 0;
+}
+
+a.disabled {
+ visibility: hidden !important;
+}
+
+a.scroll-left:hover, a.scroll-right:hover {
+ background-position: 0 -18px;
+}
/* Toolbar */
@@ -255,7 +299,7 @@
}
/* Filters row */
-tr.grid-header-row-0 td {
+tr.grid-header-row-1 td {
background-color: @@FiltersBgColor@@;
border-bottom: 1px solid black;
}
@@ -288,18 +332,19 @@
}
/* Column titles row */
-tr.grid-header-row-1 td {
+tr.grid-header-row-0 td {
height: 25px;
font-weight: bold;
background-color: @@ColumnTitlesBgColor@@;
color: @@ColumnTitlesColor@@;
+ border-bottom: 1px solid black;
}
-tr.grid-header-row-1 td a {
+tr.grid-header-row-0 td a {
color: @@ColumnTitlesColor@@;
}
-tr.grid-header-row-1 td a:hover {
+tr.grid-header-row-0 td a:hover {
color: #FFCC00;
}
@@ -307,7 +352,7 @@
.grid-footer-row td {
background-color: #D7D7D7;
font-weight: bold;
- border-right: none;
+ border-right: 1px solid #C9C9C9;
padding: 3px 5px 3px 5px;
}
@@ -320,12 +365,12 @@
vertical-align: middle !important;
}
-tr.grid-header-row-0 td.grid-header-col-0 {
+tr.grid-header-row-1 td.grid-header-col-1 {
text-align: center;
vertical-align: middle !important;
}
-tr.grid-header-row-0 td.grid-header-col-0 div {
+tr.grid-header-row-1 td.grid-header-col-1 div {
display: table-cell;
vertical-align: middle;
}
@@ -374,11 +419,12 @@
.subsectiontitle td {
vertical-align: middle;
- padding: 3px 5px 3px 5px;
+ /*padding: 3px 5px 3px 5px;*/
+ padding: 1px 5px;
}
.label-cell {
- background: #DEE7F6 url(@@base_url@@/proj-base/admin_templates/img/bgr_input_name_line.gif) no-repeat right bottom;
+ background: #DEE7F6 url(@@base_url@@/core/admin_templates/img/bgr_input_name_line.gif) no-repeat right bottom;
font: 12px arial, sans-serif;
padding: 4px 20px;
width: 150px;
@@ -387,13 +433,13 @@
.control-mid {
width: 13px;
border-left: 1px solid #7A95C2;
- background: #fff url(@@base_url@@/proj-base/admin_templates/img/bgr_mid.gif) repeat-x left bottom;
+ background: #fff url(@@base_url@@/core/admin_templates/img/bgr_mid.gif) repeat-x left bottom;
}
.control-cell {
font: 11px arial, sans-serif;
padding: 4px 10px 5px 5px;
- background: #fff url(@@base_url@@/proj-base/admin_templates/img/bgr_input_line.gif) no-repeat left bottom;
+ background: #fff url(@@base_url@@/core/admin_templates/img/bgr_input_line.gif) no-repeat left bottom;
width: auto;
vertical-align: middle;
}
@@ -420,8 +466,28 @@
color: red;
}
+.warning-table {
+ background-color: #F0F1EB;
+ border: 1px solid #000000;
+ border-collapse: collapse;
+ border-top-width: 0px;
+}
+
.form-warning {
color: red;
+ font-size: 11px;
+}
+
+.priority {
+ color: red;
+ padding-left: 1px;
+ padding-right: 1px;
+ font-size: 11px;
+}
+
+.small-statistics {
+ font-size: 11px;
+ color: #707070;
}
.req-note {
@@ -474,7 +540,13 @@
/* To be sorted */
+span#category_path, span#category_path a {
+ color: #FFFFFF;
+}
+span#category_path a {
+ text-decoration: underline;
+}
/* Section title, right to the big icon */
.admintitle {
@@ -484,7 +556,7 @@
text-decoration: none;
}
-/* Left sid of bluebar */
+/* Left side of bluebar */
.header_left_bg {
background-color: @@TitleBarBgColor@@;
background-image: none;
@@ -560,7 +632,7 @@
color: @@TreeColor@@;
font-family: Helvetica, Arial, Verdana;
text-decoration: none;
- padding: 2px 0px 2px 2px;
+ padding: 2px;
}
.tree tr.highlighted td a {
# ===== v 5.0.1 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 13764)
+++ style_template.css (working copy)
@@ -78,15 +78,15 @@
/* Head frame */
table.head-table {
- background: url(@@base_url@@/core/admin_templates/img/top_frame/right_background.jpg) top right @@HeadBgColor@@ no-repeat;
+ background: url('@@base_url@@/core/admin_templates/img/top_frame/right_background.png') top right @@HeadBgColor@@ no-repeat;
}
-.head-table tr td {
+.head-table tr td, .head-table tr td a {
color: @@HeadColor@@
}
div#extra_toolbar td.button-active {
- background: url(@@base_url@@/core/admin_templates/img/top_frame/toolbar_button_background.gif) bottom left repeat-x;
+ background: url('@@base_url@@/core/admin_templates/img/top_frame/toolbar_button_background.gif') bottom left repeat-x;
height: 22px;
}
@@ -97,7 +97,7 @@
td.kx-block-header, .head-table tr td.kx-block-header{
color: @@HeadBarColor@@;
- background: url(@@base_url@@/core/admin_templates/img/top_frame/toolbar_background.gif) repeat-x top left;
+ background: url('@@base_url@@/core/admin_templates/img/top_frame/toolbar_background.gif') repeat-x top left;
/*background-color: @@HeadBarBgColor@@;*/
padding-left: 7px;
padding-right: 7px;
@@ -129,7 +129,7 @@
font-size: 12px;
font-weight: normal;
color: #000000;
- background: url(@@base_url@@/core/admin_templates/img/button_back.gif) #f9eeae repeat-x;
+ background: url('@@base_url@@/core/admin_templates/img/button_back.gif') #f9eeae repeat-x;
text-decoration: none;
}
@@ -138,7 +138,7 @@
font-size: 12px;
font-weight: normal;
color: #676767;
- background: url(@@base_url@@/core/admin_templates/img/button_back_disabled.gif) #f9eeae repeat-x;
+ background: url('@@base_url@@/core/admin_templates/img/button_back_disabled.gif') #f9eeae repeat-x;
text-decoration: none;
}
@@ -178,11 +178,11 @@
}
a.scroll-left {
- background: transparent url(@@base_url@@/core/admin_templates/img/tabs/left.png) no-repeat scroll 0 0;
+ background: transparent url('@@base_url@@/core/admin_templates/img/tabs/left.png') no-repeat scroll 0 0;
}
a.scroll-right {
- background: transparent url(@@base_url@@/core/admin_templates/img/tabs/right.png) no-repeat scroll 0 0;
+ background: transparent url('@@base_url@@/core/admin_templates/img/tabs/right.png') no-repeat scroll 0 0;
}
a.disabled {
@@ -193,6 +193,19 @@
background-position: 0 -18px;
}
+td.scroll-right-container {
+ width: 20px;
+}
+
+td.scroll-right-container.disabled, td.scroll-right-container.disabled * {
+ width: 0px;
+ margin: 0px;
+}
+
+td.scroll-right-container.disabled br {
+ display: none;
+}
+
/* Toolbar */
.toolbar {
@@ -424,22 +437,22 @@
}
.label-cell {
- background: #DEE7F6 url(@@base_url@@/core/admin_templates/img/bgr_input_name_line.gif) no-repeat right bottom;
+ background: #DEE7F6 url('@@base_url@@/core/admin_templates/img/bgr_input_name_line.gif') no-repeat right bottom;
font: 12px arial, sans-serif;
padding: 4px 20px;
- width: 150px;
+ width: 160px;
}
.control-mid {
width: 13px;
border-left: 1px solid #7A95C2;
- background: #fff url(@@base_url@@/core/admin_templates/img/bgr_mid.gif) repeat-x left bottom;
+ background: #fff url('@@base_url@@/core/admin_templates/img/bgr_mid.gif') repeat-x left bottom;
}
.control-cell {
font: 11px arial, sans-serif;
padding: 4px 10px 5px 5px;
- background: #fff url(@@base_url@@/core/admin_templates/img/bgr_input_line.gif) no-repeat left bottom;
+ background: #fff url('@@base_url@@/core/admin_templates/img/bgr_input_line.gif') no-repeat left bottom;
width: auto;
vertical-align: middle;
}
@@ -528,14 +541,14 @@
width: 100%;
border: 1px solid black;
height: 20px;
- background: #fff url(@@base_url@@/core/admin_templates/img/progress_left.gif);
+ background: #fff url('@@base_url@@/core/admin_templates/img/progress_left.gif');
}
.uploader-done {
width: 0%;
background-color: green;
height: 20px;
- background: #4A92CE url(@@base_url@@/core/admin_templates/img/progress_done.gif);
+ background: #4A92CE url('@@base_url@@/core/admin_templates/img/progress_done.gif');
}
@@ -635,15 +648,15 @@
padding: 2px;
}
+.tree tr td a:hover {
+ color: @@TreeHoverColor@@;
+}
+
.tree tr.highlighted td a {
+ color: @@TreeHighColor@@;
background-color: @@TreeHighBgColor@@;
- color: @@TreeHighColor@@;
}
.tree tr.highlighted td a:hover {
- color: #fff;
-}
-
-.tree tr td a:hover {
- color: #000000;
+ color: @@TreeHighHoverColor@@;
}
\ No newline at end of file
# ===== v 5.1.0-B1 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 13764)
+++ style_template.css (working copy)
@@ -648,15 +648,36 @@
padding: 2px;
}
-.tree tr td a:hover {
+.tree tr td a:hover, .tree tr td a.debug-only-item:hover {
color: @@TreeHoverColor@@;
}
-.tree tr.highlighted td a {
+.tree tr.highlighted td a, .tree tr.highlighted td a.debug-only-item {
color: @@TreeHighColor@@;
background-color: @@TreeHighBgColor@@;
}
.tree tr.highlighted td a:hover {
color: @@TreeHighHoverColor@@;
+}
+
+.tree tr td a.debug-only-item {
+ color: grey;
+}
+
+/* Ajax Dropdown */
+.suggest-box {
+ border: 1px solid #999;
+ background-color: #fff;
+}
+
+.suggest-item, .suggest-item-over {
+ padding: 1px 2px 0px 2px;
+ font-family: arial,verdana;
+ font-size: 12px;
+}
+
+.suggest-item-over {
+ background-color: #3366CC;
+ color: #fff;
}
\ No newline at end of file
# ===== v 5.1.0-B2 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 13764)
+++ style_template.css (working copy)
@@ -512,45 +512,49 @@
border-collapse: separate
}
-
/* Uploader */
-
-.uploader-main {
- position: absolute;
- display: none;
- z-index: 10;
- border: 1px solid #777;
- padding: 10px;
- width: 350px;
- height: 120px;
- overflow: hidden;
- background-color: #fff;
+.uploader-queue div.file {
+ font-size: 11px;
+ border: 1px solid #7F99C5;
+ padding: 3px;
+ background-color: #DEE7F6;
+ margin-bottom: 2px;
}
-.uploader-percent {
- width: 100%;
- padding-top: 3px;
- text-align: center;
- position: relative;
- z-index: 20;
+.uploader-queue .left {
float: left;
- font-weight: bold;
+ vertical-align: top;
}
-.uploader-left {
- width: 100%;
+.uploader-queue .file-label {
+ margin-left: 5px;
+}
+
+.uploader-queue .preview .delete-checkbox {
+ margin-top: -3px;
+}
+
+.uploader-queue .progress-container {
+ margin: 2px 5px 0px 5px;
+}
+
+.uploader-queue .progress-empty {
+ width: 150px;
+ height: 9px;
border: 1px solid black;
- height: 20px;
- background: #fff url('@@base_url@@/core/admin_templates/img/progress_left.gif');
+ background: url('@@base_url@@/core/admin_templates/img/progress_left.gif') repeat-x;
}
-.uploader-done {
- width: 0%;
- background-color: green;
- height: 20px;
- background: #4A92CE url('@@base_url@@/core/admin_templates/img/progress_done.gif');
+.uploader-queue .progress-full {
+ height: 9px;
+ background: url('@@base_url@@/core/admin_templates/img/progress_done.gif');
}
+.uploader-queue .thumbnail {
+ /*margin-bottom: 2px;*/
+ border: 1px solid black;
+ background-color: grey;
+}
/* To be sorted */
span#category_path, span#category_path a {
# ===== v 5.1.1-B1 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 13862)
+++ style_template.css (working copy)
@@ -61,10 +61,12 @@
.bordered, table.bordered, .bordered-no-bottom {
border: 1px solid #000000;
+ border-top-width: 0px;
border-collapse: collapse;
}
.bordered-no-bottom {
+ border-top-width: 1px;
border-bottom: none;
}
@@ -430,6 +432,11 @@
vertical-align: middle;
}
+/* remove top-border from first sub-section element */
+table.edit-form .subsectiontitle:first-child, table.bordered .subsectiontitle:first-child {
+ border-top-width: 0;
+}
+
.subsectiontitle td {
vertical-align: middle;
/*padding: 3px 5px 3px 5px;*/
@@ -684,4 +691,64 @@
.suggest-item-over {
background-color: #3366CC;
color: #fff;
+}
+
+/* Dashboard Summary Boxes */
+.summary-box {
+ border: 1px solid black;
+ margin-bottom: 4px;
+}
+
+.summary-box .title {
+ color: white;
+ font-weight: bold;
+ padding: 6px 5px;
+ vertical-align: middle;
+ background-color: #4A92CE;
+ border-bottom: 1px solid black;
+}
+
+.summary-box .content {
+ padding: 4px;
+ background-color: #F6F6F6;
+}
+
+.summary-box .group {
+ border-bottom: 1px solid black;
+ margin-bottom: 10px;
+ padding: 0 0 10px 10px;
+}
+
+.summary-box .group.last {
+ border-width: 0px;
+ margin-bottom: 0;
+ padding-bottom: 5px;
+}
+
+.summary-box h4 {
+ margin: 0;
+ padding: 0 0 3px 0;
+ font-size: 11px;
+ font-weight: bold;
+}
+
+.summary-box .hint {
+ font-size: 10px;
+ color: grey;
+ margin-bottom: 3px;
+}
+
+.summary-box .hint .cache-key {
+ margin-bottom: 7px;
+ margin-left: 3px;
+}
+
+.summary-box ul {
+ margin-top: 5px;
+ margin-bottom: 3px;
+ padding-left: 30px;
+}
+
+.summary-box li {
+ padding-bottom: 4px;
}
\ No newline at end of file
# ===== v 5.2.0-B1 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 14590)
+++ style_template.css (working copy)
@@ -60,7 +60,7 @@
}
.bordered, table.bordered, .bordered-no-bottom {
- border: 1px solid #000000;
+ border: 1px solid #000000 !important;
border-top-width: 0px;
border-collapse: collapse;
}
@@ -269,7 +269,7 @@
}
/* Main row definition */
-.grid-data-row td, .grid-data-row-selected td, .grid-data-row-even-selected td, .grid-data-row-mouseover td, .table-color1, .table-color2 {
+.grid-data-row td, .grid-data-row-selected td, .grid-data-row-even-selected td, .grid-data-row-mouseover td, .table-color1, .table-color2, .grid-edit-table .edit-form-odd > td, .grid-edit-table .edit-form-even > td {
font-weight: normal;
color: @@OddColor@@;
background-color: @@OddBgColor@@;
@@ -277,7 +277,7 @@
overflow: hidden;
border-right: 1px solid #c9c9c9;
}
-.grid-data-row-even td, .table-color2 {
+.grid-data-row-even td, .table-color2, .grid-edit-table .edit-form-even > td {
background-color: @@EvenBgColor@@;
color: @@EvenColor@@;
}
@@ -346,6 +346,29 @@
background-color: #FFFF00;
}
+div.filter, div.filter-active {
+ background-color: white;
+ border: 1px solid #AAAAAA;
+ color: black;
+ font-weight: normal;
+ padding: 3px;
+}
+
+div.filter-active {
+ background-color: #FFFF00;
+}
+
+div.multioptions_filter {
+ position: absolute;
+ z-index: 100;
+ color: black;
+ background-color: white;
+ border: 1px solid black;
+ padding: 3px 5px;
+ display: none;
+ vertical-align: middle;
+}
+
/* Column titles row */
tr.grid-header-row-0 td {
height: 25px;
@@ -413,7 +436,7 @@
/* Forms */
table.edit-form {
border: none;
- border-top-width: 0px;
+ border-top-width: 0px !important;
border-collapse: collapse;
width: 100%;
}
@@ -493,11 +516,18 @@
border-top-width: 0px;
}
+.form-notice, .form-warning {
+ font-size: 11px;
+}
+
.form-warning {
color: red;
- font-size: 11px;
}
+.form-notice {
+ color: green;
+}
+
.priority {
color: red;
padding-left: 1px;
@@ -751,4 +781,17 @@
.summary-box li {
padding-bottom: 4px;
+}
+
+span.cke_skin_kama {
+ border-width: 0px !important;
+ -moz-border-radius: 0px !important;
+ -webkit-border-radius: 0px !important;
+ padding: 0px !important;
+}
+
+.cke_wrapper{
+ border-width: 0px !important;
+ -moz-border-radius: 0px !important;
+ -webkit-border-radius: 0px !important;
}
\ No newline at end of file
# ===== v 5.2.0 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 15359)
+++ style_template.css (working copy)
@@ -424,7 +424,7 @@
.grid-status-bar td {
background-color: @@TitleBarBgColor@@;
- color: @@TitleBarColor@@;
+ color: @@TitleBarColor@@;
font-size: 11pt;
font-weight: normal;
padding: 2px 8px 2px 8px;
@@ -610,11 +610,13 @@
text-decoration: none;
}
-/* Left side of bluebar */
-.header_left_bg {
+/* Page header (bluebar) */
+.page-title td {
background-color: @@TitleBarBgColor@@;
- background-image: none;
- padding-left: 5px;
+ color: @@TitleBarColor@@;
+ font-size: 11pt;
+ font-weight: normal;
+ padding: 2px 8px 2px 8px;
}
/* Right side of bluebar */
# ===== v 5.3.0-B1 =====
Index: style_template.css
===================================================================
--- style_template.css (revision 15483)
+++ style_template.css (working copy)
-@@ -487,6 +487,11 @@
+@@ -487,6 +487,14 @@
vertical-align: middle;
}
+.CodeMirror {
+ font-size: 13px;
+ border: 1px solid black;
+}
+
++.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
++.CodeMirror-activeline-background {background: #e8f2ff !important;}
++
.label-cell-filler {
background: #DEE7F6 none;
}
-@@ -496,6 +501,18 @@
+@@ -496,6 +504,18 @@
}
.control-cell-filler {
background: #fff none;
+}
+
+.highlight-area, .code-highlight-area {
+ border: 1px solid black;
+ padding: 8px;
+ font-family: monospace !important;
+ font-size: 12px;
+ overflow: auto;
+}
+
+.code-highlight-area {
+ background-color: #F6F6F6;
}
.error {
\ No newline at end of file

Event Timeline