From a84d99e183afaa4cb451316488234199a54c3aad Mon Sep 17 00:00:00 2001 From: Freddy Daniel Rojas Valda Date: Wed, 12 Feb 2014 17:09:51 -0400 Subject: [PATCH 1/6] Se adiciona DOWNLOAD para FILES MANAGER. Se modifican get, post, put, delete para subdirectorios FILES MANAGER --- .../engine/src/BusinessModel/FilesManager.php | 222 ++++++++++++------ .../Api/ProcessMaker/Project/FilesManager.php | 13 +- 2 files changed, 153 insertions(+), 82 deletions(-) diff --git a/workflow/engine/src/BusinessModel/FilesManager.php b/workflow/engine/src/BusinessModel/FilesManager.php index c2e142f9f..c80e80bce 100644 --- a/workflow/engine/src/BusinessModel/FilesManager.php +++ b/workflow/engine/src/BusinessModel/FilesManager.php @@ -45,16 +45,17 @@ class FilesManager { try { $sMainDirectory = current(explode("/", $path)); - if ($path) - $sSubDirectory = substr($path, strpos($path, "/")); + if(strstr($path,'/')){ + $sSubDirectory = substr($path, strpos($path, "/")+1). PATH_SEP ; + } else { + $sSubDirectory = ''; + } switch ($sMainDirectory) { case 'templates': - //$sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory; - $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP; + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory; break; case 'folder': - //$sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory; - $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP; + $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory; break; default: die(); @@ -64,7 +65,6 @@ class FilesManager $aTheFiles = array(); $aDirectories = array(); $aFiles = array(); - $sCurrentDirectory = $sSubDirectory; $oDirectory = dir($sDirectory); while ($sObject = $oDirectory->read()) { if (($sObject !== '.') && ($sObject !== '..')) { @@ -72,8 +72,7 @@ class FilesManager if (is_dir($sPath)) { $aTheFiles[] = array('name' => $sObject, 'type' => "folder", - //'path' => $sDirectory); - 'path' => ($sCurrentDirectory != '' ? $sCurrentDirectory . PATH_SEP : '') . $sObject); + 'path' => $sDirectory); } else { $aAux = pathinfo($sPath); $aAux['extension'] = (isset($aAux['extension'])?$aAux['extension']:''); @@ -93,7 +92,7 @@ class FilesManager 'type' => "file", 'path' => $sDirectory, 'editable' => $sEditable); - } + } return $aTheFiles; } catch (Exception $e) { throw $e; @@ -114,22 +113,30 @@ class FilesManager public function addProcessFilesManager($sProcessUID, $userUID, $aData) { try { - if ($aData['path'] == 'templates/' || $aData['path'] == 'folder/') { - switch ($aData['path']) { - case 'templates/': - $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $aData['file_name']; - $sEditable = false; - break; - case 'folder/': - $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $aData['file_name']; - break; - default: - $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $aData['file_name']; - break; - } - } else { + $aData['path'] = rtrim($aData['path'], '/') . '/'; + $sMainDirectory = current(explode("/", $aData['path'])); + if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); } + if(strstr($aData['path'],'/')){ + $sSubDirectory = substr($aData['path'], strpos($aData['path'], "/")+1) ; + } else { + $sSubDirectory = ''; + } + switch ($sMainDirectory) { + case 'templates': + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; + $sCheckDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory; + $sEditable = false; + break; + case 'folder': + $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; + $sCheckDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory; + break; + default: + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; + break; + } $extention = end(explode(".", $aData['file_name'])); if ($extention == 'docx' || $extention == 'doc' || $extention == 'html' || $extention == 'php' || $extention == 'jsp' || $extention == 'xlsx' || $extention == 'xls' || $extention == 'js' || $extention == 'css' || $extention == 'txt') { @@ -137,18 +144,19 @@ class FilesManager } else { $sEditable = false; } + \G::verifyPath($sCheckDirectory, true); if (file_exists(PATH_SEP.$sDirectory)) { throw (new \Exception( 'The file: '. $sDirectory . ' exists.')); } $sPkProcessFiles = \G::generateUniqueID(); $oProcessFiles = new \ProcessFiles(); - $sDate = date( 'Y-m-d H:i' ); + $sDate = date('Y-m-d H:i'); $oProcessFiles->setPrfUid( $sPkProcessFiles ); $oProcessFiles->setProUid( $sProcessUID ); $oProcessFiles->setUsrUid( $userUID ); $oProcessFiles->setPrfUpdateUsrUid( '' ); $oProcessFiles->setPrfPath( $sDirectory ); - $oProcessFiles->setPrfType( 'file' ); + $oProcessFiles->setPrfType('file'); $oProcessFiles->setPrfEditable( $sEditable ); $oProcessFiles->setPrfCreateDate( $sDate ); $oProcessFiles->save(); @@ -225,23 +233,32 @@ class FilesManager public function updateProcessFilesManager($sProcessUID, $userUID, $aData, $path) { try { - $arrayTaskUid = $this->getFileManagerUid($path); - if ($aData['path'] == 'templates/' || $aData['path'] == 'folder/') { - switch ($aData['path']) { - case 'templates/': - $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $aData['file_name']; - $sEditable = false; - break; - case 'folder/': - $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $aData['file_name']; - break; - default: - $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $aData['file_name']; - break; - } - } else { + $path = rtrim($path, '/') . '/'; + $sMainDirectory = current(explode("/", $path)); + if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); } + if(strstr($path,'/')){ + $sSubDirectory = substr($path, strpos($path, "/")+1) ; + } else { + $sSubDirectory = ''; + } + switch ($sMainDirectory) { + case 'templates': + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; + $sEditable = false; + break; + case 'folder': + $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; + break; + default: + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $aData['file_name']; + break; + } + $arrayTaskUid = $this->getFileManagerUid($sDirectory); + if (!$arrayTaskUid){ + throw (new \Exception( 'invalid value specified for `path`.')); + } $extention = end(explode(".", $aData['file_name'])); if ($extention == 'docx' || $extention == 'doc' || $extention == 'html' || $extention == 'php' || $extention == 'jsp' || $extention == 'xlsx' || $extention == 'xls' || $extention == 'js' || $extention == 'css' || $extention == 'txt') { @@ -251,13 +268,13 @@ class FilesManager } $sPkProcessFiles = \G::generateUniqueID(); $oProcessFiles = new \ProcessFiles(); - $sDate = date( 'Y-m-d H:i' ); - $oProcessFiles->setPrfUid( $sPkProcessFiles ); + $sDate = date('Y-m-d H:i'); + $oProcessFiles->setPrfUid( $sPkProcessFiles); $oProcessFiles->setProUid( $sProcessUID ); $oProcessFiles->setUsrUid( $userUID ); $oProcessFiles->setPrfUpdateUsrUid( '' ); $oProcessFiles->setPrfPath( $sDirectory ); - $oProcessFiles->setPrfType( 'file' ); + $oProcessFiles->setPrfType('file'); $oProcessFiles->setPrfEditable( $sEditable ); $oProcessFiles->setPrfCreateDate( $sDate ); $oProcessFiles->save(); @@ -291,15 +308,48 @@ class FilesManager public function deleteProcessFilesManager($sProcessUID, $path) { try { - $sPath = explode("/", $path); + $sMainDirectory = current(explode("/", $path)); + if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); + } + if ($sMainDirectory == 'folder') { + $sMainDirectory = 'public'; + } else { + $sMainDirectory = 'mailTemplates'; + } $sfile = end(explode("/",$path)); - $main = implode(array_slice($sPath, -3, 1)); - $carpeta = ''; + $sSubDirectorytemp = substr($path, strpos($path, "/")+1); + if(strstr($sSubDirectorytemp,'/')){ + $sSubDirectory = str_replace('/'.$sfile,"",$sSubDirectorytemp); + $sSubDirectoryCheck = str_replace($sfile,"",$sSubDirectorytemp); + } else { + $sSubDirectory = ''; + $sSubDirectoryCheck = ''; + } + switch ($sMainDirectory) { + case 'mailTemplates': + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectoryCheck . $sfile; + $sEditable = false; + break; + case 'public': + $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectoryCheck . $sfile; + break; + default: + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sfile; + break; + } + $arrayTaskUid = $this->getFileManagerUid($sDirectory); + if (!$arrayTaskUid){ + throw (new \Exception( 'invalid value specified for `path`.')); + } $oProcessMap = new \processMap(new \DBConnection()); $oProcessMap->deleteFile($sProcessUID, - $main, - $carpeta, + $sMainDirectory, + $sSubDirectory, $sfile); + $c = new \Criteria("workflow"); + $c->add(\ProcessFilesPeer::PRF_PATH, $sDirectory, \Criteria::EQUAL); + $rs = \ProcessFilesPeer::doDelete($c); } catch (Exception $e) { throw $e; } @@ -316,34 +366,54 @@ class FilesManager public function downloadProcessFilesManager($sProcessUID, $path) { try { - $path = PATH_SEP.$path; - //$url = http: - - if (file_exists($path)) { - $sfile = end(explode("/",$path)); - \G::streamFile($path, true); - # open file to write - $fp = fopen ($path, 'w+'); - # start curl - $ch = curl_init(); - curl_setopt( $ch, CURLOPT_URL, $url ); - # set return transfer to false - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false ); - curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true ); - curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); - # increase timeout to download big file - curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 10 ); - # write data to local file - curl_setopt( $ch, CURLOPT_FILE, $fp ); - # execute curl - curl_exec( $ch ); - # close curl - curl_close( $ch ); - # close local file - fclose( $fp ); - - if (filesize($path) > 0) return true; + $sMainDirectory = current(explode("/", $path)); + if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); } + if ($sMainDirectory == 'folder') { + $sMainDirectory = 'public'; + } else { + $sMainDirectory = 'mailTemplates'; + } + $sfile = end(explode("/",$path)); + $sSubDirectorytemp = substr($path, strpos($path, "/")+1); + if(strstr($sSubDirectorytemp,'/')){ + $sSubDirectory = str_replace('/'.$sfile,"",$sSubDirectorytemp); + $sSubDirectoryCheck = str_replace($sfile,"",$sSubDirectorytemp); + } else { + $sSubDirectory = ''; + $sSubDirectoryCheck = ''; + } + switch ($sMainDirectory) { + case 'mailTemplates': + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectoryCheck . $sfile; + $sEditable = false; + break; + case 'public': + $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectoryCheck . $sfile; + break; + default: + $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sfile; + break; + } + $arrayTaskUid = $this->getFileManagerUid($sDirectory); + if (!$arrayTaskUid){ + throw (new \Exception( 'invalid value specified for `path`.')); + } + /* + This is usefull when you are downloading big files, as it + will prevent time out of the script : + */ + set_time_limit(0); + ini_set('display_errors',true);//Just in case we get some errors, let us know.... + $fp = fopen ($sDirectory, 'w+');//This is the file where we save the information + $ch = curl_init($sDirectory);//Here is the file we are downloading + curl_setopt($ch, CURLOPT_TIMEOUT, 50); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_exec($ch); + curl_close($ch); + fclose($fp); } catch (Exception $e) { throw $e; } diff --git a/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php b/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php index 077223192..bdac76ce8 100644 --- a/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php +++ b/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php @@ -80,12 +80,12 @@ class FilesManager extends Api /** * @param string $prjUid {@min 32} {@max 32} + * @param ProcessFilesManagerStructurePut $request_data * @param string $path - * @param ProcessFilesManagerStructure1 $request_data * - * @url PUT /:prjUid/process-file-manager1 + * @url PUT /:prjUid/process-file-manager */ - public function doPutProcessFilesManager($prjUid, ProcessFilesManagerStructure1 $request_data, $path) + public function doPutProcessFilesManager($prjUid, ProcessFilesManagerStructurePut $request_data, $path) { try { $userUid = $this->getUserId(); @@ -116,7 +116,6 @@ class FilesManager extends Api //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } - return $response; } /** @@ -155,7 +154,8 @@ class ProcessFilesManagerStructure public $content; } -class ProcessFilesManagerStructure1 + +class ProcessFilesManagerStructurePut { /** * @var string {@from body} @@ -166,4 +166,5 @@ class ProcessFilesManagerStructure1 * @var string {@from body} */ public $content; -} \ No newline at end of file +} + From f4593b020b443b4466da8e2b2094c24dd0ce1131 Mon Sep 17 00:00:00 2001 From: Wendy Nestor Date: Thu, 13 Feb 2014 09:23:36 -0400 Subject: [PATCH 2/6] Mejoras y culminacion de los Behat en los features Output, pm group, report tables --- .../main_tests_output.feature | 2 +- .../pm_group/main_tests_pm_group.feature | 105 +++++++- .../pm_group/negative_tests_pm_group.feature | 24 +- .../pm_user/main_tests_pm_user.feature | 1 + .../pm_user/negative_tests_pm_user.feature | 1 + .../process/basic_sequence_process.feature | 51 ++++ .../process/main_tests_process.feature | 229 ++++++++++++++++++ .../process/negative_tests_process.feature | 76 ++++++ .../main_tests_report_tables.feature | 36 ++- .../negative_tests_report_tables.feature | 72 +++++- 10 files changed, 565 insertions(+), 32 deletions(-) create mode 100644 features/backend/pm_user/main_tests_pm_user.feature create mode 100644 features/backend/pm_user/negative_tests_pm_user.feature create mode 100644 features/backend/process/basic_sequence_process.feature create mode 100644 features/backend/process/main_tests_process.feature create mode 100644 features/backend/process/negative_tests_process.feature diff --git a/features/backend/output_documents/main_tests_output.feature b/features/backend/output_documents/main_tests_output.feature index 335198ffd..e00c39f62 100644 --- a/features/backend/output_documents/main_tests_output.feature +++ b/features/backend/output_documents/main_tests_output.feature @@ -17,7 +17,7 @@ Feature: Output Documents Main Tests And the type is "array" And the response has 2 records And the "out_doc_title" property in row 0 equals "Endpoint Old Version (base)" - And the "out_doc_title" property in row 1 equals "Endpoint New Version (base) + And the "out_doc_title" property in row 1 equals "Endpoint New Version (base)" Scenario: Get a single output document of a project diff --git a/features/backend/pm_group/main_tests_pm_group.feature b/features/backend/pm_group/main_tests_pm_group.feature index ac88cbba9..8d5173348 100644 --- a/features/backend/pm_group/main_tests_pm_group.feature +++ b/features/backend/pm_group/main_tests_pm_group.feature @@ -32,8 +32,7 @@ Feature: PM Group Main Tests | Search letters 'de | de | 0 | 5 | 2 | 200 | - - Scenario Outline: Create new Group + Scenario Outline: Create 3 new Groups Given POST this data: """ { @@ -57,7 +56,7 @@ Feature: PM Group Main Tests | 2 | Demo Group3 for main behat | INACTIVE | - Scenario: Get list Groups of workspace + Scenario: Get the Groups list when there are 23 records And I request "groups?filter=&start=0&limit=50" And the content type is "application/json" Then the response status code should be 200 @@ -107,7 +106,103 @@ Feature: PM Group Main Tests | 1 | Update Demo Group2 for main behat | INACTIVE | | 2 | Update Demo Group3 for main behat | ACTIVE | - + + #ASSIGN USER TO GROUP + + Scenario Outline: Get list Users of workspace using different filters for a group + And I request "group/36775342552d5674146d9c2078497230/users?filter=&start=&limit=" + And the content type is "application/json" + Then the response status code should be + And the response charset is "UTF-8" + And the type is "array" + And the response has record + + Examples: + + | test_description | filter | start | limit | record | http_code | + | lowercase | admin | 0 | 9 | 0 | 200 | + | uppercase | ADMIN | 0 | 9 | 0 | 200 | + | limit=3 | a | 0 | 60 | 37 | 200 | + | limit and start | a | 1 | 2 | 2 | 200 | + | high number for start | a | 1000 | 1 | 0 | 200 | + | high number for start | a | 1000 | 0 | 0 | 200 | + | empty result | xyz | 0 | 0 | 0 | 200 | + | empty string | | 0 | 10000 | 43 | 200 | + | empty string | | 1 | 2 | 2 | 200 | + | search 0 | 0 | 0 | 0 | 0 | 200 | + | search 0 | 0 | 0 | 100 | 0 | 200 | + | Search letters 'c' | c | 0 | 40 | 21 | 200 | + | Search letters 'de | de | 0 | 5 | 1 | 200 | + + + Scenario Outline: Assign users to groups created from the endpoint + Given POST this data: + """ + { + "usr_uid": "" + } + """ + And I request "group/grp_uid/user" with the key "grp_uid" stored in session array as variable "grp_uid_" + And the content type is "application/json" + Then the response status code should be 201 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + | grp_uid_number | usr_uid | + | 0 | 00000000000000000000000000000001 | + | 1 | 51049032352d56710347233042615067 | + | 2 | 25286582752d56713231082039265791 | + + + Scenario Outline: List assigned Users to Group + And I request "group/grp_uid/users" with the key "grp_uid" stored in session array as variable "grp_uid_" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "array" + And the "usr_uid" property in row 0 equals "" + And the "usr_username" property in row 0 equals "" + And the "usr_status" property in row 0 equals "" + + Examples: + | grp_uid_number | usr_uid | usr_username | usr_status | + | 0 | 00000000000000000000000000000001 | admin | ACTIVE | + | 1 | 51049032352d56710347233042615067 | aaron | ACTIVE | + | 2 | 25286582752d56713231082039265791 | amy | ACTIVE | + + + Scenario Outline: List available Users to assign to Group + And I request "group/grp_uid/available-users?filter=none" with the key "grp_uid" stored in session array as variable "grp_uid_" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "array" + And the json data is an empty array + + Examples: + | grp_uid_number | + | 0 | + | 1 | + | 2 | + + + Scenario Outline: Unassign User of the Group + Given that I want to delete a resource with the key "" stored in session array + And I request "group/grp_uid/user/" with the key "grp_uid" stored in session array as variable "grp_uid_" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + + | grp_uid_number | usr_uid | + | 0 | 00000000000000000000000000000001 | + | 1 | 51049032352d56710347233042615067 | + | 2 | 25286582752d56713231082039265791 | + + Scenario Outline: Delete all Group created previously in this script Given that I want to delete a resource with the key "grp_uid" stored in session array as variable "grp_uid_" And I request "group" @@ -123,8 +218,6 @@ Feature: PM Group Main Tests | 1 | | 2 | - #GET /api/1.0/{workspace}/groups?filter=abc&start=0&limit=25 - # Get list Groups Scenario: Get list Groups And I request "groups?filter=Update Demo Gro" And the content type is "application/json" diff --git a/features/backend/pm_group/negative_tests_pm_group.feature b/features/backend/pm_group/negative_tests_pm_group.feature index ea12a9647..b4fa63ee4 100644 --- a/features/backend/pm_group/negative_tests_pm_group.feature +++ b/features/backend/pm_group/negative_tests_pm_group.feature @@ -35,8 +35,22 @@ Feature: PM Group Negative Tests Examples: - | grp_title | grp_status | grp_title | error_code | error_message | - | Field requered grp_title | ACTIVE | | 400 | grp_title | - | Field requered grp_status | | test | 400 | grp_status | - - \ No newline at end of file + | grp_title | grp_uid_number | grp_status | grp_title | error_code | error_message | + | Field required grp_title | 1 | ACTIVE | | 400 | grp_title | + | Field required grp_status | 2 | | test | 400 | grp_status | + | Create group with same name | 4 | ACTIVE | Employees | 400 | exists | + + + + + Scenario: Assign users to groups exist in workspace with bad parameters (negative tests) + Given POST this data: + """ + { + "usr_uid": "0000000000000000444500000001" + } + """ + And I request "group/66623507552d56742865613066097298/user" + And the content type is "application/json" + Then the response status code should be 400 + And the response status message should have the following text "usr_uid" \ No newline at end of file diff --git a/features/backend/pm_user/main_tests_pm_user.feature b/features/backend/pm_user/main_tests_pm_user.feature new file mode 100644 index 000000000..67339a3bb --- /dev/null +++ b/features/backend/pm_user/main_tests_pm_user.feature @@ -0,0 +1 @@ +ygyfgy \ No newline at end of file diff --git a/features/backend/pm_user/negative_tests_pm_user.feature b/features/backend/pm_user/negative_tests_pm_user.feature new file mode 100644 index 000000000..692f0135d --- /dev/null +++ b/features/backend/pm_user/negative_tests_pm_user.feature @@ -0,0 +1 @@ +jhjh \ No newline at end of file diff --git a/features/backend/process/basic_sequence_process.feature b/features/backend/process/basic_sequence_process.feature new file mode 100644 index 000000000..5c0d095c4 --- /dev/null +++ b/features/backend/process/basic_sequence_process.feature @@ -0,0 +1,51 @@ +@ProcessMakerMichelangelo @RestAPI +Feature: Process of a Project Resources + Requirements: + a workspace with the process 14414793652a5d718b65590036026581 ("Sample Project #1") already loaded + there are three activities in the process + + Background: + Given that I have a valid access_token + + #GET /api/1.0/{workspace}/project/{prj_uid}/process + # Get a single Process + Scenario Outline: Get a single Process + Given that I want to get a resource with the key "obj_uid" stored in session array + And I request "project/14414793652a5d718b65590036026581/process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + And that "pro_title" is set to "" + And that "pro_description" is set to "" + And that "pro_status" is set to "" + And that "pro_create_user" is set to "" + And that "pro_debug" is set to "" + + Examples: + | pro_title | pro_description | pro_status | pro_create_user | pro_debug | + | Sample Project #1 | | ACTIVE | 00000000000000000000000000000001 | 0 | + + #PUT /api/1.0/{workspace}/project/{prj_uid}/process + # Update Process + Scenario Outline: Update Process + Given PUT this data: + """ + { + "pro_title": "", + "pro_description": "", + "pro_status": "", + "pro_create_user": "", + "pro_debug": + } + """ + And I request "project/14414793652a5d718b65590036026581/process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + | pro_title | pro_description | pro_status | pro_create_user | pro_debug | + | Sample Project #1 | | ACTIVE | 00000000000000000000000000000001 | 0 | + diff --git a/features/backend/process/main_tests_process.feature b/features/backend/process/main_tests_process.feature new file mode 100644 index 000000000..6afd526c3 --- /dev/null +++ b/features/backend/process/main_tests_process.feature @@ -0,0 +1,229 @@ +@ProcessMakerMichelangelo @RestAPI +Feature: Process of a Project Resources + Requirements: + a workspace with the process 79409754952f8f5110c4342001470580 ("Test Process 2") and there are two activities + and workspace with the process 58773281752f50297d6bf00047802053 ("Test Process 1") and there are two activities, in the process already loaded + + + Background: + Given that I have a valid access_token + + Scenario Outline: Get a single Process + Given that I want to get a resource with the key "obj_uid" stored in session array + And I request "project//process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + And that "pro_title" is set to "" + And that "pro_description" is set to "" + And that "pro_parent" is set to "" + And that "pro_time" is set to "" + And that "pro_timeunit" is set to "" + And that "pro_status" is set to "" + And that "pro_type_day" is set to "" + And that "pro_type" is set to "" + And that "pro_assignment" is set to "" + And that "pro_show_map" is set to "" + And that "pro_show_message" is set to "" + And that "pro_subprocess" is set to "" + And that "pro_tri_deleted" is set to "" + And that "pro_tri_canceled" is set to "" + And that "pro_tri_paused" is set to "" + And that "pro_tri_reassigned" is set to "" + And that "pro_show_delegate" is set to "" + And that "pro_show_dynaform" is set to "" + And that "pro_category" is set to "" + And that "pro_sub_category" is set to "" + And that "pro_industry" is set to "" + And that "pro_update_date" is set to "" + And that "pro_create_date" is set to "" + And that "pro_create_user" is set to "" + And that "pro_debug" is set to "" + And that "pro_derivation_screen_tpl" is set to "" + And that "pro_summary_dynaform" is set to "" + And that "pro_calendar" is set to "" + + + Examples: + | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | + | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | null | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | + | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | null | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | + + + Scenario Outline: Update Process + Given PUT this data: + """ + { + "pro_title" : "", + "pro_description" : "", + "pro_parent" : "", + "pro_time" : "", + "pro_timeunit" : "", + "pro_status" : "", + "pro_type_day" : "", + "pro_type" : "", + "pro_assignment" : "", + "pro_show_map" : "", + "pro_show_message" : "", + "pro_subprocess" : "", + "pro_tri_deleted" : "", + "pro_tri_canceled" : "", + "pro_tri_paused" : "", + "pro_tri_reassigned" : "", + "pro_show_delegate" : "", + "pro_show_dynaform" : "", + "pro_category" : "", + "pro_sub_category" : "", + "pro_industry" : "", + "pro_update_date" : "", + "pro_create_date" : "", + "pro_create_user" : "", + "pro_debug" : "", + "pro_derivation_screen_tpl": "", + "pro_summary_dynaform" : "", + "pro_calendar" : "" + } + """ + And I request "project//process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | + | 79409754952f8f5110c4342001470580 | Update Test Process 1 | Update Process - PUT | 79409754952f8f5110c4342001470580 | 1 | DAYS | INACTIVE | | NORMAL | 1 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-01-10 09:43:36 | 2015-12-09 09:43:36 | 00000000000000000000000000000001 | 0 | | | | + | 58773281752f50297d6bf00047802053 | Update Test Process 2 | Update Process - PUT | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 1 | 1 | 1 | 69112537052f503b53142c2026229055 | 30169571352f50349539aa7005920345 | 45413889552f5037587e5a4073302257 | 23429991752f5035c3eab21091451118 | 1 | 0 | 77488943552f502c3d7f649000082980 | | 0 | 2014-01-10 09:43:36 | 2014-02-07 10:58:15 | 51049032352d56710347233042615067 | 1 | tplScreen.html | 94906672952f5058bf3f0f8012616448 | 99159704252f501c63f8c58025859967 | + + + Scenario Outline: Get a single Process + Given that I want to get a resource with the key "obj_uid" stored in session array + And I request "project//process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + And that "pro_title" is set to "" + And that "pro_description" is set to "" + And that "pro_parent" is set to "" + And that "pro_time" is set to "" + And that "pro_timeunit" is set to "" + And that "pro_status" is set to "" + And that "pro_type_day" is set to "" + And that "pro_type" is set to "" + And that "pro_assignment" is set to "" + And that "pro_show_map" is set to "" + And that "pro_show_message" is set to "" + And that "pro_subprocess" is set to "" + And that "pro_tri_deleted" is set to "" + And that "pro_tri_canceled" is set to "" + And that "pro_tri_paused" is set to "" + And that "pro_tri_reassigned" is set to "" + And that "pro_show_delegate" is set to "" + And that "pro_show_dynaform" is set to "" + And that "pro_category" is set to "" + And that "pro_sub_category" is set to "" + And that "pro_industry" is set to "" + And that "pro_update_date" is set to "" + And that "pro_create_date" is set to "" + And that "pro_create_user" is set to "" + And that "pro_debug" is set to "" + And that "pro_derivation_screen_tpl" is set to "" + And that "pro_summary_dynaform" is set to "" + And that "pro_calendar" is set to "" + + + Examples: + | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | + | 79409754952f8f5110c4342001470580 | Update Sample Project #1 | Update Process - PUT | 79409754952f8f5110c4342001470580 | 1 | DAYS | INACTIVE | | NORMAL | 1 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-01-10 09:43:36 | 2015-12-09 09:43:36 | 00000000000000000000000000000001 | 0 | | | | + | 58773281752f50297d6bf00047802053 | Update Test Process | Update Process - PUT | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 1 | 1 | 1 | 69112537052f503b53142c2026229055 | 30169571352f50349539aa7005920345 | 45413889552f5037587e5a4073302257 | 23429991752f5035c3eab21091451118 | 1 | 0 | 77488943552f502c3d7f649000082980 | | 0 | 2014-01-10 09:43:36 | 2014-02-07 10:58:15 | 51049032352d56710347233042615067 | 1 | tplScreen.html | 94906672952f5058bf3f0f8012616448 | 14606161052f50839307899033145440 | + + + Scenario Outline: Update Process + Given PUT this data: + """ + { + "pro_title" : "", + "pro_description" : "", + "pro_parent" : "", + "pro_time" : "", + "pro_timeunit" : "", + "pro_status" : "", + "pro_type_day" : "", + "pro_type" : "", + "pro_assignment" : "", + "pro_show_map" : "", + "pro_show_message" : "", + "pro_subprocess" : "", + "pro_tri_deleted" : "", + "pro_tri_canceled" : "", + "pro_tri_paused" : "", + "pro_tri_reassigned" : "", + "pro_show_delegate" : "", + "pro_show_dynaform" : "", + "pro_category" : "", + "pro_sub_category" : "", + "pro_industry" : "", + "pro_update_date" : "", + "pro_create_date" : "", + "pro_create_user" : "", + "pro_debug" : "", + "pro_derivation_screen_tpl": "", + "pro_summary_dynaform" : "", + "pro_calendar" : "" + } + """ + And I request "project//process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | + | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:49:37 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | + | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:49:37 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | + + + Scenario Outline: Get a single Process + Given that I want to get a resource with the key "obj_uid" stored in session array + And I request "project//process" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + And that "pro_title" is set to "" + And that "pro_description" is set to "" + And that "pro_parent" is set to "" + And that "pro_time" is set to "" + And that "pro_timeunit" is set to "" + And that "pro_status" is set to "" + And that "pro_type_day" is set to "" + And that "pro_type" is set to "" + And that "pro_assignment" is set to "" + And that "pro_show_map" is set to "" + And that "pro_show_message" is set to "" + And that "pro_subprocess" is set to "" + And that "pro_tri_deleted" is set to "" + And that "pro_tri_canceled" is set to "" + And that "pro_tri_paused" is set to "" + And that "pro_tri_reassigned" is set to "" + And that "pro_show_delegate" is set to "" + And that "pro_show_dynaform" is set to "" + And that "pro_category" is set to "" + And that "pro_sub_category" is set to "" + And that "pro_industry" is set to "" + And that "pro_update_date" is set to "" + And that "pro_create_date" is set to "" + And that "pro_create_user" is set to "" + And that "pro_debug" is set to "" + And that "pro_derivation_screen_tpl" is set to "" + And that "pro_summary_dynaform" is set to "" + And that "pro_calendar" is set to "" + + + Examples: + | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | + | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | null | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | + | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | null | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | \ No newline at end of file diff --git a/features/backend/process/negative_tests_process.feature b/features/backend/process/negative_tests_process.feature new file mode 100644 index 000000000..f3f953744 --- /dev/null +++ b/features/backend/process/negative_tests_process.feature @@ -0,0 +1,76 @@ +@ProcessMakerMichelangelo @RestAPI +Feature: Process of a Project Resources Negative Tests + +Background: + Given that I have a valid access_token + +Scenario Outline: Update Process + Given PUT this data: + """ + { + "pro_title" : "", + "pro_description" : "", + "pro_parent" : "", + "pro_time" : "", + "pro_timeunit" : "", + "pro_status" : "", + "pro_type_day" : "", + "pro_type" : "", + "pro_assignment" : "", + "pro_show_map" : "", + "pro_show_message" : "", + "pro_subprocess" : "", + "pro_tri_deleted" : "", + "pro_tri_canceled" : "", + "pro_tri_paused" : "", + "pro_tri_reassigned" : "", + "pro_show_delegate" : "", + "pro_show_dynaform" : "", + "pro_category" : "", + "pro_sub_category" : "", + "pro_industry" : "", + "pro_update_date" : "", + "pro_create_date" : "", + "pro_create_user" : "", + "pro_debug" : "", + "pro_derivation_screen_tpl": "", + "pro_summary_dynaform" : "", + "pro_calendar" : "" + } + """ + And I request "project//process" + Then the response status code should be + And the response status message should have the following text "" + + Examples: + | test_description | project | pro_title | pro_description | pro_parent | pro_time | pro_timeunit | pro_status | pro_type_day | pro_type | pro_assignment | pro_show_map | pro_show_message | pro_subprocess | pro_tri_deleted | pro_tri_canceled | pro_tri_paused | pro_tri_reassigned | pro_show_delegate | pro_show_dynaform | pro_category | pro_sub_category | pro_industry | pro_update_date | pro_create_date | pro_create_user | pro_debug | pro_derivation_screen_tpl | pro_summary_dynaform | pro_calendar | error_code | error_message | + | Invalid prj_uid | 79409700000000000004342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | prj_uid | + | Invalid pro_parent | 58773281752f50297d6bf00047802053 | Test Process 1 | | 5877300000000000006bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_parent | + | Invalid pro_time | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 5 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_time | + | Invalid pro_timeunit | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | HOURS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_timeunit | + | Invalid pro_status | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | VALOR | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_status | + | Invalid pro_type | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | INPUT | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_type | + | Invalid pro_assignment | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 4 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_assignment | + | Invalid pro_show_map | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 5 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_show_map | + | Invalid pro_show_message | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 4 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_show_message | + | Invalid pro_subprocess | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 4 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_subprocess | + | Invalid pro_tri_deleted | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | 4541388955000000000e5a4073302257 | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_tri_deleted | + | Invalid pro_tri_canceled | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | 4541380000000000087e5a4073302257 | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_tri_canceled | + | Invalid pro_tri_paused | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | 4541388900000000087e5a4073302257 | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_tri_paused | + | Invalid pro_tri_reassigned | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | 4541380000000000000e5a4073302257 | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_tri_reassigned | + | Invalid pro_show_delegate | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 5 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_show_delegate | + | Invalid pro_show_dynaform | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 8 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_show_dynaform | + | Invalid pro_category | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | 4541380000000000000e5a4073302257 | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_category | + | Invalid pro_industry | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 4 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_industry | + | Invalid pro_create_date | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 01-98-2014 110:49:37 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_create_date | + | Invalid pro_create_user | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 0000000000003551 | 0 | | | | 400 | pro_create_user | + | Invalid pro_debug | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 5 | | | | 400 | pro_debug | + | Invalid pro_summary_dynaform | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | 954104947190420f51086854718 | | 400 | pro_summary_dynaform | + | Invalid pro_calendar | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | 954104947190420f51086854718 | 400 | pro_calendar | + | Field required prj_uid | | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | prj_uid | + | Same Name pro_title | 79409754952f8f5110c4342001470580 | Test | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 200 | | + | Same Name pro_title | 58773281752f50297d6bf00047802053 | Test | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-10 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 400 | pro_title | + | Set initial values | 79409754952f8f5110c4342001470580 | Test Process 2 | | 79409754952f8f5110c4342001470580 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-11 10:58:15 | 2014-02-10 10:49:37 | 00000000000000000000000000000001 | 0 | | | | 200 | | + | Set initial values | 58773281752f50297d6bf00047802053 | Test Process 1 | | 58773281752f50297d6bf00047802053 | 1 | DAYS | ACTIVE | | NORMAL | 0 | 0 | 0 | 0 | | | | | 0 | 0 | | | 0 | 2014-02-11 10:58:15 | 2014-02-07 10:58:15 | 00000000000000000000000000000001 | 0 | | | | 200 | | + + \ No newline at end of file diff --git a/features/backend/report_tables/main_tests_report_tables.feature b/features/backend/report_tables/main_tests_report_tables.feature index ab26fe2d2..1e5135b82 100644 --- a/features/backend/report_tables/main_tests_report_tables.feature +++ b/features/backend/report_tables/main_tests_report_tables.feature @@ -7,8 +7,6 @@ Requirements: Background: Given that I have a valid access_token - - Scenario: Verify that there are no report tables Given I request "project/922677707524ac7417ce345089010125/report-tables" Then the response status code should be 200 @@ -53,9 +51,10 @@ Scenario Outline: Create new report tables from dynaform and grid Examples: - | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | - | | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | - | | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc 2 | workflow | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | + | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | + | Create a Report Table - Normal | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | + | Create a Report Table - Grid | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc 2 | workflow | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | + Scenario: Verify that there are 2 report tables Given I request "project/922677707524ac7417ce345089010125/report-tables" @@ -64,8 +63,6 @@ Scenario: Verify that there are 2 report tables And the response has 2 record - - Scenario Outline: Update a created report tables Given PUT this data: """ @@ -103,9 +100,9 @@ Scenario Outline: Update a created report tables Examples: - | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | - | | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | - | | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | + | Update a Report Table - Normal | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | Update a Report Table - Grid | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | Scenario Outline: Get a details of created report tables @@ -119,9 +116,9 @@ Scenario Outline: Get a details of created report tables Examples: - | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | - | | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | - | | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | grid | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | + | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | grid | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | Scenario Outline: Populate report tables @@ -131,9 +128,9 @@ Scenario Outline: Populate report tables Examples: - | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | - | | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | - | | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | grid | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | + | Populate Report Table Normal | 922677707524ac7417ce345089010125 | 1 | REPORT_TABLE_1 | Report Table Desc Updated 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | + | Populate Report Table Grid | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | Report Table Desc Updated 2 | workflow | GRID | grid | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 150 | @@ -146,9 +143,10 @@ Scenario Outline: Delete a created report tables Examples: - | test_description | project | rep_uid_number | - | | 922677707524ac7417ce345089010125 | 100 | - | | 922677707524ac7417ce345089010125 | 200 | + | test_description | project | rep_uid_number | + | Delete Report Table Normal | 922677707524ac7417ce345089010125 | 1 | + | Delete Report Table Grid | 922677707524ac7417ce345089010125 | 2 | + Scenario: Verify that the report tables were deleted correctly Given I request "project/922677707524ac7417ce345089010125/report-tables" diff --git a/features/backend/report_tables/negative_tests_report_tables.feature b/features/backend/report_tables/negative_tests_report_tables.feature index dfe99092d..709f39240 100644 --- a/features/backend/report_tables/negative_tests_report_tables.feature +++ b/features/backend/report_tables/negative_tests_report_tables.feature @@ -1,5 +1,75 @@ @ProcessMakerMichelangelo @RestAPI -Feature: DataBase Connections +Feature: DataBase Connections Negative Tests Background: Given that I have a valid access_token + +Scenario Outline: Create new report tables from dynaform and grid with bad parameters (negative tests) + Given POST this data: + """ + { + "rep_tab_name" : "", + "rep_tab_dsc" : "", + "rep_tab_connection" : "", + "rep_tab_type" : "", + "rep_tab_grid" : "", + "fields" : [ + { + "fld_dyn" : "", + "fld_name" : "", + "fld_label" : "", + "fld_type" : "", + "fld_size" : "" + },{ + "fld_dyn" : "", + "fld_name" : "", + "fld_label" : "", + "fld_type" : "", + "fld_size" : "" + },{ + "fld_dyn" : "", + "fld_name" : "", + "fld_label" : "", + "fld_type" : "", + "fld_size" : "" + } + ] + } + """ + And I request "project//report-table" + Then the response status code should be + And store "rep_uid" in session array as variable "rep_uid_" + And the response status message should have the following text "" + + + Examples: + + | test_description | project | rep_uid_number | rep_tab_name | rep_tab_dsc | rep_tab_connection | rep_tab_type | rep_tab_grid | fld_dyn_1 | fld_name_1 | fld_label_1 | fld_type_1 | fld_size_1 | fld_dyn_2 | fld_name_2 | fld_label_2 | fld_type_2 | fld_size_2 | fld_dyn_3 | fld_name_3 | fld_label_3 | fld_type_3 | fld_size_3 | error_code | error_message | + | Field required rep_tab_name | 922677707524ac7417ce345089010125 | 1 | | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_name | + | Field required rep_tab_dsc | 922677707524ac7417ce345089010125 | 2 | REPORT_TABLE_2 | | workflow | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_dsc | + | Field required rep_tab_connection | 922677707524ac7417ce345089010125 | 3 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_connection | + | Field required fld_type | 922677707524ac7417ce345089010125 | 4 | REPORT_TABLE_2 | Report Table Desc 2 | workflow | | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | fld_type | + | Field required fld_name | 922677707524ac7417ce345089010125 | 5 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | | Name Any | VARCHAR | 64 | date1 | | Date | DATE | | | | Custom Field 1 | VARCHAR | 15 | 400 | fld_name | + | Field required fld_label | 922677707524ac7417ce345089010125 | 6 | REPORT_TABLE_2 | Report Table Desc 2 | | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | | VARCHAR | 64 | fecha1 | DATE_1 | | DATE | | | CUSTOM_FIELD_1 | | VARCHAR | 15 | 400 | fld_label | + | The name is too short | 922677707524ac7417ce345089010125 | 7 | RE | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | characters | + | Field required Project | 92267000000000000000000089010125 | 8 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | prj_uid | + | Invalid rep_tab_connection | 922677707524ac7417ce345089010125 | 9 | REPORT_TABLE_2 | Report Table Desc 2 | sample | GRID | 267480685524ac9b3bd5e23004484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_connection | + | Invalid rep_tab_type | 922677707524ac7417ce345089010125 | 10 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | INPUT | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_type | + | Invalid fld_type | 922677707524ac7417ce345089010125 | 11 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | SAMPLE | 64 | date1 | DATE_1 | Date | SAMPLE | | | CUSTOM_FIELD_1 | Custom Field 1 | SAMPLE | 15 | 400 | fld_type | + | Invalid fld_size | 922677707524ac7417ce345089010125 | 12 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64,34.55 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 64,34.55 | 400 | fld_size | + | Invalid rep_tab_grid | 922677707524ac7417ce345089010125 | 13 | REPORT_TABLE_2 | Report Table Desc 2 | workflow | GRID | 26748060000000000000000000484669 | text1 | TEXT_1 | Text 1 | VARCHAR | 64 | fecha1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | rep_tab_grid | + | Create same rep_tab_name | 922677707524ac7417ce345089010125 | 14 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 200 | | + | Create same rep_tab_name | 922677707524ac7417ce345089010125 | 15 | REPORT_TABLE_1 | Report Table Desc 1 | workflow | NORMAL | | nameany | NAME_ANY | Name Any | VARCHAR | 64 | date1 | DATE_1 | Date | DATE | | | CUSTOM_FIELD_1 | Custom Field 1 | VARCHAR | 15 | 400 | already exits | + + +Scenario Outline: Delete a created report tables + Given that I want to delete a resource with the key "rep_uid" stored in session array as variable "rep_uid_" + And I request "project//report-table" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the type is "object" + + Examples: + + | test_description | project | rep_uid_number | + | Delete Report Table Normal | 922677707524ac7417ce345089010125 | 14 | \ No newline at end of file From f2c105d5284695a503fa3dc826fd5369f61e8ea2 Mon Sep 17 00:00:00 2001 From: Freddy Daniel Rojas Valda Date: Thu, 13 Feb 2014 14:56:52 -0400 Subject: [PATCH 3/6] Se adiciona BEHAT para FILES MANAGER. Se arreglos en FILES MANAGER --- .../basic_sequence_files_manager.feature | 86 +++++++++++++++++++ .../engine/src/BusinessModel/FilesManager.php | 30 +++---- .../Api/ProcessMaker/Project/FilesManager.php | 4 +- 3 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 features/backend/files_manager/basic_sequence_files_manager.feature diff --git a/features/backend/files_manager/basic_sequence_files_manager.feature b/features/backend/files_manager/basic_sequence_files_manager.feature new file mode 100644 index 000000000..edf731ddf --- /dev/null +++ b/features/backend/files_manager/basic_sequence_files_manager.feature @@ -0,0 +1,86 @@ +@ProcessMakerMichelangelo @RestAPI +Feature: Files Manager Resources + + Background: + Given that I have a valid access_token + + Scenario: Get a list of main process files manager + Given I request "project/1265557095225ff5c688f46031700471/process-file-manager" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the content type is "application/json" + And the type is "array" + + Scenario: Get a list public folder of process files manager + Given I request "project/1265557095225ff5c688f46031700471/process-file-manager?path=public" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the content type is "application/json" + And the type is "array" + + Scenario: Get a list templates folder of process files manager + Given I request "project/1265557095225ff5c688f46031700471/process-file-manager?path=templates" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the content type is "application/json" + And the type is "array" + + Scenario Outline: Post files + Given POST this data: + """ + { + "file_name": "", + "path": "", + "content": "" + } + """ + And I request "project/1265557095225ff5c688f46031700471/process-file-manager" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the content type is "application/json" + And the type is "" + And store "prf_uid" in session array as variable "prf_uid" + + Examples: + | test_description | file_name | path | content | http_code | type | i | + | into public folder | test.txt | public/ | test | 200 | object | 0 | + | into maintemplates folder | test.txt | templates/ | test | 200 | object | 1 | + | into public subfolder | test.txt | public/test_folder | test | 200 | object | 2 | + | into public subfolder | test.txt | templates/test_folder | test | 200 | object | 3 | + + + + Scenario Outline: Post files + Given PUT this data: + """ + { + "file_name": "", + "content": "" + } + """ + And I request "project/1265557095225ff5c688f46031700471/process-file-manager?path=" + Then the response status code should be 200 + And the response charset is "UTF-8" + And the content type is "application/json" + And the type is "" + + Examples: + | test_description | file_name | path | content | http_code | type | + | put into public folder | test.txt | public/ | put test | 200 | object | + | put into maintemplates folder | test.txt | templates/ | put test | 200 | object | + | put into public subfolder | test.txt | public/test_folder | put test | 200 | object | + | put into public subfolder | test.txt | templates/test_folder | put test | 200 | object | + + Scenario Outline: Delete User + Given that I want to delete a "" + And I request "project/1265557095225ff5c688f46031700471/process-file-manager?path=" + And the content type is "application/json" + Then the response status code should be 200 + And the response charset is "UTF-8" + + Examples: + | test_description | path | + | put into public folder | public/test.txt | + | put into maintemplates folder | templates/test.txt | + | put into public subfolder | public/test_folder/test.txt | + | put into public subfolder | templates/test_folder/test.txt | \ No newline at end of file diff --git a/workflow/engine/src/BusinessModel/FilesManager.php b/workflow/engine/src/BusinessModel/FilesManager.php index c80e80bce..3773cd22a 100644 --- a/workflow/engine/src/BusinessModel/FilesManager.php +++ b/workflow/engine/src/BusinessModel/FilesManager.php @@ -54,7 +54,7 @@ class FilesManager case 'templates': $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory; break; - case 'folder': + case 'public': $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory; break; default: @@ -115,8 +115,8 @@ class FilesManager try { $aData['path'] = rtrim($aData['path'], '/') . '/'; $sMainDirectory = current(explode("/", $aData['path'])); - if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { - throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); + if ($sMainDirectory != 'public' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `public/`')); } if(strstr($aData['path'],'/')){ $sSubDirectory = substr($aData['path'], strpos($aData['path'], "/")+1) ; @@ -129,7 +129,7 @@ class FilesManager $sCheckDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory; $sEditable = false; break; - case 'folder': + case 'public': $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; $sCheckDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory; break; @@ -235,8 +235,8 @@ class FilesManager try { $path = rtrim($path, '/') . '/'; $sMainDirectory = current(explode("/", $path)); - if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { - throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); + if ($sMainDirectory != 'public' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `public/`')); } if(strstr($path,'/')){ $sSubDirectory = substr($path, strpos($path, "/")+1) ; @@ -248,7 +248,7 @@ class FilesManager $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; $sEditable = false; break; - case 'folder': + case 'public': $sDirectory = PATH_DATA_PUBLIC . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['file_name']; break; default: @@ -309,12 +309,10 @@ class FilesManager { try { $sMainDirectory = current(explode("/", $path)); - if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { - throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); + if ($sMainDirectory != 'public' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `public/`')); } - if ($sMainDirectory == 'folder') { - $sMainDirectory = 'public'; - } else { + if ($sMainDirectory == 'templates') { $sMainDirectory = 'mailTemplates'; } $sfile = end(explode("/",$path)); @@ -367,12 +365,10 @@ class FilesManager { try { $sMainDirectory = current(explode("/", $path)); - if ($sMainDirectory != 'folder' && $sMainDirectory != 'templates') { - throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `folder/`')); + if ($sMainDirectory != 'public' && $sMainDirectory != 'templates') { + throw (new \Exception( 'invalid value specified for `prf_path`. Expecting `templates/` or `public/`')); } - if ($sMainDirectory == 'folder') { - $sMainDirectory = 'public'; - } else { + if ($sMainDirectory == 'templates') { $sMainDirectory = 'mailTemplates'; } $sfile = end(explode("/",$path)); diff --git a/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php b/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php index bdac76ce8..e00386573 100644 --- a/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php +++ b/workflow/engine/src/Services/Api/ProcessMaker/Project/FilesManager.php @@ -61,7 +61,7 @@ class FilesManager extends Api /** * @param string $prjUid {@min 32} {@max 32} * - * @url POST /:prjUid/process-file-manager-upload + * @url POST /:prjUid/process-file-manager/upload */ public function doPostProcessFilesManagerUpload($prjUid) { @@ -124,7 +124,7 @@ class FilesManager extends Api * * @url GET /:prjUid/process-file-manager/download */ - public function doDownloadProcessFilesManager($prjUid, $path) + public function doGetProcessFilesManagerDownload($prjUid, $path) { try { $filesManager = new \BusinessModel\FilesManager(); From 5d3d7392f013c8cc7df14beccad8d7b557baa8ad Mon Sep 17 00:00:00 2001 From: jennylee Date: Thu, 13 Feb 2014 15:19:17 -0400 Subject: [PATCH 4/6] New CodeMirror Library version 3.16 Replacing the CodeMirror Version to 3.16 Adding files to ..templates/designer/index.html to use the new extended control codeMirror.js and codeMirrorField.js --- gulliver/js/codemirror/.gitattributes | 8 + gulliver/js/codemirror/.gitignore | 6 + gulliver/js/codemirror/.travis.yml | 3 + gulliver/js/codemirror/AUTHORS | 300 +++ gulliver/js/codemirror/CONTRIBUTING.md | 14 +- gulliver/js/codemirror/LICENSE | 6 +- gulliver/js/codemirror/README.md | 4 +- .../js/codemirror/addon/comment/comment.js | 149 ++ .../addon/comment/continuecomment.js | 54 + gulliver/js/codemirror/addon/dialog/dialog.js | 44 +- .../codemirror/addon/display/fullscreen.css | 6 + .../js/codemirror/addon/display/fullscreen.js | 31 + .../codemirror/addon/display/placeholder.js | 14 +- .../js/codemirror/addon/edit/closebrackets.js | 58 +- gulliver/js/codemirror/addon/edit/closetag.js | 74 +- .../codemirror/addon/edit/continuecomment.js | 44 - .../js/codemirror/addon/edit/continuelist.js | 4 +- .../js/codemirror/addon/edit/matchbrackets.js | 43 +- .../js/codemirror/addon/edit/matchtags.js | 56 + .../js/codemirror/addon/edit/trailingspace.js | 15 + .../js/codemirror/addon/fold/brace-fold.js | 96 +- .../js/codemirror/addon/fold/comment-fold.js | 42 + gulliver/js/codemirror/addon/fold/foldcode.js | 94 +- .../js/codemirror/addon/fold/foldgutter.css | 21 + .../js/codemirror/addon/fold/foldgutter.js | 124 ++ .../js/codemirror/addon/fold/indent-fold.js | 35 +- gulliver/js/codemirror/addon/fold/xml-fold.js | 193 +- .../js/codemirror/addon/hint/anyword-hint.js | 32 + gulliver/js/codemirror/addon/hint/css-hint.js | 46 + .../js/codemirror/addon/hint/html-hint.js | 901 +++----- .../codemirror/addon/hint/javascript-hint.js | 41 +- gulliver/js/codemirror/addon/hint/php-hint.js | 267 --- gulliver/js/codemirror/addon/hint/pig-hint.js | 44 +- .../js/codemirror/addon/hint/python-hint.js | 12 +- .../js/codemirror/addon/hint/show-hint.css | 2 +- .../js/codemirror/addon/hint/show-hint.js | 392 ++-- gulliver/js/codemirror/addon/hint/sql-hint.js | 146 ++ gulliver/js/codemirror/addon/hint/xml-hint.js | 171 +- .../addon/lint/coffeescript-lint.js | 27 + gulliver/js/codemirror/addon/lint/css-lint.js | 19 + .../codemirror/addon/lint/javascript-lint.js | 119 +- .../js/codemirror/addon/lint/json-lint.js | 7 +- gulliver/js/codemirror/addon/lint/lint.css | 37 +- gulliver/js/codemirror/addon/lint/lint.js | 90 +- .../addon/merge/dep/diff_match_patch.js | 50 + gulliver/js/codemirror/addon/merge/merge.css | 92 + gulliver/js/codemirror/addon/merge/merge.js | 479 ++++ .../js/codemirror/addon/mode/multiplex.js | 20 +- .../codemirror/addon/mode/multiplex_test.js | 30 + gulliver/js/codemirror/addon/mode/overlay.js | 4 +- .../addon/runmode/runmode-standalone.js | 49 +- .../js/codemirror/addon/runmode/runmode.js | 10 +- .../codemirror/addon/runmode/runmode.node.js | 59 +- .../codemirror/addon/scroll/scrollpastend.js | 34 + .../addon/search/match-highlighter.js | 81 +- gulliver/js/codemirror/addon/search/search.js | 37 +- .../codemirror/addon/search/searchcursor.js | 111 +- .../codemirror/addon/selection/active-line.js | 36 +- .../addon/selection/mark-selection.js | 104 +- gulliver/js/codemirror/addon/tern/tern.css | 85 + gulliver/js/codemirror/addon/tern/tern.js | 632 ++++++ gulliver/js/codemirror/addon/tern/worker.js | 41 + gulliver/js/codemirror/addon/wrap/hardwrap.js | 111 + gulliver/js/codemirror/bin/authors.sh | 6 + gulliver/js/codemirror/bin/compress | 7 +- gulliver/js/codemirror/bin/lint | 16 + gulliver/js/codemirror/bin/release | 41 + gulliver/js/codemirror/bin/source-highlight | 61 + gulliver/js/codemirror/bower.json | 15 + gulliver/js/codemirror/demo/activeline.html | 42 +- gulliver/js/codemirror/demo/anywordhint.html | 79 + gulliver/js/codemirror/demo/bidi.html | 47 +- gulliver/js/codemirror/demo/btree.html | 45 +- gulliver/js/codemirror/demo/buffers.html | 41 +- gulliver/js/codemirror/demo/changemode.html | 45 +- .../js/codemirror/demo/closebrackets.html | 50 +- gulliver/js/codemirror/demo/closetag.html | 56 +- gulliver/js/codemirror/demo/complete.html | 56 +- gulliver/js/codemirror/demo/completePHP.html | 81 - gulliver/js/codemirror/demo/emacs.html | 47 +- gulliver/js/codemirror/demo/folding.html | 105 +- gulliver/js/codemirror/demo/fullscreen.html | 85 +- gulliver/js/codemirror/demo/hardwrap.html | 72 + .../js/codemirror/demo/html5complete.html | 132 +- gulliver/js/codemirror/demo/indentwrap.html | 40 +- gulliver/js/codemirror/demo/lint.html | 127 +- gulliver/js/codemirror/demo/loadmode.html | 39 +- gulliver/js/codemirror/demo/marker.html | 63 +- .../js/codemirror/demo/markselection.html | 41 +- .../js/codemirror/demo/matchhighlighter.html | 43 +- gulliver/js/codemirror/demo/matchtags.html | 49 + gulliver/js/codemirror/demo/merge.html | 81 + gulliver/js/codemirror/demo/multiplex.html | 47 +- gulliver/js/codemirror/demo/mustache.html | 43 +- gulliver/js/codemirror/demo/placeholder.html | 39 +- gulliver/js/codemirror/demo/preview.html | 46 +- gulliver/js/codemirror/demo/resize.html | 39 +- gulliver/js/codemirror/demo/runmode.html | 40 +- gulliver/js/codemirror/demo/search.html | 47 +- .../demo/spanaffectswrapping_shim.html | 34 +- gulliver/js/codemirror/demo/tern.html | 129 ++ gulliver/js/codemirror/demo/theme.html | 100 +- .../js/codemirror/demo/trailingspace.html | 48 + .../js/codemirror/demo/variableheight.html | 49 +- gulliver/js/codemirror/demo/vim.html | 51 +- gulliver/js/codemirror/demo/visibletabs.html | 39 +- gulliver/js/codemirror/demo/widget.html | 41 +- gulliver/js/codemirror/demo/xmlcomplete.html | 171 +- gulliver/js/codemirror/doc/activebookmark.js | 48 + gulliver/js/codemirror/doc/baboon.png | Bin 23299 -> 0 bytes gulliver/js/codemirror/doc/baboon_vector.svg | 153 -- gulliver/js/codemirror/doc/compress.html | 129 +- gulliver/js/codemirror/doc/docs.css | 313 +-- gulliver/js/codemirror/doc/internals.html | 106 +- gulliver/js/codemirror/doc/logo.png | Bin 0 -> 12003 bytes gulliver/js/codemirror/doc/logo.svg | 147 ++ gulliver/js/codemirror/doc/manual.html | 1751 ++++++++++----- gulliver/js/codemirror/doc/modes.html | 93 - gulliver/js/codemirror/doc/realworld.html | 78 +- .../doc/{oldrelease.html => releases.html} | 411 +++- gulliver/js/codemirror/doc/reporting.html | 37 +- gulliver/js/codemirror/doc/upgrade_v2.2.html | 44 +- gulliver/js/codemirror/doc/upgrade_v3.html | 131 +- gulliver/js/codemirror/index.html | 630 ++---- gulliver/js/codemirror/keymap/emacs.js | 396 +++- gulliver/js/codemirror/keymap/extra.js | 43 + gulliver/js/codemirror/keymap/vim.js | 1926 +++++++++++++---- gulliver/js/codemirror/lib/codemirror.css | 42 +- gulliver/js/codemirror/lib/codemirror.js | 650 ++++-- gulliver/js/codemirror/mode/apl/index.html | 41 +- .../js/codemirror/mode/asterisk/asterisk.js | 12 +- .../js/codemirror/mode/asterisk/index.html | 42 +- gulliver/js/codemirror/mode/clike/clike.js | 127 +- gulliver/js/codemirror/mode/clike/index.html | 128 +- gulliver/js/codemirror/mode/clike/scala.html | 54 +- .../js/codemirror/mode/clojure/clojure.js | 13 +- .../js/codemirror/mode/clojure/index.html | 42 +- .../js/codemirror/mode/coffeescript/LICENSE | 22 - .../mode/coffeescript/coffeescript.js | 663 +++--- .../codemirror/mode/coffeescript/index.html | 42 +- .../codemirror/mode/commonlisp/commonlisp.js | 6 +- .../js/codemirror/mode/commonlisp/index.html | 42 +- gulliver/js/codemirror/mode/css/css.js | 712 +++--- gulliver/js/codemirror/mode/css/index.html | 44 +- gulliver/js/codemirror/mode/css/less.html | 152 ++ gulliver/js/codemirror/mode/css/less_test.js | 48 + gulliver/js/codemirror/mode/css/scss.html | 44 +- gulliver/js/codemirror/mode/css/scss_test.js | 69 +- gulliver/js/codemirror/mode/css/test.js | 132 +- gulliver/js/codemirror/mode/d/index.html | 41 +- gulliver/js/codemirror/mode/diff/index.html | 42 +- gulliver/js/codemirror/mode/ecl/ecl.js | 24 +- gulliver/js/codemirror/mode/ecl/index.html | 43 +- gulliver/js/codemirror/mode/erlang/erlang.js | 574 +++-- gulliver/js/codemirror/mode/erlang/index.html | 56 +- gulliver/js/codemirror/mode/gfm/gfm.js | 16 +- gulliver/js/codemirror/mode/gfm/index.html | 56 +- gulliver/js/codemirror/mode/gfm/test.js | 19 +- gulliver/js/codemirror/mode/go/go.js | 5 +- gulliver/js/codemirror/mode/go/index.html | 43 +- gulliver/js/codemirror/mode/groovy/groovy.js | 3 +- gulliver/js/codemirror/mode/groovy/index.html | 41 +- .../js/codemirror/mode/haskell/haskell.js | 74 +- .../js/codemirror/mode/haskell/index.html | 43 +- gulliver/js/codemirror/mode/haxe/haxe.js | 32 +- gulliver/js/codemirror/mode/haxe/index.html | 41 +- .../mode/htmlembedded/htmlembedded.js | 12 +- .../codemirror/mode/htmlembedded/index.html | 47 +- .../js/codemirror/mode/htmlmixed/htmlmixed.js | 4 +- .../js/codemirror/mode/htmlmixed/index.html | 50 +- gulliver/js/codemirror/mode/http/index.html | 41 +- gulliver/js/codemirror/mode/index.html | 115 + .../js/codemirror/mode/javascript/index.html | 61 +- .../codemirror/mode/javascript/javascript.js | 415 +++- .../js/codemirror/mode/javascript/test.js | 113 + .../mode/javascript/typescript.html | 41 +- gulliver/js/codemirror/mode/jinja2/index.html | 42 +- gulliver/js/codemirror/mode/jinja2/jinja2.js | 18 +- gulliver/js/codemirror/mode/less/index.html | 46 +- gulliver/js/codemirror/mode/less/less.js | 273 ++- .../js/codemirror/mode/livescript/LICENSE | 23 - .../js/codemirror/mode/livescript/index.html | 43 +- gulliver/js/codemirror/mode/lua/index.html | 46 +- gulliver/js/codemirror/mode/lua/lua.js | 20 +- .../js/codemirror/mode/markdown/index.html | 49 +- .../js/codemirror/mode/markdown/markdown.js | 414 +++- gulliver/js/codemirror/mode/markdown/test.js | 166 +- gulliver/js/codemirror/mode/meta.js | 28 +- gulliver/js/codemirror/mode/mirc/index.html | 44 +- gulliver/js/codemirror/mode/mirc/mirc.js | 114 +- .../js/codemirror/mode/ntriples/index.html | 42 +- .../js/codemirror/mode/ntriples/ntriples.js | 44 +- gulliver/js/codemirror/mode/ocaml/index.html | 131 -- gulliver/js/codemirror/mode/ocaml/ocaml.js | 113 - gulliver/js/codemirror/mode/pascal/LICENSE | 7 - gulliver/js/codemirror/mode/pascal/index.html | 41 +- gulliver/js/codemirror/mode/perl/LICENSE | 19 - gulliver/js/codemirror/mode/perl/index.html | 41 +- gulliver/js/codemirror/mode/perl/perl.js | 1582 +++++++------- gulliver/js/codemirror/mode/php/index.html | 51 +- gulliver/js/codemirror/mode/php/php.js | 234 +- gulliver/js/codemirror/mode/pig/index.html | 37 +- gulliver/js/codemirror/mode/pig/pig.js | 326 +-- .../js/codemirror/mode/properties/index.html | 42 +- .../js/codemirror/mode/python/LICENSE.txt | 21 - gulliver/js/codemirror/mode/python/index.html | 89 +- gulliver/js/codemirror/mode/python/python.js | 92 +- gulliver/js/codemirror/mode/q/index.html | 43 +- gulliver/js/codemirror/mode/q/q.js | 2 +- gulliver/js/codemirror/mode/r/LICENSE | 24 - gulliver/js/codemirror/mode/r/index.html | 42 +- gulliver/js/codemirror/mode/r/r.js | 6 +- .../js/codemirror/mode/rpm/changes/index.html | 34 +- .../js/codemirror/mode/rpm/spec/index.html | 33 +- gulliver/js/codemirror/mode/rpm/spec/spec.js | 2 +- gulliver/js/codemirror/mode/rst/LICENSE.txt | 21 - gulliver/js/codemirror/mode/rst/index.html | 40 +- gulliver/js/codemirror/mode/rst/rst.js | 88 +- gulliver/js/codemirror/mode/ruby/LICENSE | 24 - gulliver/js/codemirror/mode/ruby/index.html | 44 +- gulliver/js/codemirror/mode/ruby/ruby.js | 92 +- gulliver/js/codemirror/mode/rust/index.html | 41 +- gulliver/js/codemirror/mode/rust/rust.js | 6 +- gulliver/js/codemirror/mode/sass/index.html | 44 +- gulliver/js/codemirror/mode/sass/sass.js | 39 +- gulliver/js/codemirror/mode/scheme/index.html | 42 +- gulliver/js/codemirror/mode/scheme/scheme.js | 4 +- gulliver/js/codemirror/mode/shell/index.html | 31 +- gulliver/js/codemirror/mode/shell/shell.js | 4 +- gulliver/js/codemirror/mode/sieve/LICENSE | 19 - gulliver/js/codemirror/mode/sieve/index.html | 42 +- gulliver/js/codemirror/mode/sieve/sieve.js | 20 +- .../js/codemirror/mode/smalltalk/index.html | 41 +- .../js/codemirror/mode/smalltalk/smalltalk.js | 220 +- gulliver/js/codemirror/mode/smarty/index.html | 85 +- gulliver/js/codemirror/mode/smarty/smarty.js | 285 ++- gulliver/js/codemirror/mode/sparql/index.html | 44 +- gulliver/js/codemirror/mode/sparql/sparql.js | 8 +- gulliver/js/codemirror/mode/sql/index.html | 89 +- gulliver/js/codemirror/mode/sql/sql.js | 190 +- gulliver/js/codemirror/mode/stex/index.html | 42 +- gulliver/js/codemirror/mode/stex/stex.js | 2 +- gulliver/js/codemirror/mode/stex/test.js | 3 + gulliver/js/codemirror/mode/tcl/index.html | 44 +- gulliver/js/codemirror/mode/tcl/tcl.js | 2 +- .../js/codemirror/mode/tiddlywiki/index.html | 45 +- .../codemirror/mode/tiddlywiki/tiddlywiki.js | 622 +++--- gulliver/js/codemirror/mode/tiki/index.html | 44 +- gulliver/js/codemirror/mode/tiki/tiki.js | 595 +++-- gulliver/js/codemirror/mode/turtle/index.html | 42 +- gulliver/js/codemirror/mode/turtle/turtle.js | 28 +- gulliver/js/codemirror/mode/vb/LICENSE.txt | 21 - gulliver/js/codemirror/mode/vb/index.html | 49 +- gulliver/js/codemirror/mode/vb/vb.js | 61 +- .../js/codemirror/mode/vbscript/index.html | 45 +- .../js/codemirror/mode/vbscript/vbscript.js | 348 ++- .../js/codemirror/mode/velocity/index.html | 48 +- .../js/codemirror/mode/velocity/velocity.js | 66 +- .../js/codemirror/mode/verilog/index.html | 39 +- gulliver/js/codemirror/mode/xml/index.html | 42 +- gulliver/js/codemirror/mode/xml/xml.js | 250 +-- gulliver/js/codemirror/mode/xquery/LICENSE | 20 - gulliver/js/codemirror/mode/xquery/index.html | 67 +- gulliver/js/codemirror/mode/xquery/xquery.js | 140 +- gulliver/js/codemirror/mode/yaml/index.html | 42 +- gulliver/js/codemirror/mode/yaml/yaml.js | 182 +- gulliver/js/codemirror/mode/z80/index.html | 41 +- gulliver/js/codemirror/mode/z80/z80.js | 192 +- gulliver/js/codemirror/package.json | 11 +- gulliver/js/codemirror/test/comment_test.js | 63 + gulliver/js/codemirror/test/doc_test.js | 2 +- gulliver/js/codemirror/test/driver.js | 61 +- gulliver/js/codemirror/test/emacs_test.js | 138 ++ gulliver/js/codemirror/test/index.html | 154 +- gulliver/js/codemirror/test/lint/lint.js | 45 +- gulliver/js/codemirror/test/lint/parse-js.js | 1372 ------------ gulliver/js/codemirror/test/mode_test.js | 98 +- gulliver/js/codemirror/test/run.js | 35 +- gulliver/js/codemirror/test/search_test.js | 62 + gulliver/js/codemirror/test/test.js | 287 ++- gulliver/js/codemirror/test/vim_test.js | 1191 +++++++++- gulliver/js/codemirror/theme/3024-day.css | 34 + gulliver/js/codemirror/theme/3024-night.css | 34 + gulliver/js/codemirror/theme/ambiance.css | 6 +- gulliver/js/codemirror/theme/base16-dark.css | 34 + gulliver/js/codemirror/theme/base16-light.css | 34 + gulliver/js/codemirror/theme/blackboard.css | 5 +- gulliver/js/codemirror/theme/cobalt.css | 5 +- gulliver/js/codemirror/theme/eclipse.css | 8 +- gulliver/js/codemirror/theme/elegant.css | 5 +- gulliver/js/codemirror/theme/erlang-dark.css | 15 +- gulliver/js/codemirror/theme/lesser-dark.css | 7 +- gulliver/js/codemirror/theme/mbo.css | 37 + gulliver/js/codemirror/theme/midnight.css | 43 + gulliver/js/codemirror/theme/monokai.css | 3 +- gulliver/js/codemirror/theme/neat.css | 3 + gulliver/js/codemirror/theme/night.css | 5 +- gulliver/js/codemirror/theme/paraiso-dark.css | 34 + .../js/codemirror/theme/paraiso-light.css | 34 + .../js/codemirror/theme/pastel-on-dark.css | 49 + gulliver/js/codemirror/theme/rubyblue.css | 6 +- gulliver/js/codemirror/theme/solarized.css | 47 +- gulliver/js/codemirror/theme/the-matrix.css | 26 + .../theme/tomorrow-night-eighties.css | 34 + gulliver/js/codemirror/theme/twilight.css | 4 +- gulliver/js/codemirror/theme/vibrant-ink.css | 9 +- gulliver/js/codemirror/theme/xq-dark.css | 7 +- gulliver/js/codemirror/theme/xq-light.css | 4 +- workflow/engine/templates/designer/index.html | 14 + 309 files changed, 21218 insertions(+), 12102 deletions(-) create mode 100644 gulliver/js/codemirror/.gitattributes create mode 100644 gulliver/js/codemirror/.gitignore create mode 100644 gulliver/js/codemirror/.travis.yml create mode 100644 gulliver/js/codemirror/AUTHORS create mode 100644 gulliver/js/codemirror/addon/comment/comment.js create mode 100644 gulliver/js/codemirror/addon/comment/continuecomment.js create mode 100644 gulliver/js/codemirror/addon/display/fullscreen.css create mode 100644 gulliver/js/codemirror/addon/display/fullscreen.js delete mode 100644 gulliver/js/codemirror/addon/edit/continuecomment.js create mode 100644 gulliver/js/codemirror/addon/edit/matchtags.js create mode 100644 gulliver/js/codemirror/addon/edit/trailingspace.js create mode 100644 gulliver/js/codemirror/addon/fold/comment-fold.js create mode 100644 gulliver/js/codemirror/addon/fold/foldgutter.css create mode 100644 gulliver/js/codemirror/addon/fold/foldgutter.js create mode 100644 gulliver/js/codemirror/addon/hint/anyword-hint.js create mode 100644 gulliver/js/codemirror/addon/hint/css-hint.js delete mode 100644 gulliver/js/codemirror/addon/hint/php-hint.js create mode 100644 gulliver/js/codemirror/addon/hint/sql-hint.js create mode 100644 gulliver/js/codemirror/addon/lint/coffeescript-lint.js create mode 100644 gulliver/js/codemirror/addon/lint/css-lint.js create mode 100644 gulliver/js/codemirror/addon/merge/dep/diff_match_patch.js create mode 100644 gulliver/js/codemirror/addon/merge/merge.css create mode 100644 gulliver/js/codemirror/addon/merge/merge.js create mode 100644 gulliver/js/codemirror/addon/mode/multiplex_test.js create mode 100644 gulliver/js/codemirror/addon/scroll/scrollpastend.js create mode 100644 gulliver/js/codemirror/addon/tern/tern.css create mode 100644 gulliver/js/codemirror/addon/tern/tern.js create mode 100644 gulliver/js/codemirror/addon/tern/worker.js create mode 100644 gulliver/js/codemirror/addon/wrap/hardwrap.js create mode 100644 gulliver/js/codemirror/bin/authors.sh create mode 100644 gulliver/js/codemirror/bin/lint create mode 100644 gulliver/js/codemirror/bin/release create mode 100644 gulliver/js/codemirror/bin/source-highlight create mode 100644 gulliver/js/codemirror/bower.json create mode 100644 gulliver/js/codemirror/demo/anywordhint.html delete mode 100644 gulliver/js/codemirror/demo/completePHP.html create mode 100644 gulliver/js/codemirror/demo/hardwrap.html create mode 100644 gulliver/js/codemirror/demo/matchtags.html create mode 100644 gulliver/js/codemirror/demo/merge.html create mode 100644 gulliver/js/codemirror/demo/tern.html create mode 100644 gulliver/js/codemirror/demo/trailingspace.html create mode 100644 gulliver/js/codemirror/doc/activebookmark.js delete mode 100644 gulliver/js/codemirror/doc/baboon.png delete mode 100644 gulliver/js/codemirror/doc/baboon_vector.svg create mode 100644 gulliver/js/codemirror/doc/logo.png create mode 100644 gulliver/js/codemirror/doc/logo.svg delete mode 100644 gulliver/js/codemirror/doc/modes.html rename gulliver/js/codemirror/doc/{oldrelease.html => releases.html} (56%) create mode 100644 gulliver/js/codemirror/keymap/extra.js delete mode 100644 gulliver/js/codemirror/mode/coffeescript/LICENSE create mode 100644 gulliver/js/codemirror/mode/css/less.html create mode 100644 gulliver/js/codemirror/mode/css/less_test.js create mode 100644 gulliver/js/codemirror/mode/index.html create mode 100644 gulliver/js/codemirror/mode/javascript/test.js delete mode 100644 gulliver/js/codemirror/mode/livescript/LICENSE delete mode 100644 gulliver/js/codemirror/mode/ocaml/index.html delete mode 100644 gulliver/js/codemirror/mode/ocaml/ocaml.js delete mode 100644 gulliver/js/codemirror/mode/pascal/LICENSE delete mode 100644 gulliver/js/codemirror/mode/perl/LICENSE delete mode 100644 gulliver/js/codemirror/mode/python/LICENSE.txt delete mode 100644 gulliver/js/codemirror/mode/r/LICENSE delete mode 100644 gulliver/js/codemirror/mode/rst/LICENSE.txt delete mode 100644 gulliver/js/codemirror/mode/ruby/LICENSE delete mode 100644 gulliver/js/codemirror/mode/sieve/LICENSE delete mode 100644 gulliver/js/codemirror/mode/vb/LICENSE.txt delete mode 100644 gulliver/js/codemirror/mode/xquery/LICENSE create mode 100644 gulliver/js/codemirror/test/comment_test.js create mode 100644 gulliver/js/codemirror/test/emacs_test.js delete mode 100644 gulliver/js/codemirror/test/lint/parse-js.js create mode 100644 gulliver/js/codemirror/test/search_test.js create mode 100644 gulliver/js/codemirror/theme/3024-day.css create mode 100644 gulliver/js/codemirror/theme/3024-night.css create mode 100644 gulliver/js/codemirror/theme/base16-dark.css create mode 100644 gulliver/js/codemirror/theme/base16-light.css create mode 100644 gulliver/js/codemirror/theme/mbo.css create mode 100644 gulliver/js/codemirror/theme/midnight.css create mode 100644 gulliver/js/codemirror/theme/paraiso-dark.css create mode 100644 gulliver/js/codemirror/theme/paraiso-light.css create mode 100644 gulliver/js/codemirror/theme/pastel-on-dark.css create mode 100644 gulliver/js/codemirror/theme/the-matrix.css create mode 100644 gulliver/js/codemirror/theme/tomorrow-night-eighties.css diff --git a/gulliver/js/codemirror/.gitattributes b/gulliver/js/codemirror/.gitattributes new file mode 100644 index 000000000..f8bdd60f4 --- /dev/null +++ b/gulliver/js/codemirror/.gitattributes @@ -0,0 +1,8 @@ +*.txt text +*.js text +*.html text +*.md text +*.json text +*.yml text +*.css text +*.svg text diff --git a/gulliver/js/codemirror/.gitignore b/gulliver/js/codemirror/.gitignore new file mode 100644 index 000000000..b471fe6e6 --- /dev/null +++ b/gulliver/js/codemirror/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/npm-debug.log +test.html +.tern-* +*~ +*.swp diff --git a/gulliver/js/codemirror/.travis.yml b/gulliver/js/codemirror/.travis.yml new file mode 100644 index 000000000..baa0031d5 --- /dev/null +++ b/gulliver/js/codemirror/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.8 diff --git a/gulliver/js/codemirror/AUTHORS b/gulliver/js/codemirror/AUTHORS new file mode 100644 index 000000000..737acedab --- /dev/null +++ b/gulliver/js/codemirror/AUTHORS @@ -0,0 +1,300 @@ +List of CodeMirror contributors. Updated before every release. + +4r2r +Aaron Brooks +Abe Fettig +Adam King +adanlobato +Adán Lobato +aeroson +Ahmad Amireh +Ahmad M. Zawawi +ahoward +Akeksandr Motsjonov +Alberto Pose +Albert Xing +Alexander Pavlov +Alexander Schepanovski +Alexander Solovyov +alexey-k +Alex Piggott +Amy +Ananya Sen +AndersMad +Anderson Mesquita +Andre von Houck +Andrey Lushnikov +Andy Joslin +Andy Kimball +Andy Li +angelozerr +angelo.zerr@gmail.com +Ankit Ahuja +Ansel Santosa +Anthony Grimes +Anton Kovalyov +areos +AtomicPages LLC +Atul Bhouraskar +Aurelian Oancea +Bastian Müller +benbro +Beni Cherniavsky-Paskin +Benjamin DeCoste +Ben Keen +boomyjee +borawjm +Brandon Frohs +Brandon Wamboldt +Brett Zamir +Brian Sletten +Bruce Mitchener +Chandra Sekhar Pydi +Charles Skelton +Chris Coyier +Chris Granger +Chris Morgan +Christopher Brown +ciaranj +CodeAnimal +ComFreek +dagsta +Dan Heberden +Daniel, Dao Quang Minh +Daniel Faust +Daniel Huigens +Daniel KJ +Daniel Neel +Daniel Parnell +Danny Yoo +Darius Roberts +David Mignot +David Pathakjee +deebugger +Deep Thought +Dominator008 +Domizio Demichelis +Drew Bratcher +Drew Hintz +Drew Khoury +Dror BG +duralog +eborden +edsharp +ekhaled +Enam Mijbah Noor +Eric Allam +eustas +Fabio Zendhi Nagao +Fauntleroy +fbuchinger +feizhang365 +Felipe Lalanne +Felix Raab +Filip Noetzel +flack +ForbesLindesay +Forbes Lindesay +Ford_Lawnmower +Gabriel Nahmias +galambalazs +Gautam Mehta +Glenn Jorde +Glenn Ruehle +Golevka +Gordon Smith +greengiant +Guillaume Massé +Guillaume Massé +Hans Engel +Hardest +Hasan Karahan +hitsthings +Hocdoc +Ian Beck +Ian Dickinson +Ian Wehrman +Ian Wetherbee +Ice White +ICHIKAWA, Yuji +ilvalle +Ingo Richter +Irakli Gozalishvili +Ivan Kurnosov +Jacob Lee +Jakub Vrana +James Campos +James Thorne +Jamie Hill +Jan Jongboom +jankeromnes +Jan Keromnes +Jan Odvarko +Jan T. Sott +Jason +Jason Grout +Jason Johnston +Jason San Jose +Jason Siefken +Jean Boussier +jeffkenton +Jeff Pickhardt +jem (graphite) +Jochen Berger +Johan Ask +John Connor +John Lees-Miller +John Snelson +John Van Der Loo +jongalloway +Jon Malmaud +Joost-Wim Boekesteijn +Joseph Pecoraro +Joshua Newman +jots +jsoojeon +Juan Benavides Romero +Jucovschi Constantin +Juho Vuori +jwallers@gmail.com +kaniga +Ken Newman +Ken Rockot +Kevin Sawicki +Klaus Silveira +Koh Zi Han, Cliff +komakino +Konstantin Lopuhin +koops +ks-ifware +kubelsmieci +Lanny +Laszlo Vidacs +leaf corcoran +Leonya Khachaturov +Liam Newman +LM +Lorenzo Stoakes +Luciano Longo +lynschinzer +Maksim Lin +Maksym Taran +Marat Dreizin +Marco Aurélio +Marijn Haverbeke +Mario Pietsch +Mark Lentczner +Marko Bonaci +Martin Balek +Martín Gaitán +Martin Hasoň +Mason Malone +Mateusz Paprocki +mats cronqvist +Matthew Beale +Matthias BUSSONNIER +Matt McDonald +Matt Pass +Matt Sacks +Maximilian Hils +Maxim Kraev +Max Kirsch +mbarkhau +Metatheos +Micah Dubinko +Michael Lehenbauer +Michael Zhou +Mighty Guava +Miguel Castillo +Mike +Mike Brevoort +Mike Diaz +Mike Ivanov +Mike Kadin +MinRK +Miraculix87 +misfo +mloginov +mps +Narciso Jaramillo +Nathan Williams +nerbert +nguillaumin +Niels van Groningen +Nikita Beloglazov +Nikita Vasilyev +Nikolay Kostov +nlwillia +pablo +Page +Patrick Strawderman +Paul Garvin +Paul Ivanov +Pavel Feldman +Pavel Strashkin +Paweł Bartkiewicz +peteguhl +peterkroon +Peter Kroon +prasanthj +Prasanth J +Rahul +Randy Edmunds +Richard Z.H. Wang +robertop23 +Robert Plummer +Ruslan Osmanov +sabaca +Samuel Ainsworth +sandeepshetty +santec +Sascha Peilicke +satchmorun +sathyamoorthi +SCLINIC\jdecker +Sebastian Zaha +shaund +shaun gilchrist +Shawn A +Shiv Deepak +Shmuel Englard +soliton4 +sonson +spastorelli +Stanislav Oaserele +Stas Kobzar +Stefan Borsje +Steffen Beyer +Steve O'Hara +stoskov +Taha Jahangir +Tarmil +tfjgeorge +Thaddee Tyl +think +Thomas Dvornik +Thomas Schmid +Tim Baumann +Timothy Farrell +Timothy Hatcher +TobiasBg +Tomas-A +Tomas Varaneckas +Tom Erik Støwer +Tom MacWright +Tony Jian +Travis Heppe +Vestimir Markov +vf +Volker Mische +wenli +Wesley Wiser +William Jamieson +Wojtek Ptak +Xavier Mendez +YNH Webdev +Yunchi Luo +Yuvi Panda +Zachary Dremann +zziuni +魏鹏刚 diff --git a/gulliver/js/codemirror/CONTRIBUTING.md b/gulliver/js/codemirror/CONTRIBUTING.md index afc18373c..8938f6204 100644 --- a/gulliver/js/codemirror/CONTRIBUTING.md +++ b/gulliver/js/codemirror/CONTRIBUTING.md @@ -4,12 +4,12 @@ - [Submitting bug reports](#submitting-bug-reports-) - [Contributing code](#contributing-code-) -## Getting help [^](#how-to-contribute) +## Getting help Community discussion, questions, and informal bug reporting is done on the [CodeMirror Google group](http://groups.google.com/group/codemirror). -## Submitting bug reports [^](#how-to-contribute) +## Submitting bug reports The preferred way to report bugs is to use the [GitHub issue tracker](http://github.com/marijnh/CodeMirror/issues). Before @@ -45,7 +45,7 @@ should be asked on the [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and include the resulting link in your bug report. -## Contributing code [^](#how-to-contribute) +## Contributing code - Make sure you have a [GitHub Account](https://github.com/signup/free) - Fork [CodeMirror](https://github.com/marijnh/CodeMirror/) @@ -56,6 +56,8 @@ should be asked on the test suite under `mode/XXX/test.js`. Feel free to add new test suites to modes that don't have one yet (be sure to link the new tests into `test/index.html`). +- Follow the general code style of the rest of the project (see + below). Run `bin/lint` to verify that the linter is happy. - Make sure all tests pass. Visit `test/index.html` in your browser to run them. - Submit a pull request @@ -65,6 +67,6 @@ should be asked on the - 2 spaces per indentation level, no tabs. - Include semicolons after statements. -- Note that the linter (`test/lint/lint.js`) which is run after each - commit complains about unused variables and functions. Prefix their - names with an underscore to muffle it. +- Note that the linter (`bin/lint`) which is run after each commit + complains about unused variables and functions. Prefix their names + with an underscore to muffle it. diff --git a/gulliver/js/codemirror/LICENSE b/gulliver/js/codemirror/LICENSE index 482d55eb7..442d11cdc 100644 --- a/gulliver/js/codemirror/LICENSE +++ b/gulliver/js/codemirror/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2013 by Marijn Haverbeke +Copyright (C) 2013 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17,7 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Please note that some subdirectories of the CodeMirror distribution -include their own LICENSE files, and are released under different -licences. diff --git a/gulliver/js/codemirror/README.md b/gulliver/js/codemirror/README.md index 976584e3b..61f6b6452 100644 --- a/gulliver/js/codemirror/README.md +++ b/gulliver/js/codemirror/README.md @@ -1,4 +1,6 @@ -# CodeMirror [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) +# CodeMirror +[![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) +[![NPM version](https://badge.fury.io/js/codemirror.png)](http://badge.fury.io/js/codemirror) CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding diff --git a/gulliver/js/codemirror/addon/comment/comment.js b/gulliver/js/codemirror/addon/comment/comment.js new file mode 100644 index 000000000..5975b0bf6 --- /dev/null +++ b/gulliver/js/codemirror/addon/comment/comment.js @@ -0,0 +1,149 @@ +(function() { + "use strict"; + + var noOptions = {}; + var nonWS = /[^\s\u00a0]/; + var Pos = CodeMirror.Pos; + + function firstNonWS(str) { + var found = str.search(nonWS); + return found == -1 ? 0 : found; + } + + CodeMirror.commands.toggleComment = function(cm) { + var from = cm.getCursor("start"), to = cm.getCursor("end"); + cm.uncomment(from, to) || cm.lineComment(from, to); + }; + + CodeMirror.defineExtension("lineComment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var commentString = options.lineComment || mode.lineComment; + if (!commentString) { + if (options.blockCommentStart || mode.blockCommentStart) { + options.fullLines = true; + self.blockComment(from, to, options); + } + return; + } + var firstLine = self.getLine(from.line); + if (firstLine == null) return; + var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); + var pad = options.padding == null ? " " : options.padding; + var blankLines = options.commentBlankLines || from.line == to.line; + + self.operation(function() { + if (options.indent) { + var baseString = firstLine.slice(0, firstNonWS(firstLine)); + for (var i = from.line; i < end; ++i) { + var line = self.getLine(i), cut = baseString.length; + if (!blankLines && !nonWS.test(line)) continue; + if (line.slice(0, cut) != baseString) cut = firstNonWS(line); + self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); + } + } else { + for (var i = from.line; i < end; ++i) { + if (blankLines || nonWS.test(self.getLine(i))) + self.replaceRange(commentString + pad, Pos(i, 0)); + } + } + }); + }); + + CodeMirror.defineExtension("blockComment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var startString = options.blockCommentStart || mode.blockCommentStart; + var endString = options.blockCommentEnd || mode.blockCommentEnd; + if (!startString || !endString) { + if ((options.lineComment || mode.lineComment) && options.fullLines != false) + self.lineComment(from, to, options); + return; + } + + var end = Math.min(to.line, self.lastLine()); + if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; + + var pad = options.padding == null ? " " : options.padding; + if (from.line > end) return; + + self.operation(function() { + if (options.fullLines != false) { + var lastLineHasText = nonWS.test(self.getLine(end)); + self.replaceRange(pad + endString, Pos(end)); + self.replaceRange(startString + pad, Pos(from.line, 0)); + var lead = options.blockCommentLead || mode.blockCommentLead; + if (lead != null) for (var i = from.line + 1; i <= end; ++i) + if (i != end || lastLineHasText) + self.replaceRange(lead + pad, Pos(i, 0)); + } else { + self.replaceRange(endString, to); + self.replaceRange(startString, from); + } + }); + }); + + CodeMirror.defineExtension("uncomment", function(from, to, options) { + if (!options) options = noOptions; + var self = this, mode = self.getModeAt(from); + var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end); + + // Try finding line comments + var lineString = options.lineComment || mode.lineComment, lines = []; + var pad = options.padding == null ? " " : options.padding, didSomething; + lineComment: { + if (!lineString) break lineComment; + for (var i = start; i <= end; ++i) { + var line = self.getLine(i); + var found = line.indexOf(lineString); + if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; + if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; + if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; + lines.push(line); + } + self.operation(function() { + for (var i = start; i <= end; ++i) { + var line = lines[i - start]; + var pos = line.indexOf(lineString), endPos = pos + lineString.length; + if (pos < 0) continue; + if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; + didSomething = true; + self.replaceRange("", Pos(i, pos), Pos(i, endPos)); + } + }); + if (didSomething) return true; + } + + // Try block comments + var startString = options.blockCommentStart || mode.blockCommentStart; + var endString = options.blockCommentEnd || mode.blockCommentEnd; + if (!startString || !endString) return false; + var lead = options.blockCommentLead || mode.blockCommentLead; + var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end); + var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString); + if (close == -1 && start != end) { + endLine = self.getLine(--end); + close = endLine.lastIndexOf(endString); + } + if (open == -1 || close == -1 || + !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || + !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) + return false; + + self.operation(function() { + self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), + Pos(end, close + endString.length)); + var openEnd = open + startString.length; + if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; + self.replaceRange("", Pos(start, open), Pos(start, openEnd)); + if (lead) for (var i = start + 1; i <= end; ++i) { + var line = self.getLine(i), found = line.indexOf(lead); + if (found == -1 || nonWS.test(line.slice(0, found))) continue; + var foundEnd = found + lead.length; + if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; + self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); + } + }); + return true; + }); +})(); diff --git a/gulliver/js/codemirror/addon/comment/continuecomment.js b/gulliver/js/codemirror/addon/comment/continuecomment.js new file mode 100644 index 000000000..a3370a607 --- /dev/null +++ b/gulliver/js/codemirror/addon/comment/continuecomment.js @@ -0,0 +1,54 @@ +(function() { + var modes = ["clike", "css", "javascript"]; + for (var i = 0; i < modes.length; ++i) + CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); + + function continueComment(cm) { + var pos = cm.getCursor(), token = cm.getTokenAt(pos); + if (token.type != "comment" || cm.getOption("disableInput")) return CodeMirror.Pass; + var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; + + var insert; + if (mode.blockCommentStart && mode.blockCommentContinue) { + var end = token.string.indexOf(mode.blockCommentEnd); + var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; + if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { + // Comment ended, don't continue it + } else if (token.string.indexOf(mode.blockCommentStart) == 0) { + insert = full.slice(0, token.start); + if (!/^\s*$/.test(insert)) { + insert = ""; + for (var i = 0; i < token.start; ++i) insert += " "; + } + } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && + found + mode.blockCommentContinue.length > token.start && + /^\s*$/.test(full.slice(0, found))) { + insert = full.slice(0, found); + } + if (insert != null) insert += mode.blockCommentContinue; + } + if (insert == null && mode.lineComment) { + var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); + if (found > -1) { + insert = line.slice(0, found); + if (/\S/.test(insert)) insert = null; + else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; + } + } + + if (insert != null) + cm.replaceSelection("\n" + insert, "end"); + else + return CodeMirror.Pass; + } + + CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { + if (prev && prev != CodeMirror.Init) + cm.removeKeyMap("continueComment"); + if (val) { + var map = {name: "continueComment"}; + map[typeof val == "string" ? val : "Enter"] = continueComment; + cm.addKeyMap(map); + } + }); +})(); diff --git a/gulliver/js/codemirror/addon/dialog/dialog.js b/gulliver/js/codemirror/addon/dialog/dialog.js index 71e228744..41d7bf866 100644 --- a/gulliver/js/codemirror/addon/dialog/dialog.js +++ b/gulliver/js/codemirror/addon/dialog/dialog.js @@ -10,11 +10,22 @@ } else { dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; } - dialog.innerHTML = template; + if (typeof template == "string") { + dialog.innerHTML = template; + } else { // Assuming it's a detached DOM element. + dialog.appendChild(template); + } return dialog; } + function closeNotification(cm, newVal) { + if (cm.state.currentNotificationClose) + cm.state.currentNotificationClose(); + cm.state.currentNotificationClose = newVal; + } + CodeMirror.defineExtension("openDialog", function(template, callback, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var closed = false, me = this; function close() { @@ -24,6 +35,7 @@ } var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { + if (options && options.value) inp.value = options.value; CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { @@ -51,6 +63,7 @@ }); CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; @@ -77,4 +90,33 @@ CodeMirror.on(b, "focus", function() { ++blurring; }); } }); + + /* + * openNotification + * Opens a notification, that can be closed with an optional timer + * (default 5000ms timer) and always closes on click. + * + * If a notification is opened while another is opened, it will close the + * currently opened one and open the new one immediately. + */ + CodeMirror.defineExtension("openNotification", function(template, options) { + closeNotification(this, close); + var dialog = dialogDiv(this, template, options && options.bottom); + var duration = options && (options.duration === undefined ? 5000 : options.duration); + var closed = false, doneTimer; + + function close() { + if (closed) return; + closed = true; + clearTimeout(doneTimer); + dialog.parentNode.removeChild(dialog); + } + + CodeMirror.on(dialog, 'click', function(e) { + CodeMirror.e_preventDefault(e); + close(); + }); + if (duration) + doneTimer = setTimeout(close, options.duration); + }); })(); diff --git a/gulliver/js/codemirror/addon/display/fullscreen.css b/gulliver/js/codemirror/addon/display/fullscreen.css new file mode 100644 index 000000000..437acd89b --- /dev/null +++ b/gulliver/js/codemirror/addon/display/fullscreen.css @@ -0,0 +1,6 @@ +.CodeMirror-fullscreen { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + height: auto; + z-index: 9; +} diff --git a/gulliver/js/codemirror/addon/display/fullscreen.js b/gulliver/js/codemirror/addon/display/fullscreen.js new file mode 100644 index 000000000..a442f6a43 --- /dev/null +++ b/gulliver/js/codemirror/addon/display/fullscreen.js @@ -0,0 +1,31 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { + if (old == CodeMirror.Init) old = false; + if (!old == !val) return; + if (val) setFullscreen(cm); + else setNormal(cm); + }); + + function setFullscreen(cm) { + var wrap = cm.getWrapperElement(); + cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, + width: wrap.style.width, height: wrap.style.height}; + wrap.style.width = ""; + wrap.style.height = "auto"; + wrap.className += " CodeMirror-fullscreen"; + document.documentElement.style.overflow = "hidden"; + cm.refresh(); + } + + function setNormal(cm) { + var wrap = cm.getWrapperElement(); + wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); + document.documentElement.style.overflow = ""; + var info = cm.state.fullScreenRestore; + wrap.style.width = info.width; wrap.style.height = info.height; + window.scrollTo(info.scrollLeft, info.scrollTop); + cm.refresh(); + } +})(); diff --git a/gulliver/js/codemirror/addon/display/placeholder.js b/gulliver/js/codemirror/addon/display/placeholder.js index f85f2df12..748afe727 100644 --- a/gulliver/js/codemirror/addon/display/placeholder.js +++ b/gulliver/js/codemirror/addon/display/placeholder.js @@ -2,12 +2,10 @@ CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - cm.on("focus", onFocus); cm.on("blur", onBlur); cm.on("change", onChange); onChange(cm); } else if (!val && prev) { - cm.off("focus", onFocus); cm.off("blur", onBlur); cm.off("change", onChange); clearPlaceholder(cm); @@ -19,23 +17,20 @@ }); function clearPlaceholder(cm) { - if (cm._placeholder) { - cm._placeholder.parentNode.removeChild(cm._placeholder); - cm._placeholder = null; + if (cm.state.placeholder) { + cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); + cm.state.placeholder = null; } } function setPlaceholder(cm) { clearPlaceholder(cm); - var elt = cm._placeholder = document.createElement("pre"); + var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; elt.className = "CodeMirror-placeholder"; elt.appendChild(document.createTextNode(cm.getOption("placeholder"))); cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); } - function onFocus(cm) { - clearPlaceholder(cm); - } function onBlur(cm) { if (isEmpty(cm)) setPlaceholder(cm); } @@ -43,7 +38,6 @@ var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); - if (cm.hasFocus()) return; if (empty) setPlaceholder(cm); else clearPlaceholder(cm); } diff --git a/gulliver/js/codemirror/addon/edit/closebrackets.js b/gulliver/js/codemirror/addon/edit/closebrackets.js index b46caca02..0575222be 100644 --- a/gulliver/js/codemirror/addon/edit/closebrackets.js +++ b/gulliver/js/codemirror/addon/edit/closebrackets.js @@ -1,34 +1,47 @@ (function() { var DEFAULT_BRACKETS = "()[]{}''\"\""; + var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; var SPACE_CHAR_REGEX = /\s/; CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { - var wasOn = old && old != CodeMirror.Init; - if (val && !wasOn) - cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS)); - else if (!val && wasOn) + if (old != CodeMirror.Init && old) cm.removeKeyMap("autoCloseBrackets"); + if (!val) return; + var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; + if (typeof val == "string") pairs = val; + else if (typeof val == "object") { + if (val.pairs != null) pairs = val.pairs; + if (val.explode != null) explode = val.explode; + } + var map = buildKeymap(pairs); + if (explode) map.Enter = buildExplodeHandler(explode); + cm.addKeyMap(map); }); + function charsAround(cm, pos) { + var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1), + CodeMirror.Pos(pos.line, pos.ch + 1)); + return str.length == 2 ? str : null; + } + function buildKeymap(pairs) { var map = { name : "autoCloseBrackets", Backspace: function(cm) { - if (cm.somethingSelected()) return CodeMirror.Pass; - var cur = cm.getCursor(), line = cm.getLine(cur.line); - if (cur.ch && cur.ch < line.length && - pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) + if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass; + var cur = cm.getCursor(), around = charsAround(cm, cur); + if (around && pairs.indexOf(around) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); else return CodeMirror.Pass; } }; - var closingBrackets = []; + var closingBrackets = ""; for (var i = 0; i < pairs.length; i += 2) (function(left, right) { - if (left != right) closingBrackets.push(right); + if (left != right) closingBrackets += right; function surround(cm) { - var selection = cm.getSelection(); - cm.replaceSelection(left + selection + right); + var selection = cm.getSelection(); + cm.replaceSelection(left + selection + right); } function maybeOverwrite(cm) { var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); @@ -36,10 +49,15 @@ else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { + if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" || + cm.getOption("disableInput")) + return CodeMirror.Pass; if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); - var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch); + var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : ""; + if (left == right && CodeMirror.isWordChar(curChar)) + return CodeMirror.Pass; if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); else @@ -49,4 +67,18 @@ })(pairs.charAt(i), pairs.charAt(i + 1)); return map; } + + function buildExplodeHandler(pairs) { + return function(cm) { + var cur = cm.getCursor(), around = charsAround(cm, cur); + if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput")) + return CodeMirror.Pass; + cm.operation(function() { + var newPos = CodeMirror.Pos(cur.line + 1, 0); + cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); + cm.indentLine(cur.line + 1, null, true); + cm.indentLine(cur.line + 2, null, true); + }); + }; + } })(); diff --git a/gulliver/js/codemirror/addon/edit/closetag.js b/gulliver/js/codemirror/addon/edit/closetag.js index 54fa19546..cad776a78 100644 --- a/gulliver/js/codemirror/addon/edit/closetag.js +++ b/gulliver/js/codemirror/addon/edit/closetag.js @@ -24,16 +24,15 @@ (function() { CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { - if (val && (old == CodeMirror.Init || !old)) { - var map = {name: "autoCloseTags"}; - if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); }; - if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); }; - cm.addKeyMap(map); - } else if (!val && (old != CodeMirror.Init && old)) { + if (old != CodeMirror.Init && old) cm.removeKeyMap("autoCloseTags"); - } + if (!val) return; + var map = {name: "autoCloseTags"}; + if (typeof val != "object" || val.whenClosing) + map["'/'"] = function(cm) { return autoCloseSlash(cm); }; + if (typeof val != "object" || val.whenOpening) + map["'>'"] = function(cm) { return autoCloseGT(cm); }; + cm.addKeyMap(map); }); var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", @@ -41,39 +40,48 @@ var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; - function autoCloseTag(cm, ch) { + function autoCloseGT(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml") return CodeMirror.Pass; + if (inner.mode.name != "xml" || !state.tagName || cm.getOption("disableInput")) return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - if (ch == ">" && state.tagName) { - var tagName = state.tagName; - if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); - var lowerTagName = tagName.toLowerCase(); - // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 || - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) - return CodeMirror.Pass; + var tagName = state.tagName; + if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); + var lowerTagName = tagName.toLowerCase(); + // Don't process the '>' at the end of an end-tag or self-closing tag + if (!tagName || + tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || + tok.type == "tag" && state.type == "closeTag" || + tok.string.indexOf("/") == (tok.string.length - 1) || // match something like + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || + CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName, + Math.min(cm.lastLine() + 1, pos.line + 50))) + return CodeMirror.Pass; - var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; - var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); - cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", - {head: curPos, anchor: curPos}); - if (doIndent) { - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); - } - return; - } else if (ch == "/" && tok.string == "<") { - var tagName = state.context && state.context.tagName; - if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); - return; + var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; + var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); + cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", + {head: curPos, anchor: curPos}); + if (doIndent) { + cm.indentLine(pos.line + 1, null, true); + cm.indentLine(pos.line + 2, null); } - return CodeMirror.Pass; + } + + function autoCloseSlash(cm) { + var pos = cm.getCursor(), tok = cm.getTokenAt(pos); + var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; + if (tok.type == "string" || tok.string.charAt(0) != "<" || + tok.start != pos.ch - 1 || inner.mode.name != "xml" || + cm.getOption("disableInput")) + return CodeMirror.Pass; + + var tagName = state.context && state.context.tagName; + if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); } function indexOf(collection, elt) { diff --git a/gulliver/js/codemirror/addon/edit/continuecomment.js b/gulliver/js/codemirror/addon/edit/continuecomment.js deleted file mode 100644 index 308026229..000000000 --- a/gulliver/js/codemirror/addon/edit/continuecomment.js +++ /dev/null @@ -1,44 +0,0 @@ -(function() { - var modes = ["clike", "css", "javascript"]; - for (var i = 0; i < modes.length; ++i) - CodeMirror.extendMode(modes[i], {blockCommentStart: "/*", - blockCommentEnd: "*/", - blockCommentContinue: " * "}); - - function continueComment(cm) { - var pos = cm.getCursor(), token = cm.getTokenAt(pos); - var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; - var space; - - if (token.type == "comment" && mode.blockCommentStart) { - var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; - if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { - // Comment ended, don't continue it - } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - space = full.slice(0, token.start); - if (!/^\s*$/.test(space)) { - space = ""; - for (var i = 0; i < token.start; ++i) space += " "; - } - } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && - found + mode.blockCommentContinue.length > token.start && - /^\s*$/.test(full.slice(0, found))) { - space = full.slice(0, found); - } - } - - if (space != null) - cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); - else - return CodeMirror.Pass; - } - - CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { - if (prev && prev != CodeMirror.Init) - cm.removeKeyMap("continueComment"); - var map = {name: "continueComment"}; - map[typeof val == "string" ? val : "Enter"] = continueComment; - cm.addKeyMap(map); - }); -})(); diff --git a/gulliver/js/codemirror/addon/edit/continuelist.js b/gulliver/js/codemirror/addon/edit/continuelist.js index fb1fc38ba..190b41dcb 100644 --- a/gulliver/js/codemirror/addon/edit/continuelist.js +++ b/gulliver/js/codemirror/addon/edit/continuelist.js @@ -5,8 +5,10 @@ unorderedBullets = '*+-'; CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var pos = cm.getCursor(), - inList = cm.getStateAfter(pos.line).list, + inList = cm.getStateAfter(pos.line).list !== false, match; if (!inList || !(match = cm.getLine(pos.line).match(listRE))) { diff --git a/gulliver/js/codemirror/addon/edit/matchbrackets.js b/gulliver/js/codemirror/addon/edit/matchbrackets.js index f4925b725..465b6ccaa 100644 --- a/gulliver/js/codemirror/addon/edit/matchbrackets.js +++ b/gulliver/js/codemirror/addon/edit/matchbrackets.js @@ -3,25 +3,29 @@ (document.documentMode == null || document.documentMode < 8); var Pos = CodeMirror.Pos; - // Disable brace matching in long lines, since it'll cause hugely slow updates - var maxLineLen = 1000; var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - function findMatchingBracket(cm) { - var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; + function findMatchingBracket(cm, where, strict) { + var state = cm.state.matchBrackets; + var maxScanLen = (state && state.maxScanLineLength) || 10000; + var maxScanLines = (state && state.maxScanLines) || 100; + + var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; if (!match) return null; var forward = match.charAt(1) == ">", d = forward ? 1 : -1; - var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type; + if (strict && forward != (pos == cur.ch)) return null; + var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1)); var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; function scan(line, lineNo, start) { if (!line.text) return; var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; + if (line.text.length > maxScanLen) return null; if (start != null) pos = start + d; for (; pos != end; pos += d) { var ch = line.text.charAt(pos); - if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) { + if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) { var match = matching[ch]; if (match.charAt(1) == ">" == forward) stack.push(ch); else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; @@ -29,25 +33,28 @@ } } } - for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { + for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) { if (i == cur.line) found = scan(line, i, pos); else found = scan(cm.getLineHandle(i), i); if (found) break; } - return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match}; + return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), + match: found && found.match, forward: forward}; } function matchBrackets(cm, autoclear) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; var found = findMatchingBracket(cm); - if (!found || cm.getLine(found.from.line).length > maxLineLen || - found.to && cm.getLine(found.to.line).length > maxLineLen) + if (!found || cm.getLine(found.from.line).length > maxHighlightLen || + found.to && cm.getLine(found.to.line).length > maxHighlightLen) return; var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textare whever this fires. + // input stops going to the textarea whenever this fires. if (ie_lt8 && cm.state.focused) cm.display.input.focus(); var clear = function() { cm.operation(function() { one.clear(); two && two.clear(); }); @@ -64,11 +71,17 @@ }); } - CodeMirror.defineOption("matchBrackets", false, function(cm, val) { - if (val) cm.on("cursorActivity", doMatchBrackets); - else cm.off("cursorActivity", doMatchBrackets); + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) + cm.off("cursorActivity", doMatchBrackets); + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + } }); CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); - CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){ + return findMatchingBracket(this, pos, strict); + }); })(); diff --git a/gulliver/js/codemirror/addon/edit/matchtags.js b/gulliver/js/codemirror/addon/edit/matchtags.js new file mode 100644 index 000000000..f189c1f8e --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/matchtags.js @@ -0,0 +1,56 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("matchTags", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchTags); + cm.off("viewportChange", maybeUpdateMatch); + clear(cm); + } + if (val) { + cm.state.matchBothTags = typeof val == "object" && val.bothTags; + cm.on("cursorActivity", doMatchTags); + cm.on("viewportChange", maybeUpdateMatch); + doMatchTags(cm); + } + }); + + function clear(cm) { + if (cm.state.tagHit) cm.state.tagHit.clear(); + if (cm.state.tagOther) cm.state.tagOther.clear(); + cm.state.tagHit = cm.state.tagOther = null; + } + + function doMatchTags(cm) { + cm.state.failedTagMatch = false; + cm.operation(function() { + clear(cm); + if (cm.somethingSelected()) return; + var cur = cm.getCursor(), range = cm.getViewport(); + range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); + var match = CodeMirror.findMatchingTag(cm, cur, range); + if (!match) return; + if (cm.state.matchBothTags) { + var hit = match.at == "open" ? match.open : match.close; + if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"}); + } + var other = match.at == "close" ? match.open : match.close; + if (other) + cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); + else + cm.state.failedTagMatch = true; + }); + } + + function maybeUpdateMatch(cm) { + if (cm.state.failedTagMatch) doMatchTags(cm); + } + + CodeMirror.commands.toMatchingTag = function(cm) { + var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); + if (found) { + var other = found.at == "close" ? found.open : found.close; + if (other) cm.setSelection(other.to, other.from); + } + }; +})(); diff --git a/gulliver/js/codemirror/addon/edit/trailingspace.js b/gulliver/js/codemirror/addon/edit/trailingspace.js new file mode 100644 index 000000000..f6bb02645 --- /dev/null +++ b/gulliver/js/codemirror/addon/edit/trailingspace.js @@ -0,0 +1,15 @@ +CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); +}); diff --git a/gulliver/js/codemirror/addon/fold/brace-fold.js b/gulliver/js/codemirror/addon/fold/brace-fold.js index aad6e0141..2560b2b94 100644 --- a/gulliver/js/codemirror/addon/fold/brace-fold.js +++ b/gulliver/js/codemirror/addon/fold/brace-fold.js @@ -1,31 +1,93 @@ -CodeMirror.braceRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "brace", function(cm, start) { var line = start.line, lineText = cm.getLine(line); - var at = lineText.length, startChar, tokenType; - for (;;) { - var found = lineText.lastIndexOf("{", at); - if (found < start.ch) break; - tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; - if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } - at = found - 1; + var startCh, tokenType; + + function findOpening(openCh) { + for (var at = start.ch, pass = 0;;) { + var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); + if (found == -1) { + if (pass == 1) break; + pass = 1; + at = lineText.length; + continue; + } + if (pass == 1 && found < start.ch) break; + tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); + if (!/^(comment|string)/.test(tokenType)) return found + 1; + at = found - 1; + } } - if (startChar == null || lineText.lastIndexOf("}") > startChar) return; - var count = 1, lastLine = cm.lineCount(), end, endCh; - outer: for (var i = line + 1; i < lastLine; ++i) { - var text = cm.getLine(i), pos = 0; + + var startToken = "{", endToken = "}", startCh = findOpening("{"); + if (startCh == null) { + startToken = "[", endToken = "]"; + startCh = findOpening("["); + } + + if (startCh == null) return; + var count = 1, lastLine = cm.lastLine(), end, endCh; + outer: for (var i = line; i <= lastLine; ++i) { + var text = cm.getLine(i), pos = i == line ? startCh : 0; for (;;) { - var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); + var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); if (nextOpen < 0) nextOpen = text.length; if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; - if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { + if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; endCh = pos; break outer; } } ++pos; } } - if (end == null || end == line + 1) return; - return {from: CodeMirror.Pos(line, startChar + 1), + if (end == null || line == end && endCh == startCh) return; + return {from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh)}; -}; +}); +CodeMirror.braceRangeFinder = CodeMirror.fold.brace; // deprecated + +CodeMirror.registerHelper("fold", "import", function(cm, start) { + function hasImport(line) { + if (line < cm.firstLine() || line > cm.lastLine()) return null; + var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); + if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1)); + if (start.type != "keyword" || start.string != "import") return null; + // Now find closing semicolon, return its position + for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) { + var text = cm.getLine(i), semi = text.indexOf(";"); + if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)}; + } + } + + var start = start.line, has = hasImport(start), prev; + if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1)) + return null; + for (var end = has.end;;) { + var next = hasImport(end.line + 1); + if (next == null) break; + end = next.end; + } + return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end}; +}); +CodeMirror.importRangeFinder = CodeMirror.fold["import"]; // deprecated + +CodeMirror.registerHelper("fold", "include", function(cm, start) { + function hasInclude(line) { + if (line < cm.firstLine() || line > cm.lastLine()) return null; + var start = cm.getTokenAt(CodeMirror.Pos(line, 1)); + if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1)); + if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8; + } + + var start = start.line, has = hasInclude(start); + if (has == null || hasInclude(start - 1) != null) return null; + for (var end = start;;) { + var next = hasInclude(end + 1); + if (next == null) break; + ++end; + } + return {from: CodeMirror.Pos(start, has + 1), + to: cm.clipPos(CodeMirror.Pos(end))}; +}); +CodeMirror.includeRangeFinder = CodeMirror.fold.include; // deprecated diff --git a/gulliver/js/codemirror/addon/fold/comment-fold.js b/gulliver/js/codemirror/addon/fold/comment-fold.js new file mode 100644 index 000000000..26e72f09f --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/comment-fold.js @@ -0,0 +1,42 @@ +CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { + return mode.blockCommentStart && mode.blockCommentEnd; +}, function(cm, start) { + var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; + if (!startToken || !endToken) return; + var line = start.line, lineText = cm.getLine(line); + + var startCh; + for (var at = start.ch, pass = 0;;) { + var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1); + if (found == -1) { + if (pass == 1) return; + pass = 1; + at = lineText.length; + continue; + } + if (pass == 1 && found < start.ch) return; + if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)))) { + startCh = found + startToken.length; + break; + } + at = found - 1; + } + + var depth = 1, lastLine = cm.lastLine(), end, endCh; + outer: for (var i = line; i <= lastLine; ++i) { + var text = cm.getLine(i), pos = i == line ? startCh : 0; + for (;;) { + var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); + if (nextOpen < 0) nextOpen = text.length; + if (nextClose < 0) nextClose = text.length; + pos = Math.min(nextOpen, nextClose); + if (pos == text.length) break; + if (pos == nextOpen) ++depth; + else if (!--depth) { end = i; endCh = pos; break outer; } + ++pos; + } + } + if (end == null || line == end && endCh == startCh) return; + return {from: CodeMirror.Pos(line, startCh), + to: CodeMirror.Pos(end, endCh)}; +}); diff --git a/gulliver/js/codemirror/addon/fold/foldcode.js b/gulliver/js/codemirror/addon/fold/foldcode.js index b8b4b0da9..5c00d7093 100644 --- a/gulliver/js/codemirror/addon/fold/foldcode.js +++ b/gulliver/js/codemirror/addon/fold/foldcode.js @@ -1,32 +1,86 @@ -CodeMirror.newFoldFunction = function(rangeFinder, widget) { - if (widget == null) widget = "\u2194"; - if (typeof widget == "string") { - var text = document.createTextNode(widget); - widget = document.createElement("span"); - widget.appendChild(text); - widget.className = "CodeMirror-foldmarker"; - } +(function() { + "use strict"; - return function(cm, pos) { + function doFold(cm, pos, options, force) { + var finder = options && (options.call ? options : options.rangeFinder); + if (!finder) finder = CodeMirror.fold.auto; if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); - var range = rangeFinder(cm, pos); - if (!range) return; + var minSize = options && options.minFoldSize || 0; - var present = cm.findMarksAt(range.from), cleared = 0; - for (var i = 0; i < present.length; ++i) { - if (present[i].__isFold) { - ++cleared; - present[i].clear(); + function getRange(allowFolded) { + var range = finder(cm, pos); + if (!range || range.to.line - range.from.line < minSize) return null; + var marks = cm.findMarksAt(range.from); + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold && force !== "fold") { + if (!allowFolded) return null; + range.cleared = true; + marks[i].clear(); + } } + return range; } - if (cleared) return; - var myWidget = widget.cloneNode(true); - CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();}); + var range = getRange(true); + if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) { + pos = CodeMirror.Pos(pos.line - 1, 0); + range = getRange(false); + } + if (!range || range.cleared || force === "unfold") return; + + var myWidget = makeWidget(options); + CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); }); var myRange = cm.markText(range.from, range.to, { replacedWith: myWidget, clearOnEnter: true, __isFold: true }); + myRange.on("clear", function(from, to) { + CodeMirror.signal(cm, "unfold", cm, from, to); + }); + CodeMirror.signal(cm, "fold", cm, range.from, range.to); + } + + function makeWidget(options) { + var widget = (options && options.widget) || "\u2194"; + if (typeof widget == "string") { + var text = document.createTextNode(widget); + widget = document.createElement("span"); + widget.appendChild(text); + widget.className = "CodeMirror-foldmarker"; + } + return widget; + } + + // Clumsy backwards-compatible interface + CodeMirror.newFoldFunction = function(rangeFinder, widget) { + return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; }; -}; + + // New-style interface + CodeMirror.defineExtension("foldCode", function(pos, options, force) { + doFold(this, pos, options, force); + }); + + CodeMirror.commands.fold = function(cm) { + cm.foldCode(cm.getCursor()); + }; + + CodeMirror.registerHelper("fold", "combine", function() { + var funcs = Array.prototype.slice.call(arguments, 0); + return function(cm, start) { + for (var i = 0; i < funcs.length; ++i) { + var found = funcs[i](cm, start); + if (found) return found; + } + }; + }); + + CodeMirror.registerHelper("fold", "auto", function(cm, start) { + var helpers = cm.getHelpers(start, "fold"); + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, start); + if (cur) return cur; + } + }); +})(); diff --git a/gulliver/js/codemirror/addon/fold/foldgutter.css b/gulliver/js/codemirror/addon/fold/foldgutter.css new file mode 100644 index 000000000..49805393d --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/foldgutter.css @@ -0,0 +1,21 @@ +.CodeMirror-foldmarker { + color: blue; + text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; + font-family: arial; + line-height: .3; + cursor: pointer; +} +.CodeMirror-foldgutter { + width: .7em; +} +.CodeMirror-foldgutter-open, +.CodeMirror-foldgutter-folded { + color: #555; + cursor: pointer; +} +.CodeMirror-foldgutter-open:after { + content: "\25BE"; +} +.CodeMirror-foldgutter-folded:after { + content: "\25B8"; +} diff --git a/gulliver/js/codemirror/addon/fold/foldgutter.js b/gulliver/js/codemirror/addon/fold/foldgutter.js new file mode 100644 index 000000000..a4f3bb318 --- /dev/null +++ b/gulliver/js/codemirror/addon/fold/foldgutter.js @@ -0,0 +1,124 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.clearGutter(cm.state.foldGutter.options.gutter); + cm.state.foldGutter = null; + cm.off("gutterClick", onGutterClick); + cm.off("change", onChange); + cm.off("viewportChange", onViewportChange); + cm.off("fold", onFold); + cm.off("unfold", onFold); + cm.off("swapDoc", updateInViewport); + } + if (val) { + cm.state.foldGutter = new State(parseOptions(val)); + updateInViewport(cm); + cm.on("gutterClick", onGutterClick); + cm.on("change", onChange); + cm.on("viewportChange", onViewportChange); + cm.on("fold", onFold); + cm.on("unfold", onFold); + cm.on("swapDoc", updateInViewport); + } + }); + + var Pos = CodeMirror.Pos; + + function State(options) { + this.options = options; + this.from = this.to = 0; + } + + function parseOptions(opts) { + if (opts === true) opts = {}; + if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter"; + if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open"; + if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded"; + return opts; + } + + function isFolded(cm, line) { + var marks = cm.findMarksAt(Pos(line)); + for (var i = 0; i < marks.length; ++i) + if (marks[i].__isFold && marks[i].find().from.line == line) return true; + } + + function marker(spec) { + if (typeof spec == "string") { + var elt = document.createElement("div"); + elt.className = spec; + return elt; + } else { + return spec.cloneNode(true); + } + } + + function updateFoldInfo(cm, from, to) { + var opts = cm.state.foldGutter.options, cur = from; + cm.eachLine(from, to, function(line) { + var mark = null; + if (isFolded(cm, cur)) { + mark = marker(opts.indicatorFolded); + } else { + var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; + var range = func && func(cm, pos); + if (range && range.from.line + 1 < range.to.line) + mark = marker(opts.indicatorOpen); + } + cm.setGutterMarker(line, opts.gutter, mark); + ++cur; + }); + } + + function updateInViewport(cm) { + var vp = cm.getViewport(), state = cm.state.foldGutter; + if (!state) return; + cm.operation(function() { + updateFoldInfo(cm, vp.from, vp.to); + }); + state.from = vp.from; state.to = vp.to; + } + + function onGutterClick(cm, line, gutter) { + var opts = cm.state.foldGutter.options; + if (gutter != opts.gutter) return; + cm.foldCode(Pos(line, 0), opts.rangeFinder); + } + + function onChange(cm) { + var state = cm.state.foldGutter, opts = cm.state.foldGutter.options; + state.from = state.to = 0; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600); + } + + function onViewportChange(cm) { + var state = cm.state.foldGutter, opts = cm.state.foldGutter.options; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { + var vp = cm.getViewport(); + if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { + updateInViewport(cm); + } else { + cm.operation(function() { + if (vp.from < state.from) { + updateFoldInfo(cm, vp.from, state.from); + state.from = vp.from; + } + if (vp.to > state.to) { + updateFoldInfo(cm, state.to, vp.to); + state.to = vp.to; + } + }); + } + }, opts.updateViewportTimeSpan || 400); + } + + function onFold(cm, from) { + var state = cm.state.foldGutter, line = from.line; + if (line >= state.from && line < state.to) + updateFoldInfo(cm, line, line + 1); + } +})(); diff --git a/gulliver/js/codemirror/addon/fold/indent-fold.js b/gulliver/js/codemirror/addon/fold/indent-fold.js index 94a0a1ffa..434c2bc5c 100644 --- a/gulliver/js/codemirror/addon/fold/indent-fold.js +++ b/gulliver/js/codemirror/addon/fold/indent-fold.js @@ -1,11 +1,30 @@ -CodeMirror.indentRangeFinder = function(cm, start) { +CodeMirror.registerHelper("fold", "indent", function(cm, start) { var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); - var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); - for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { + if (!/\S/.test(firstLine)) return; + var getIndent = function(line) { + return CodeMirror.countColumn(line, null, tabSize); + }; + var myIndent = getIndent(firstLine); + var lastLineInFold = null; + // Go through lines until we find a line that definitely doesn't belong in + // the block we're folding, or to the end. + for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { var curLine = cm.getLine(i); - if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent && - CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent) - return {from: CodeMirror.Pos(start.line, firstLine.length), - to: CodeMirror.Pos(i, curLine.length)}; + var curIndent = getIndent(curLine); + if (curIndent > myIndent) { + // Lines with a greater indent are considered part of the block. + lastLineInFold = i; + } else if (!/\S/.test(curLine)) { + // Empty lines might be breaks within the block we're trying to fold. + } else { + // A non-empty line at an indent equal to or less than ours marks the + // start of another block. + break; + } } -}; + if (lastLineInFold) return { + from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) + }; +}); +CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated diff --git a/gulliver/js/codemirror/addon/fold/xml-fold.js b/gulliver/js/codemirror/addon/fold/xml-fold.js index 79c524d48..db5aed704 100644 --- a/gulliver/js/codemirror/addon/fold/xml-fold.js +++ b/gulliver/js/codemirror/addon/fold/xml-fold.js @@ -1,64 +1,173 @@ -CodeMirror.tagRangeFinder = (function() { +(function() { + "use strict"; + + var Pos = CodeMirror.Pos; + function cmp(a, b) { return a.line - b.line || a.ch - b.ch; } + var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); - return function(cm, start) { - var line = start.line, ch = start.ch, lineText = cm.getLine(line); + function Iter(cm, line, ch, range) { + this.line = line; this.ch = ch; + this.cm = cm; this.text = cm.getLine(line); + this.min = range ? range.from : cm.firstLine(); + this.max = range ? range.to - 1 : cm.lastLine(); + } - function nextLine() { - if (line >= cm.lastLine()) return; - ch = 0; - lineText = cm.getLine(++line); - return true; - } - function toTagEnd() { - for (;;) { - var gt = lineText.indexOf(">", ch); - if (gt == -1) { if (nextLine()) continue; else return; } - var lastSlash = lineText.lastIndexOf("/", gt); - var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt)); - ch = gt + 1; - return selfClose ? "selfClose" : "regular"; - } - } - function toNextTag() { - for (;;) { - xmlTagStart.lastIndex = ch; - var found = xmlTagStart.exec(lineText); - if (!found) { if (nextLine()) continue; else return; } - ch = found.index + found[0].length; - return found; - } - } + function tagAt(iter, ch) { + var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch)); + return type && /\btag\b/.test(type); + } - var stack = [], startCh; + function nextLine(iter) { + if (iter.line >= iter.max) return; + iter.ch = 0; + iter.text = iter.cm.getLine(++iter.line); + return true; + } + function prevLine(iter) { + if (iter.line <= iter.min) return; + iter.text = iter.cm.getLine(--iter.line); + iter.ch = iter.text.length; + return true; + } + + function toTagEnd(iter) { for (;;) { - var openTag = toNextTag(), end; - if (!openTag || line != start.line || !(end = toTagEnd())) return; - if (!openTag[1] && end != "selfClose") { - stack.push(openTag[2]); - startCh = ch; - break; - } + var gt = iter.text.indexOf(">", iter.ch); + if (gt == -1) { if (nextLine(iter)) continue; else return; } + if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; } + var lastSlash = iter.text.lastIndexOf("/", gt); + var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt)); + iter.ch = gt + 1; + return selfClose ? "selfClose" : "regular"; } - + } + function toTagStart(iter) { for (;;) { - var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0); - if (!next || !(end = toTagEnd())) return; + var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1; + if (lt == -1) { if (prevLine(iter)) continue; else return; } + if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; } + xmlTagStart.lastIndex = lt; + iter.ch = lt; + var match = xmlTagStart.exec(iter.text); + if (match && match.index == lt) return match; + } + } + + function toNextTag(iter) { + for (;;) { + xmlTagStart.lastIndex = iter.ch; + var found = xmlTagStart.exec(iter.text); + if (!found) { if (nextLine(iter)) continue; else return; } + if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; } + iter.ch = found.index + found[0].length; + return found; + } + } + function toPrevTag(iter) { + for (;;) { + var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1; + if (gt == -1) { if (prevLine(iter)) continue; else return; } + if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; } + var lastSlash = iter.text.lastIndexOf("/", gt); + var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt)); + iter.ch = gt + 1; + return selfClose ? "selfClose" : "regular"; + } + } + + function findMatchingClose(iter, tag) { + var stack = []; + for (;;) { + var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0); + if (!next || !(end = toTagEnd(iter))) return; if (end == "selfClose") continue; if (next[1]) { // closing tag for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { stack.length = i; break; } - if (!stack.length) return { - from: CodeMirror.Pos(start.line, startCh), - to: CodeMirror.Pos(tagLine, tagCh) + if (i < 0 && (!tag || tag == next[2])) return { + tag: next[2], + from: Pos(startLine, startCh), + to: Pos(iter.line, iter.ch) }; } else { // opening tag stack.push(next[2]); } } + } + function findMatchingOpen(iter, tag) { + var stack = []; + for (;;) { + var prev = toPrevTag(iter); + if (!prev) return; + if (prev == "selfClose") { toTagStart(iter); continue; } + var endLine = iter.line, endCh = iter.ch; + var start = toTagStart(iter); + if (!start) return; + if (start[1]) { // closing tag + stack.push(start[2]); + } else { // opening tag + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) { + stack.length = i; + break; + } + if (i < 0 && (!tag || tag == start[2])) return { + tag: start[2], + from: Pos(iter.line, iter.ch), + to: Pos(endLine, endCh) + }; + } + } + } + + CodeMirror.registerHelper("fold", "xml", function(cm, start) { + var iter = new Iter(cm, start.line, 0); + for (;;) { + var openTag = toNextTag(iter), end; + if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return; + if (!openTag[1] && end != "selfClose") { + var start = Pos(iter.line, iter.ch); + var close = findMatchingClose(iter, openTag[2]); + return close && {from: start, to: close.from}; + } + } + }); + CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated + + CodeMirror.findMatchingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); + if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return; + var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); + var start = end && toTagStart(iter); + if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; + var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; + + if (start[1]) { // closing tag + return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"}; + } else { // opening tag + iter = new Iter(cm, to.line, to.ch, range); + return {open: here, close: findMatchingClose(iter, start[2]), at: "open"}; + } + }; + + CodeMirror.findEnclosingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); + for (;;) { + var open = findMatchingOpen(iter); + if (!open) break; + var forward = new Iter(cm, pos.line, pos.ch, range); + var close = findMatchingClose(forward, open.tag); + if (close) return {open: open, close: close}; + } + }; + + // Used by addon/edit/closetag.js + CodeMirror.scanForClosingTag = function(cm, pos, name, end) { + var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); + return !!findMatchingClose(iter, name); }; })(); diff --git a/gulliver/js/codemirror/addon/hint/anyword-hint.js b/gulliver/js/codemirror/addon/hint/anyword-hint.js new file mode 100644 index 000000000..a144768c8 --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/anyword-hint.js @@ -0,0 +1,32 @@ +(function() { + "use strict"; + + var WORD = /[\w$]+/, RANGE = 500; + + CodeMirror.registerHelper("hint", "anyword", function(editor, options) { + var word = options && options.word || WORD; + var range = options && options.range || RANGE; + var cur = editor.getCursor(), curLine = editor.getLine(cur.line); + var start = cur.ch, end = start; + while (end < curLine.length && word.test(curLine.charAt(end))) ++end; + while (start && word.test(curLine.charAt(start - 1))) --start; + var curWord = start != end && curLine.slice(start, end); + + var list = [], seen = {}; + var re = new RegExp(word.source, "g"); + for (var dir = -1; dir <= 1; dir += 2) { + var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; + for (; line != end; line += dir) { + var text = editor.getLine(line), m; + while (m = re.exec(text)) { + if (line == cur.line && m[0] === curWord) continue; + if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { + seen[m[0]] = true; + list.push(m[0]); + } + } + } + } + return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; + }); +})(); diff --git a/gulliver/js/codemirror/addon/hint/css-hint.js b/gulliver/js/codemirror/addon/hint/css-hint.js new file mode 100644 index 000000000..6789c458b --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/css-hint.js @@ -0,0 +1,46 @@ +(function () { + "use strict"; + + var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, + "first-letter": 1, "first-line": 1, "first-child": 1, + before: 1, after: 1, lang: 1}; + + CodeMirror.registerHelper("hint", "css", function(cm) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var inner = CodeMirror.innerMode(cm.getMode(), token.state); + if (inner.mode.name != "css") return; + + var word = token.string, start = token.start, end = token.end; + if (/[^\w$_-]/.test(word)) { + word = ""; start = end = cur.ch; + } + + var spec = CodeMirror.resolveMode("text/css"); + + var result = []; + function add(keywords) { + for (var name in keywords) + if (!word || name.lastIndexOf(word, 0) == 0) + result.push(name); + } + + var st = token.state.state; + if (st == "pseudo" || token.type == "variable-3") { + add(pseudoClasses); + } else if (st == "block" || st == "maybeprop") { + add(spec.propertyKeywords); + } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { + add(spec.valueKeywords); + add(spec.colorKeywords); + } else if (st == "media" || st == "media_parens") { + add(spec.mediaTypes); + add(spec.mediaFeatures); + } + + if (result.length) return { + list: result, + from: CodeMirror.Pos(cur.line, start), + to: CodeMirror.Pos(cur.line, end) + }; + }); +})(); diff --git a/gulliver/js/codemirror/addon/hint/html-hint.js b/gulliver/js/codemirror/addon/hint/html-hint.js index 8b5dc6f00..cf256851e 100755 --- a/gulliver/js/codemirror/addon/hint/html-hint.js +++ b/gulliver/js/codemirror/addon/hint/html-hint.js @@ -1,582 +1,337 @@ (function () { - function htmlHint(editor, htmlStructure, getToken) { - var cur = editor.getCursor(); - var token = getToken(editor, cur); - var keywords = []; - var i = 0; - var j = 0; - var k = 0; - var from = {line: cur.line, ch: cur.ch}; - var to = {line: cur.line, ch: cur.ch}; - var flagClean = true; + var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" "); + var targets = ["_blank", "_self", "_top", "_parent"]; + var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"]; + var methods = ["get", "post", "put", "delete"]; + var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]; + var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech", + "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait", + "orientation:landscape", "device-height: [X]", "device-width: [X]"]; + var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags - var text = editor.getRange({line: 0, ch: 0}, cur); - - var open = text.lastIndexOf('<'); - var close = text.lastIndexOf('>'); - var tokenString = token.string.replace("<",""); - - if(open > close) { - var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); - if(last == "<") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - from.ch = token.start + 1; - } else { - var counter = 0; - var found = function(token, type, position) { - counter++; - if(counter > 50) return; - if(token.type == type) { - return token; - } else { - position.ch = token.start; - var newToken = editor.getTokenAt(position); - return found(newToken, type, position); - } - }; - - var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); - var node = nodeToken.string.substring(1); - - if(token.type === null && token.string.trim() === "") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - } else if(token.type == "string") { - tokenString = tokenString.substring(1, tokenString.length - 1); - var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); - var attribute = attributeToken.string; - - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - if(htmlStructure[i].attr[j].key == attribute) { - for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { - keywords.push(htmlStructure[i].attr[j].values[k]); - } - } - } - - for(j = 0; j < globalAttributes.length; j++) { - if(globalAttributes[j].key == attribute) { - for(k = 0; k < globalAttributes[j].values.length; k++) { - keywords.push(globalAttributes[j].values[k]); - } - } - } - } - } - from.ch = token.start + 1; - } else if(token.type == "attribute") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - from.ch = token.start; - } else if(token.type == "tag") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - - from.ch = token.start + 1; - } + var data = { + a: { + attrs: { + href: null, ping: null, type: null, + media: media, + target: targets, + hreflang: langs } - } else { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push("<" + htmlStructure[i].tag); + }, + abbr: s, + acronym: s, + address: s, + applet: s, + area: { + attrs: { + alt: null, coords: null, href: null, target: null, ping: null, + media: media, hreflang: langs, type: null, + shape: ["default", "rect", "circle", "poly"] } + }, + article: s, + aside: s, + audio: { + attrs: { + src: null, mediagroup: null, + crossorigin: ["anonymous", "use-credentials"], + preload: ["none", "metadata", "auto"], + autoplay: ["", "autoplay"], + loop: ["", "loop"], + controls: ["", "controls"] + } + }, + b: s, + base: { attrs: { href: null, target: targets } }, + basefont: s, + bdi: s, + bdo: s, + big: s, + blockquote: { attrs: { cite: null } }, + body: s, + br: s, + button: { + attrs: { + form: null, formaction: null, name: null, value: null, + autofocus: ["", "autofocus"], + disabled: ["", "autofocus"], + formenctype: encs, + formmethod: methods, + formnovalidate: ["", "novalidate"], + formtarget: targets, + type: ["submit", "reset", "button"] + } + }, + canvas: { attrs: { width: null, height: null } }, + caption: s, + center: s, + cite: s, + code: s, + col: { attrs: { span: null } }, + colgroup: { attrs: { span: null } }, + command: { + attrs: { + type: ["command", "checkbox", "radio"], + label: null, icon: null, radiogroup: null, command: null, title: null, + disabled: ["", "disabled"], + checked: ["", "checked"] + } + }, + data: { attrs: { value: null } }, + datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } }, + datalist: { attrs: { data: null } }, + dd: s, + del: { attrs: { cite: null, datetime: null } }, + details: { attrs: { open: ["", "open"] } }, + dfn: s, + dir: s, + div: s, + dl: s, + dt: s, + em: s, + embed: { attrs: { src: null, type: null, width: null, height: null } }, + eventsource: { attrs: { src: null } }, + fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } }, + figcaption: s, + figure: s, + font: s, + footer: s, + form: { + attrs: { + action: null, name: null, + "accept-charset": charsets, + autocomplete: ["on", "off"], + enctype: encs, + method: methods, + novalidate: ["", "novalidate"], + target: targets + } + }, + frame: s, + frameset: s, + h1: s, h2: s, h3: s, h4: s, h5: s, h6: s, + head: { + attrs: {}, + children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"] + }, + header: s, + hgroup: s, + hr: s, + html: { + attrs: { manifest: null }, + children: ["head", "body"] + }, + i: s, + iframe: { + attrs: { + src: null, srcdoc: null, name: null, width: null, height: null, + sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"], + seamless: ["", "seamless"] + } + }, + img: { + attrs: { + alt: null, src: null, ismap: null, usemap: null, width: null, height: null, + crossorigin: ["anonymous", "use-credentials"] + } + }, + input: { + attrs: { + alt: null, dirname: null, form: null, formaction: null, + height: null, list: null, max: null, maxlength: null, min: null, + name: null, pattern: null, placeholder: null, size: null, src: null, + step: null, value: null, width: null, + accept: ["audio/*", "video/*", "image/*"], + autocomplete: ["on", "off"], + autofocus: ["", "autofocus"], + checked: ["", "checked"], + disabled: ["", "disabled"], + formenctype: encs, + formmethod: methods, + formnovalidate: ["", "novalidate"], + formtarget: targets, + multiple: ["", "multiple"], + readonly: ["", "readonly"], + required: ["", "required"], + type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month", + "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", + "file", "submit", "image", "reset", "button"] + } + }, + ins: { attrs: { cite: null, datetime: null } }, + kbd: s, + keygen: { + attrs: { + challenge: null, form: null, name: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + keytype: ["RSA"] + } + }, + label: { attrs: { "for": null, form: null } }, + legend: s, + li: { attrs: { value: null } }, + link: { + attrs: { + href: null, type: null, + hreflang: langs, + media: media, + sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"] + } + }, + map: { attrs: { name: null } }, + mark: s, + menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } }, + meta: { + attrs: { + content: null, + charset: charsets, + name: ["viewport", "application-name", "author", "description", "generator", "keywords"], + "http-equiv": ["content-language", "content-type", "default-style", "refresh"] + } + }, + meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } }, + nav: s, + noframes: s, + noscript: s, + object: { + attrs: { + data: null, type: null, name: null, usemap: null, form: null, width: null, height: null, + typemustmatch: ["", "typemustmatch"] + } + }, + ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } }, + optgroup: { attrs: { disabled: ["", "disabled"], label: null } }, + option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } }, + output: { attrs: { "for": null, form: null, name: null } }, + p: s, + param: { attrs: { name: null, value: null } }, + pre: s, + progress: { attrs: { value: null, max: null } }, + q: { attrs: { cite: null } }, + rp: s, + rt: s, + ruby: s, + s: s, + samp: s, + script: { + attrs: { + type: ["text/javascript"], + src: null, + async: ["", "async"], + defer: ["", "defer"], + charset: charsets + } + }, + section: s, + select: { + attrs: { + form: null, name: null, size: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + multiple: ["", "multiple"] + } + }, + small: s, + source: { attrs: { src: null, type: null, media: null } }, + span: s, + strike: s, + strong: s, + style: { + attrs: { + type: ["text/css"], + media: media, + scoped: null + } + }, + sub: s, + summary: s, + sup: s, + table: s, + tbody: s, + td: { attrs: { colspan: null, rowspan: null, headers: null } }, + textarea: { + attrs: { + dirname: null, form: null, maxlength: null, name: null, placeholder: null, + rows: null, cols: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + readonly: ["", "readonly"], + required: ["", "required"], + wrap: ["soft", "hard"] + } + }, + tfoot: s, + th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } }, + thead: s, + time: { attrs: { datetime: null } }, + title: s, + tr: s, + track: { + attrs: { + src: null, label: null, "default": null, + kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"], + srclang: langs + } + }, + tt: s, + u: s, + ul: s, + "var": s, + video: { + attrs: { + src: null, poster: null, width: null, height: null, + crossorigin: ["anonymous", "use-credentials"], + preload: ["auto", "metadata", "none"], + autoplay: ["", "autoplay"], + mediagroup: ["movie"], + muted: ["", "muted"], + controls: ["", "controls"] + } + }, + wbr: s + }; - tokenString = ("<" + tokenString).trim(); - from.ch = token.start; - } - - if(flagClean === true && tokenString.trim() === "") { - flagClean = false; - } - - if(flagClean) { - keywords = cleanResults(tokenString, keywords); - } - - return {list: keywords, from: from, to: to}; + var globalAttrs = { + accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], + "class": null, + contenteditable: ["true", "false"], + contextmenu: null, + dir: ["ltr", "rtl", "auto"], + draggable: ["true", "false", "auto"], + dropzone: ["copy", "move", "link", "string:", "file:"], + hidden: ["hidden"], + id: null, + inert: ["inert"], + itemid: null, + itemprop: null, + itemref: null, + itemscope: ["itemscope"], + itemtype: null, + lang: ["en", "es"], + spellcheck: ["true", "false"], + style: null, + tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"], + title: null, + translate: ["yes", "no"], + onclick: null, + rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"] + }; + function populate(obj) { + for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr)) + obj.attrs[attr] = globalAttrs[attr]; } + populate(s); + for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s) + populate(data[tag]); - var cleanResults = function(text, keywords) { - var results = []; - var i = 0; - - for(i = 0; i < keywords.length; i++) { - if(keywords[i].substring(0, text.length) == text) { - results.push(keywords[i]); - } - } - - return results; - }; - - var htmlStructure = [ - {tag: '!DOCTYPE', attr: []}, - {tag: 'a', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]}, - {key: 'ping', values: [""]}, - {key: 'media', values: ["#"]}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'type', values: []} - ]}, - {tag: 'abbr', attr: []}, - {tag: 'acronym', attr: []}, - {tag: 'address', attr: []}, - {tag: 'applet', attr: []}, - {tag: 'area', attr: [ - {key: 'alt', values: [""]}, - {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, - {key: 'shape', values: ["default","rect","circle","poly"]}, - {key: 'href', values: ["#"]}, - {key: 'target', values: ["#"]}, - {key: 'ping', values: []}, - {key: 'media', values: []}, - {key: 'hreflang', values: []}, - {key: 'type', values: []} - - ]}, - {tag: 'article', attr: []}, - {tag: 'aside', attr: []}, - {tag: 'audio', attr: [ - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'preload', values: ["none","metadata","auto"]}, - {key: 'autoplay', values: ["","autoplay"]}, - {key: 'mediagroup', values: []}, - {key: 'loop', values: ["","loop"]}, - {key: 'controls', values: ["","controls"]} - ]}, - {tag: 'b', attr: []}, - {tag: 'base', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'basefont', attr: []}, - {tag: 'bdi', attr: []}, - {tag: 'bdo', attr: []}, - {tag: 'big', attr: []}, - {tag: 'blockquote', attr: [ - {key: 'cite', values: ["http://"]} - ]}, - {tag: 'body', attr: []}, - {tag: 'br', attr: []}, - {tag: 'button', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'name', values: []}, - {key: 'type', values: ["submit","reset","button"]}, - {key: 'value', values: []} - ]}, - {tag: 'canvas', attr: [ - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'caption', attr: []}, - {tag: 'center', attr: []}, - {tag: 'cite', attr: []}, - {tag: 'code', attr: []}, - {tag: 'col', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'colgroup', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'command', attr: [ - {key: 'type', values: ["command","checkbox","radio"]}, - {key: 'label', values: []}, - {key: 'icon', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'radiogroup', values: []}, - {key: 'command', values: []}, - {key: 'title', values: []} - ]}, - {tag: 'data', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'datagrid', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'multiple', values: ["","multiple"]} - ]}, - {tag: 'datalist', attr: [ - {key: 'data', values: []} - ]}, - {tag: 'dd', attr: []}, - {tag: 'del', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'details', attr: [ - {key: 'open', values: ["","open"]} - ]}, - {tag: 'dfn', attr: []}, - {tag: 'dir', attr: []}, - {tag: 'div', attr: [ - {key: 'id', values: []}, - {key: 'class', values: []}, - {key: 'style', values: []} - ]}, - {tag: 'dl', attr: []}, - {tag: 'dt', attr: []}, - {tag: 'em', attr: []}, - {tag: 'embed', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'eventsource', attr: [ - {key: 'src', values: []} - ]}, - {tag: 'fieldset', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'figcaption', attr: []}, - {tag: 'figure', attr: []}, - {tag: 'font', attr: []}, - {tag: 'footer', attr: []}, - {tag: 'form', attr: [ - {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, - {key: 'action', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'method', values: ["get","post","put","delete","dialog"]}, - {key: 'name', values: []}, - {key: 'novalidate', values: ["","novalidate"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'frame', attr: []}, - {tag: 'frameset', attr: []}, - {tag: 'h1', attr: []}, - {tag: 'h2', attr: []}, - {tag: 'h3', attr: []}, - {tag: 'h4', attr: []}, - {tag: 'h5', attr: []}, - {tag: 'h6', attr: []}, - {tag: 'head', attr: []}, - {tag: 'header', attr: []}, - {tag: 'hgroup', attr: []}, - {tag: 'hr', attr: []}, - {tag: 'html', attr: [ - {key: 'manifest', values: []} - ]}, - {tag: 'i', attr: []}, - {tag: 'iframe', attr: [ - {key: 'src', values: []}, - {key: 'srcdoc', values: []}, - {key: 'name', values: []}, - {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, - {key: 'seamless', values: ["","seamless"]}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'img', attr: [ - {key: 'alt', values: []}, - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'ismap', values: []}, - {key: 'usemap', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'input', attr: [ - {key: 'accept', values: ["audio/*","video/*","image/*"]}, - {key: 'alt', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'height', values: []}, - {key: 'list', values: []}, - {key: 'max', values: []}, - {key: 'maxlength', values: []}, - {key: 'min', values: []}, - {key: 'multiple', values: ["","multiple"]}, - {key: 'name', values: []}, - {key: 'pattern', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'size', values: []}, - {key: 'src', values: []}, - {key: 'step', values: []}, - {key: 'type', values: [ - "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", - "number","range","color","checkbox","radio","file","submit","image","reset","button" - ]}, - {key: 'value', values: []}, - {key: 'width', values: []} - ]}, - {tag: 'ins', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'kbd', attr: []}, - {tag: 'keygen', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'challenge', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'keytype', values: ["RSA"]}, - {key: 'name', values: []} - ]}, - {tag: 'label', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []} - ]}, - {tag: 'legend', attr: []}, - {tag: 'li', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'link', attr: [ - {key: 'href', values: []}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'media', values: [ - "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", - "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", - "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", - "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" - ]}, - {key: 'type', values: []}, - {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} - ]}, - {tag: 'map', attr: [ - {key: 'name', values: []} - ]}, - {tag: 'mark', attr: []}, - {tag: 'menu', attr: [ - {key: 'type', values: ["list","context","toolbar"]}, - {key: 'label', values: []} - ]}, - {tag: 'meta', attr: [ - {key: 'charset', attr: ["utf-8"]}, - {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, - {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, - {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} - ]}, - {tag: 'meter', attr: [ - {key: 'value', values: []}, - {key: 'min', values: []}, - {key: 'low', values: []}, - {key: 'high', values: []}, - {key: 'max', values: []}, - {key: 'optimum', values: []} - ]}, - {tag: 'nav', attr: []}, - {tag: 'noframes', attr: []}, - {tag: 'noscript', attr: []}, - {tag: 'object', attr: [ - {key: 'data', values: []}, - {key: 'type', values: []}, - {key: 'typemustmatch', values: ["","typemustmatch"]}, - {key: 'name', values: []}, - {key: 'usemap', values: []}, - {key: 'form', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'ol', attr: [ - {key: 'reversed', values: ["", "reversed"]}, - {key: 'start', values: []}, - {key: 'type', values: ["1","a","A","i","I"]} - ]}, - {tag: 'optgroup', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'label', values: []} - ]}, - {tag: 'option', attr: [ - {key: 'disabled', values: ["", "disabled"]}, - {key: 'label', values: []}, - {key: 'selected', values: ["", "selected"]}, - {key: 'value', values: []} - ]}, - {tag: 'output', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'p', attr: []}, - {tag: 'param', attr: [ - {key: 'name', values: []}, - {key: 'value', values: []} - ]}, - {tag: 'pre', attr: []}, - {tag: 'progress', attr: [ - {key: 'value', values: []}, - {key: 'max', values: []} - ]}, - {tag: 'q', attr: [ - {key: 'cite', values: []} - ]}, - {tag: 'rp', attr: []}, - {tag: 'rt', attr: []}, - {tag: 'ruby', attr: []}, - {tag: 's', attr: []}, - {tag: 'samp', attr: []}, - {tag: 'script', attr: [ - {key: 'type', values: ["text/javascript"]}, - {key: 'src', values: []}, - {key: 'async', values: ["","async"]}, - {key: 'defer', values: ["","defer"]}, - {key: 'charset', values: ["utf-8"]} - ]}, - {tag: 'section', attr: []}, - {tag: 'select', attr: [ - {key: 'autofocus', values: ["", "autofocus"]}, - {key: 'disabled', values: ["", "disabled"]}, - {key: 'form', values: []}, - {key: 'multiple', values: ["", "multiple"]}, - {key: 'name', values: []}, - {key: 'size', values: []} - ]}, - {tag: 'small', attr: []}, - {tag: 'source', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'media', values: []} - ]}, - {tag: 'span', attr: []}, - {tag: 'strike', attr: []}, - {tag: 'strong', attr: []}, - {tag: 'style', attr: [ - {key: 'type', values: ["text/css"]}, - {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, - {key: 'scoped', values: []} - ]}, - {tag: 'sub', attr: []}, - {tag: 'summary', attr: []}, - {tag: 'sup', attr: []}, - {tag: 'table', attr: [ - {key: 'border', values: []} - ]}, - {tag: 'tbody', attr: []}, - {tag: 'td', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []} - ]}, - {tag: 'textarea', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'maxlength', values: []}, - {key: 'name', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'rows', values: []}, - {key: 'cols', values: []}, - {key: 'wrap', values: ["soft","hard"]} - ]}, - {tag: 'tfoot', attr: []}, - {tag: 'th', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []}, - {key: 'scope', values: ["row","col","rowgroup","colgroup"]} - ]}, - {tag: 'thead', attr: []}, - {tag: 'time', attr: [ - {key: 'datetime', values: []} - ]}, - {tag: 'title', attr: []}, - {tag: 'tr', attr: []}, - {tag: 'track', attr: [ - {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, - {key: 'src', values: []}, - {key: 'srclang', values: ["en","es"]}, - {key: 'label', values: []}, - {key: 'default', values: []} - ]}, - {tag: 'tt', attr: []}, - {tag: 'u', attr: []}, - {tag: 'ul', attr: []}, - {tag: 'var', attr: []}, - {tag: 'video', attr: [ - {key: "src", values: []}, - {key: "crossorigin", values: ["anonymous","use-credentials"]}, - {key: "poster", values: []}, - {key: "preload", values: ["auto","metadata","none"]}, - {key: "autoplay", values: ["","autoplay"]}, - {key: "mediagroup", values: ["movie"]}, - {key: "loop", values: ["","loop"]}, - {key: "muted", values: ["","muted"]}, - {key: "controls", values: ["","controls"]}, - {key: "width", values: []}, - {key: "height", values: []} - ]}, - {tag: 'wbr', attr: []} - ]; - - var globalAttributes = [ - {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, - {key: "class", values: []}, - {key: "contenteditable", values: ["true", "false"]}, - {key: "contextmenu", values: []}, - {key: "dir", values: ["ltr","rtl","auto"]}, - {key: "draggable", values: ["true","false","auto"]}, - {key: "dropzone", values: ["copy","move","link","string:","file:"]}, - {key: "hidden", values: ["hidden"]}, - {key: "id", values: []}, - {key: "inert", values: ["inert"]}, - {key: "itemid", values: []}, - {key: "itemprop", values: []}, - {key: "itemref", values: []}, - {key: "itemscope", values: ["itemscope"]}, - {key: "itemtype", values: []}, - {key: "lang", values: ["en","es"]}, - {key: "spellcheck", values: ["true","false"]}, - {key: "style", values: []}, - {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, - {key: "title", values: []}, - {key: "translate", values: ["yes","no"]}, - {key: "onclick", values: []}, - {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} - ]; - - CodeMirror.htmlHint = function(editor) { - if(String.prototype.trim == undefined) { - String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; - } - return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); - }; + CodeMirror.htmlSchema = data; + function htmlHint(cm, options) { + var local = {schemaInfo: data}; + if (options) for (var opt in options) local[opt] = options[opt]; + return CodeMirror.hint.xml(cm, local); + } + CodeMirror.htmlHint = htmlHint; // deprecated + CodeMirror.registerHelper("hint", "html", htmlHint); })(); diff --git a/gulliver/js/codemirror/addon/hint/javascript-hint.js b/gulliver/js/codemirror/addon/hint/javascript-hint.js index fe2240af1..c347c206e 100644 --- a/gulliver/js/codemirror/addon/hint/javascript-hint.js +++ b/gulliver/js/codemirror/addon/hint/javascript-hint.js @@ -4,7 +4,7 @@ function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; @@ -21,8 +21,11 @@ function scriptHint(editor, keywords, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + if (/\b(?:string|comment)\b/.test(token.type)) return; + token.state = CodeMirror.innerMode(editor.getMode(), token.state).state; + // If it's not a 'word-style' token, ignore the token. - if (!/^[\w$_]*$/.test(token.string)) { + if (!/^[\w$_]*$/.test(token.string)) { token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, type: token.string == "." ? "property" : null}; } @@ -31,21 +34,6 @@ tprop = getToken(editor, Pos(cur.line, tprop.start)); if (tprop.string != ".") return; tprop = getToken(editor, Pos(cur.line, tprop.start)); - if (tprop.string == ')') { - var level = 1; - do { - tprop = getToken(editor, Pos(cur.line, tprop.start)); - switch (tprop.string) { - case ')': level++; break; - case '(': level--; break; - default: break; - } - } while (level > 0); - tprop = getToken(editor, Pos(cur.line, tprop.start)); - if (tprop.type.indexOf("variable") === 0) - tprop.type = "function"; - else return; // no clue - } if (!context) var context = []; context.push(tprop); } @@ -54,11 +42,13 @@ to: Pos(cur.line, token.end)}; } - CodeMirror.javascriptHint = function(editor, options) { + function javascriptHint(editor, options) { return scriptHint(editor, javascriptKeywords, function (e, cur) {return e.getTokenAt(cur);}, options); }; + CodeMirror.javascriptHint = javascriptHint; // deprecated + CodeMirror.registerHelper("hint", "javascript", javascriptHint); function getCoffeeScriptToken(editor, cur) { // This getToken, it is for coffeescript, imitates the behavior of @@ -78,9 +68,11 @@ return token; } - CodeMirror.coffeescriptHint = function(editor, options) { + function coffeescriptHint(editor, options) { return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); - }; + } + CodeMirror.coffeescriptHint = coffeescriptHint; // deprecated + CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint); var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + "toUpperCase toLowerCase split concat match replace search").split(" "); @@ -95,7 +87,7 @@ function getCompletions(token, context, keywords, options) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { if (typeof obj == "string") forEach(stringProps, maybeAdd); @@ -104,11 +96,11 @@ for (var name in obj) maybeAdd(name); } - if (context) { + if (context && context.length) { // If this is a property, see if it belongs to some object we can // find in the current environment. var obj = context.pop(), base; - if (obj.type.indexOf("variable") === 0) { + if (obj.type && obj.type.indexOf("variable") === 0) { if (options && options.additionalContext) base = options.additionalContext[obj.string]; base = base || window[obj.string]; @@ -126,8 +118,7 @@ while (base != null && context.length) base = base[context.pop().string]; if (base != null) gatherCompletions(base); - } - else { + } else { // If not, just look in the window object and any local scope // (reading into JS mode internals to get at the local and global variables) for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); diff --git a/gulliver/js/codemirror/addon/hint/php-hint.js b/gulliver/js/codemirror/addon/hint/php-hint.js deleted file mode 100644 index 9723cb250..000000000 --- a/gulliver/js/codemirror/addon/hint/php-hint.js +++ /dev/null @@ -1,267 +0,0 @@ -(function () { - var Pos = CodeMirror.Pos; - - function forEach(arr, f) { - for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); - } - - function arrayContains(arr, item) { - if (!Array.prototype.indexOf) { - var i = arr.length; - while (i--) { - if (arr[i] === item) { - return true; - } - } - return false; - } - return arr.indexOf(item) != -1; - } - - function scriptHint(editor, keywords, getToken, options) { - // Find the token at the cursor - var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; - var sToken = token.string.trim(); - - if ( sToken == "(") { - token = tprop = getToken(editor, Pos(cur.line, tprop.start)); - return {list: getCompletions(token.string, keywords, options), - from: Pos(cur.line, token.start), - to: Pos(cur.line, token.end + 1)}; - } - if ( sToken == "=") { - return {list: getCompletions(token.string, keywords, options), - from: Pos(cur.line, token.start + 1), - to: Pos(cur.line, token.end)}; - } - return {list: getCompletions(token.string, keywords, options), - from: Pos(cur.line, token.start), - to: Pos(cur.line, token.end)}; - } - - CodeMirror.phpHint = function(editor, options) { - return scriptHint(editor, phpPMFunctions, function (e, cur) {return e.getTokenAt(cur);}, options); - }; - - var SPACE = " "; - var arrayFunctions = []; - - var formatDate = "formatDate"; - var formatDateFunction = [formatDate+"($date,$format,$language);",formatDate+"($date,$format);"]; - arrayFunctions[formatDate] = formatDateFunction; - - var getCurrentDate = "getCurrentDate"; - var getCurrentDateFunction = [getCurrentDate+"()"]; - arrayFunctions[getCurrentDate] = getCurrentDateFunction; - - var getCurrentTime = "getCurrentTime"; - var getCurrentTimeFunction = [getCurrentTime+"()"]; - arrayFunctions[getCurrentTime] = getCurrentTimeFunction; - - var literalDate = "literalDate"; - var literalDateFunction = [literalDate+"($date,$Language)",literalDate+"($date)"]; - arrayFunctions[literalDate] = literalDateFunction; - - var capitalize = "capitalize"; - var capitalizeFunction = [capitalize+"($textToConvert)"]; - arrayFunctions[capitalize] = capitalizeFunction; - - var lowerCase = "lowerCase"; - var lowerCaseFunction = [lowerCase+"($textToConvert)"]; - arrayFunctions[lowerCase] = lowerCaseFunction; - - var upperCase = "upperCase"; - var upperCaseFunction = [upperCase+"($textToConvert)"]; - arrayFunctions[upperCase] = upperCaseFunction; - - var userInfo = "userInfo"; - var userInfoFunction = [userInfo+"($USER_ID)"]; - arrayFunctions[userInfo] = userInfoFunction; - - var executeQuery = "executeQuery"; - var executeQueryFunction = [executeQuery+"($sqlStatement,$DBConnectionUID)",executeQuery+"($sqlStatement)"]; - arrayFunctions[executeQuery] = executeQueryFunction; - - var orderGrid = "orderGrid"; - var orderGridFunction = ("orderGrid($gridName,$field,$criteria) orderGrid($gridName,$field)").split(SPACE); - arrayFunctions[orderGrid] = orderGridFunction; - - var evaluateFunction = "evaluateFunction"; - var evaluateFunctionFunction = [evaluateFunction+"($gridName,$Expression)"]; - arrayFunctions[evaluateFunction] = evaluateFunctionFunction; - - var PMFTaskCase = "PMFTaskCase"; - var PMFTaskCaseFunction = [PMFTaskCase+"($caseId)"]; - arrayFunctions[PMFTaskCase] = PMFTaskCaseFunction; - - var PMFTaskList = "PMFTaskList"; - var PMFTaskListFunction = [PMFTaskList+"($userId)"]; - arrayFunctions[PMFTaskList] = PMFTaskListFunction; - - var PMFUserList = "PMFUserList"; - var PMFUserListFunction = [PMFUserList+"()"]; - arrayFunctions[PMFUserList] = PMFUserListFunction; - - var PMFGroupList = "PMFGroupList"; - var PMFGroupListFunction = [PMFGroupList+"()"]; - arrayFunctions[PMFGroupList] = PMFGroupListFunction; - - var PMFRoleList = "PMFRoleList"; - var PMFRoleListFunction = [PMFRoleList+"()"]; - arrayFunctions[PMFRoleList] = PMFRoleListFunction; - - var PMFCaseList = "PMFCaseList"; - var PMFCaseListFunction = [PMFCaseList+"($userId)",PMFCaseList+"()"]; - arrayFunctions[PMFCaseList] = PMFCaseListFunction; - - var PMFProcessList = "PMFProcessList"; - var PMFProcessListFunction = [PMFProcessList+"()"]; - arrayFunctions[PMFProcessList] = PMFProcessListFunction; - - var PMFSendVariables = "PMFSendVariables"; - var PMFSendVariablesFunction = [PMFSendVariables+"($caseId,$variables)"]; - arrayFunctions[PMFSendVariables] = PMFSendVariablesFunction; - - var PMFDerivateCase = "PMFDerivateCase"; - var PMFDerivateCaseFunction = [PMFDerivateCase+"($caseId,$delegation,$executeTriggersBeforeAssigment)",PMFDerivateCase+"($caseId,$delegation)"]; - arrayFunctions[PMFDerivateCase] = PMFDerivateCaseFunction; - - var PMFNewCaseImpersonate = "PMFNewCaseImpersonate"; - var PMFNewCaseImpersonateFunction = [PMFNewCaseImpersonate+"($processId,$userId,$variables)"]; - arrayFunctions[PMFNewCaseImpersonate] = PMFNewCaseImpersonateFunction; - - var PMFNewCase = "PMFNewCase"; - var PMFNewCaseFunction = [PMFNewCase+"($processId,$userId,$taskId,$variables)"]; - arrayFunctions[PMFNewCase] = PMFNewCaseFunction; - - var PMFPauseCase = "PMFPauseCase"; - var PMFPauseCaseFunction = [PMFPauseCase+"($caseUid,$delIndex,$userUid,$unpauseDate)",PMFPauseCase+"($caseUid,$delIndex,$userUid)"]; - arrayFunctions[PMFPauseCase] = PMFPauseCaseFunction; - - var PMFAssignUserToGroup = "PMFAssignUserToGroup"; - var PMFAssignUserToGroupFunction = [PMFAssignUserToGroup+"($userId,$groupId)"]; - arrayFunctions[PMFAssignUserToGroup] = PMFAssignUserToGroupFunction; - - var PMFCreateUser = "PMFCreateUser"; - var PMFCreateUserFunction = [PMFCreateUser+"($userId,$password,$firstname,$lastname,$email,$role)"]; - arrayFunctions[PMFCreateUser] = PMFCreateUserFunction; - - var PMFUpdateUser = "PMFUpdateUser"; - var PMFUpdateUserFunction = [PMFUpdateUser+"($userUid,$userName,$firstName,$lastName,$email,$dueDate,$status,$role,$password)"]; - arrayFunctions[PMFUpdateUser] = PMFUpdateUserFunction; - - var PMFInformationUser = "PMFInformationUser"; - var PMFInformationUserFunction = [PMFInformationUser+"($userUid)"]; - arrayFunctions[PMFInformationUser] = PMFInformationUserFunction; - - var generateCode = "generateCode"; - var generateCodeFunction = [generateCode+"($size,$type)"]; - arrayFunctions[generateCode] = generateCodeFunction; - - var setCaseTrackerCode = "setCaseTrackerCode"; - var setCaseTrackerCodeFunction = [setCaseTrackerCode+"($caseId,$code,$pin)"]; - arrayFunctions[setCaseTrackerCode] = setCaseTrackerCodeFunction; - - var jumping = "jumping"; - var jumpingFunction = [jumping+"($caseId,$delegation)"]; - arrayFunctions[jumping] = jumpingFunction; - - var PMFRedirectToStep = "PMFRedirectToStep"; - var PMFRedirectToStepFunction = [PMFRedirectToStep+"($caseId,$delegation,$stepType,$stepId)"]; - arrayFunctions[PMFRedirectToStep] = PMFRedirectToStepFunction; - - var pauseCase = "pauseCase"; - var pauseCaseFunction = [pauseCase+"($caseId,$delegation,$userId,$unpauseDate)",pauseCase+"($caseId,$delegation,$userId)"]; - arrayFunctions[pauseCase] = pauseCaseFunction; - - var PMFUnpauseCase = "PMFUnpauseCase"; - var PMFUnpauseCaseFunction = [PMFUnpauseCase+"($caseId,$delegation,$userId,$unpauseDate)",PMFUnpauseCase+"($caseId,$delegation,$userId)"]; - arrayFunctions[PMFUnpauseCase] = PMFUnpauseCaseFunction; - - var PMFSendMessage = "PMFSendMessage"; - var PMFSendMessageFunction = [PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template,$fields,$attachments)",PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template,$fields)",PMFSendMessage+"($caseId,$from,$to,$cc,$bcc,$subject,$template)"]; - arrayFunctions[PMFSendMessage] = PMFSendMessageFunction; - - var PMFgetLabelOption = "PMFgetLabelOption"; - var PMFgetLabelOptionFunction = [PMFgetLabelOption+"($processId,$dynaformId,$fieldName,$optionId)"]; - arrayFunctions[PMFgetLabelOption] = PMFgetLabelOptionFunction; - - var PMFGenerateOutputDocument = "PMFGenerateOutputDocument"; - var PMFGenerateOutputDocumentFunction = [PMFGenerateOutputDocument+"($outputID)"]; - arrayFunctions[PMFGenerateOutputDocument] = PMFGenerateOutputDocumentFunction; - - var PMFGetUserEmailAddress = "PMFGetUserEmailAddress"; - var PMFGetUserEmailAddressFunction = [PMFGetUserEmailAddress+"($id,$APP_UID,$prefix)",PMFGetUserEmailAddress+"($id,$APP_UID)",PMFGetUserEmailAddress+"($id)"]; - arrayFunctions[PMFGetUserEmailAddress] = PMFGetUserEmailAddressFunction; - - var PMFGetNextAssignedUser = "PMFGetNextAssignedUser"; - var PMFGetNextAssignedUserFunction = (PMFGetNextAssignedUser+"($application,$task)").split(SPACE); - arrayFunctions[PMFGetNextAssignedUser] = PMFGetNextAssignedUserFunction; - - var PMFDeleteCase = "PMFDeleteCase"; - var PMFDeleteCaseFunction = ("PMFDeleteCase($caseId)").split(SPACE); - arrayFunctions[PMFDeleteCase] = PMFDeleteCaseFunction; - - var PMFCancelCase = "PMFCancelCase"; - var PMFCancelCaseFunction = [PMFCancelCase+"($caseUid,$delIndex,$userUid)"]; - arrayFunctions[PMFCancelCase] = PMFCancelCaseFunction; - - var PMFAddInputDocument = "PMFAddInputDocument"; - var PMFAddInputDocumentFunction = [PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid,$option,$file)",PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid,$option)",PMFAddInputDocument+"($inputDocumentUid,$appDocUid,$docVersion,$appDocType,$appDocComment,$inputDocumentAction,$caseUid,$delIndex,$taskUid,$userUid)"]; - arrayFunctions[PMFAddInputDocument] = PMFAddInputDocumentFunction; - - var PMFAddCaseNote = "PMFAddCaseNote"; - var PMFAddCaseNoteFunction = [PMFAddCaseNote+"($caseUid,$processUid,$taskUid,$userUid,$note,$sendMail)"]; - arrayFunctions[PMFAddCaseNote] = PMFAddCaseNoteFunction; - - var PMFGetCaseNotes = "PMFGetCaseNotes"; - var PMFGetCaseNotesFunction = [PMFGetCaseNotes+"($applicationID,$type,$userUid);",PMFGetCaseNotes+"($applicationID,$type)",PMFGetCaseNotes+"($applicationID)"]; - arrayFunctions[PMFGetCaseNotes] = PMFGetCaseNotesFunction; - - var phpPMFunctions = [formatDate,getCurrentDate,getCurrentTime,literalDate,capitalize,lowerCase,upperCase,userInfo,executeQuery,orderGrid, - evaluateFunction,PMFTaskCase,PMFTaskList,PMFUserList,PMFGroupList,PMFRoleList,PMFCaseList,PMFProcessList,PMFSendVariables,PMFDerivateCase, - PMFNewCaseImpersonate,PMFNewCase,PMFPauseCase,PMFUnpauseCase,PMFAssignUserToGroup,PMFCreateUser,PMFUpdateUser,PMFInformationUser, - generateCode,setCaseTrackerCode,jumping,PMFRedirectToStep,pauseCase,PMFSendMessage,PMFgetLabelOption,PMFGenerateOutputDocument, - PMFGetUserEmailAddress,PMFGetNextAssignedUser,PMFDeleteCase,PMFCancelCase,PMFAddInputDocument,PMFAddCaseNote,PMFGetCaseNotes]; - - var phpKeywords = ("break case catch continue default do else false for function " + - "if new return switch throw true try var while").split(SPACE); - - function getCompletions(functionName, keywords, options) { - - var found = []; - - function maybeAdd(str) {// for keywords ? - if ( str.indexOf(functionName) == 0 && !arrayContains(found, str)) { - found.push(str); - } - } - - function yesAdd(str) { - if ( !arrayContains(found, str)) { - found.push(str); - } - } - - arrayFunction = arrayFunctions[functionName]; - - if (arrayFunction != undefined) { - forEach( arrayFunction, yesAdd); - } else { - if (functionName.trim() == "") { - forEach (phpKeywords, yesAdd); - forEach (keywords, yesAdd); - } else if (functionName == "=") { - forEach (phpPMFunctions, yesAdd); - } else { - for (index = 0; index < phpKeywords.length; index++) { - if ( phpKeywords[index].indexOf(functionName) == 0 ) { - found.push(phpKeywords[index]); - } - } - forEach(keywords, maybeAdd); - } - } - return found; - } -})(); diff --git a/gulliver/js/codemirror/addon/hint/pig-hint.js b/gulliver/js/codemirror/addon/hint/pig-hint.js index 9847996be..6c0c52377 100644 --- a/gulliver/js/codemirror/addon/hint/pig-hint.js +++ b/gulliver/js/codemirror/addon/hint/pig-hint.js @@ -1,8 +1,10 @@ (function () { + "use strict"; + function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; @@ -25,11 +27,11 @@ token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, className: token.string == ":" ? "pig-type" : null}; } - + if (!context) var context = []; context.push(tprop); - - var completionList = getCompletions(token, context); + + var completionList = getCompletions(token, context); completionList = completionList.sort(); //prevent autocomplete for last word, instead show dropdown with one word if(completionList.length == 1) { @@ -40,24 +42,26 @@ from: CodeMirror.Pos(cur.line, token.start), to: CodeMirror.Pos(cur.line, token.end)}; } - - CodeMirror.pigHint = function(editor) { + + function pigHint(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; - + } + CodeMirror.pigHint = pigHint; // deprecated + CodeMirror.registerHelper("hint", "pig", pigHint); + var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " - + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + "NEQ MATCHES TRUE FALSE"; var pigKeywordsU = pigKeywords.split(" "); var pigKeywordsL = pigKeywords.toLowerCase().split(" "); - + var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP"; var pigTypesU = pigTypes.split(" "); var pigTypesL = pigTypes.toLowerCase().split(" "); - - var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + + var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " @@ -66,9 +70,9 @@ + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " - + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; - var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); - var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; + var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); + var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " @@ -76,13 +80,13 @@ + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize " + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax " + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" "); - + function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } - + function gatherCompletions(obj) { if(obj == ":") { forEach(pigTypesL, maybeAdd); @@ -103,11 +107,11 @@ // find in the current environment. var obj = context.pop(), base; - if (obj.type == "variable") + if (obj.type == "variable") base = obj.string; else if(obj.type == "variable-3") base = ":" + obj.string; - + while (base != null && context.length) base = base[context.pop().string]; if (base != null) gatherCompletions(base); diff --git a/gulliver/js/codemirror/addon/hint/python-hint.js b/gulliver/js/codemirror/addon/hint/python-hint.js index 60221b89e..248284e8c 100644 --- a/gulliver/js/codemirror/addon/hint/python-hint.js +++ b/gulliver/js/codemirror/addon/hint/python-hint.js @@ -31,19 +31,17 @@ var completionList = getCompletions(token, context); completionList = completionList.sort(); - //prevent autocomplete for last word, instead show dropdown with one word - if(completionList.length == 1) { - completionList.push(" "); - } return {list: completionList, from: CodeMirror.Pos(cur.line, token.start), to: CodeMirror.Pos(cur.line, token.end)}; } - CodeMirror.pythonHint = function(editor) { + function pythonHint(editor) { return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; + } + CodeMirror.pythonHint = pythonHint; // deprecated + CodeMirror.registerHelper("hint", "python", pythonHint); var pythonKeywords = "and del from not while as elif global or with assert else if pass yield" + "break except import print class exec in raise continue finally is return def for lambda try"; @@ -64,7 +62,7 @@ function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { - if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); + if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(_obj) { diff --git a/gulliver/js/codemirror/addon/hint/show-hint.css b/gulliver/js/codemirror/addon/hint/show-hint.css index 47fb7921f..8a4ff052e 100644 --- a/gulliver/js/codemirror/addon/hint/show-hint.css +++ b/gulliver/js/codemirror/addon/hint/show-hint.css @@ -25,7 +25,7 @@ margin: 0; padding: 0 4px; border-radius: 2px; - max-width: 150em; + max-width: 19em; overflow: hidden; white-space: pre; color: black; diff --git a/gulliver/js/codemirror/addon/hint/show-hint.js b/gulliver/js/codemirror/addon/hint/show-hint.js index 42e8caac7..7e03d1139 100644 --- a/gulliver/js/codemirror/addon/hint/show-hint.js +++ b/gulliver/js/codemirror/addon/hint/show-hint.js @@ -1,80 +1,199 @@ -CodeMirror.showHint = function(cm, getHints, options) { - if (!options) options = {}; - var startCh = cm.getCursor().ch, continued = false; - var closeOn = options.closeCharacters || /[\s()\[\]{};:]/; +(function() { + "use strict"; - function startHinting() { + var HINT_ELEMENT_CLASS = "CodeMirror-hint"; + var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; + + CodeMirror.showHint = function(cm, getHints, options) { // We want a single cursor position. if (cm.somethingSelected()) return; + if (getHints == null) { + if (options && options.async) return; + else getHints = CodeMirror.hint.auto; + } - if (options.async) - getHints(cm, showHints, options); + if (cm.state.completionActive) cm.state.completionActive.close(); + + var completion = cm.state.completionActive = new Completion(cm, getHints, options || {}); + CodeMirror.signal(cm, "startCompletion", cm); + if (completion.options.async) + getHints(cm, function(hints) { completion.showHints(hints); }, completion.options); else - return showHints(getHints(cm, options)); + return completion.showHints(getHints(cm, completion.options)); + }; + + function Completion(cm, getHints, options) { + this.cm = cm; + this.getHints = getHints; + this.options = options; + this.widget = this.onClose = null; } + Completion.prototype = { + close: function() { + if (!this.active()) return; + this.cm.state.completionActive = null; + + if (this.widget) this.widget.close(); + if (this.onClose) this.onClose(); + CodeMirror.signal(this.cm, "endCompletion", this.cm); + }, + + active: function() { + return this.cm.state.completionActive == this; + }, + + pick: function(data, i) { + var completion = data.list[i]; + if (completion.hint) completion.hint(this.cm, data, completion); + else this.cm.replaceRange(getText(completion), data.from, data.to); + CodeMirror.signal(data, "pick", completion); + this.close(); + }, + + showHints: function(data) { + if (!data || !data.list.length || !this.active()) return this.close(); + + if (this.options.completeSingle != false && data.list.length == 1) + this.pick(data, 0); + else + this.showWidget(data); + }, + + showWidget: function(data) { + this.widget = new Widget(this, data); + CodeMirror.signal(data, "shown"); + + var debounce = 0, completion = this, finished; + var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/; + var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length; + + var requestAnimationFrame = window.requestAnimationFrame || function(fn) { + return setTimeout(fn, 1000/60); + }; + var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; + + function done() { + if (finished) return; + finished = true; + completion.close(); + completion.cm.off("cursorActivity", activity); + if (data) CodeMirror.signal(data, "close"); + } + + function update() { + if (finished) return; + CodeMirror.signal(data, "update"); + if (completion.options.async) + completion.getHints(completion.cm, finishUpdate, completion.options); + else + finishUpdate(completion.getHints(completion.cm, completion.options)); + } + function finishUpdate(data_) { + data = data_; + if (finished) return; + if (!data || !data.list.length) return done(); + completion.widget = new Widget(completion, data); + } + + function clearDebounce() { + if (debounce) { + cancelAnimationFrame(debounce); + debounce = 0; + } + } + + function activity() { + clearDebounce(); + var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line); + if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch || + pos.ch < startPos.ch || completion.cm.somethingSelected() || + (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) { + completion.close(); + } else { + debounce = requestAnimationFrame(update); + if (completion.widget) completion.widget.close(); + } + } + this.cm.on("cursorActivity", activity); + this.onClose = done; + } + }; + function getText(completion) { if (typeof completion == "string") return completion; else return completion.text; } - function pickCompletion(cm, data, completion) { - if (completion.hint) completion.hint(cm, data, completion); - else cm.replaceRange(getText(completion), data.from, data.to); + function buildKeyMap(options, handle) { + var baseMap = { + Up: function() {handle.moveFocus(-1);}, + Down: function() {handle.moveFocus(1);}, + PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, + PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, + Home: function() {handle.setFocus(0);}, + End: function() {handle.setFocus(handle.length - 1);}, + Enter: handle.pick, + Tab: handle.pick, + Esc: handle.close + }; + var ourMap = options.customKeys ? {} : baseMap; + function addBinding(key, val) { + var bound; + if (typeof val != "string") + bound = function(cm) { return val(cm, handle); }; + // This mechanism is deprecated + else if (baseMap.hasOwnProperty(val)) + bound = baseMap[val]; + else + bound = val; + ourMap[key] = bound; + } + if (options.customKeys) + for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) + addBinding(key, options.customKeys[key]); + if (options.extraKeys) + for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key)) + addBinding(key, options.extraKeys[key]); + return ourMap; } - function getMaxAccordingList(list) { - var max = 0; - for (var i = 0; i < list.length; i++) { - if (list[i].length > max) - max = list[i].length; + function getHintElement(hintsElement, el) { + while (el && el != hintsElement) { + if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; + el = el.parentNode; } - return max; } - function showHints(data) { - if (!data || !data.list.length) return; - var completions = data.list; - var maxWidth = getMaxAccordingList(data.list); - // When there is only one completion, use it directly. - if (!continued && options.completeSingle !== false && completions.length == 1) { - pickCompletion(cm, data, completions[0]); - return true; - } + function Widget(completion, data) { + this.completion = completion; + this.data = data; + var widget = this, cm = completion.cm, options = completion.options; - // Build the select widget - var hints = document.createElement("ul"), selectedHint = 0; + var hints = this.hints = document.createElement("ul"); hints.className = "CodeMirror-hints"; - for (var i = 0; i < completions.length; ++i) { - var elt = hints.appendChild(document.createElement("li")), completion = completions[i]; - var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active"); - if (completion.className != null) className = completion.className + " " + className; + this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0; + + var completions = data.list; + for (var i = 0; i < completions.length; ++i) { + var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; + var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); + if (cur.className != null) className = cur.className + " " + className; elt.className = className; - if (completion.render) {completion.render(elt, data, completion);} - else elt.appendChild(document.createTextNode(getText(completion))); + if (cur.render) cur.render(elt, data, cur); + else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); elt.hintId = i; } - + var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null); var left = pos.left, top = pos.bottom, below = true; hints.style.left = left + "px"; hints.style.top = top + "px"; - hints.style.width = (maxWidth * 18) + "px"; - - ie = /MSIE \d/.test(navigator.userAgent); - if( ie.length > 0 ) { - hints.style.left = (left + 2) + "px"; - hints.style.top = (top + 20) + "px"; - } - - hints.style.zIndex = "5000"; - document.body.appendChild(hints); - // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); + (options.container || document.body).appendChild(hints); var box = hints.getBoundingClientRect(); - var overlapX = box.right - winW, overlapY = box.bottom - winH; if (overlapX > 0) { if (box.right - box.left > winW) { @@ -95,99 +214,122 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.style.top = (top = pos.bottom - overlapY) + "px"; } - function changeActive(i) { - i = Math.max(0, Math.min(i, completions.length - 1)); - if (selectedHint == i) return; - var node = hints.childNodes[selectedHint]; - node.className = node.className.replace(" CodeMirror-hint-active", ""); - node = hints.childNodes[selectedHint = i]; - node.className += " CodeMirror-hint-active"; - if (node.offsetTop < hints.scrollTop) - hints.scrollTop = node.offsetTop - 3; - else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight) - hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; + cm.addKeyMap(this.keyMap = buildKeyMap(options, { + moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, + setFocus: function(n) { widget.changeActive(n); }, + menuSize: function() { return widget.screenAmount(); }, + length: completions.length, + close: function() { completion.close(); }, + pick: function() { widget.pick(); } + })); + + if (options.closeOnUnfocus !== false) { + var closingOnBlur; + cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); + cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); } - function screenAmount() { - return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1; - } - - var ourMap = { - Up: function() {changeActive(selectedHint - 1);}, - Down: function() {changeActive(selectedHint + 1);}, - PageUp: function() {changeActive(selectedHint - screenAmount());}, - PageDown: function() {changeActive(selectedHint + screenAmount());}, - Home: function() {changeActive(0);}, - End: function() {changeActive(completions.length - 1);}, - Enter: pick, - Tab: pick, - Esc: close - }; - if (options.customKeys) for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) { - var val = options.customKeys[key]; - if (/^(Up|Down|Enter|Esc)$/.test(key)) val = ourMap[val]; - ourMap[key] = val; - } - - cm.addKeyMap(ourMap); - cm.on("cursorActivity", cursorActivity); - var closingOnBlur; - function onBlur(){ closingOnBlur = setTimeout(close, 100); }; - function onFocus(){ clearTimeout(closingOnBlur); }; - cm.on("blur", onBlur); - cm.on("focus", onFocus); var startScroll = cm.getScrollInfo(); - function onScroll() { + cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); - var newTop = top + startScroll.top - curScroll.top, point = newTop; + var newTop = top + startScroll.top - curScroll.top; + var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); if (!below) point += hints.offsetHeight; - if (point <= editor.top || point >= editor.bottom) return close(); + if (point <= editor.top || point >= editor.bottom) return completion.close(); hints.style.top = newTop + "px"; hints.style.left = (left + startScroll.left - curScroll.left) + "px"; - } - cm.on("scroll", onScroll); + }); + CodeMirror.on(hints, "dblclick", function(e) { - var t = e.target || e.srcElement; - if (t.hintId != null) {selectedHint = t.hintId; pick();} + var t = getHintElement(hints, e.target || e.srcElement); + if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} }); + CodeMirror.on(hints, "click", function(e) { - var t = e.target || e.srcElement; - if (t.hintId != null) changeActive(t.hintId); + var t = getHintElement(hints, e.target || e.srcElement); + if (t && t.hintId != null) { + widget.changeActive(t.hintId); + if (options.completeOnSingleClick) widget.pick(); + } }); + CodeMirror.on(hints, "mousedown", function() { setTimeout(function(){cm.focus();}, 20); }); - var done = false, once; - function close() { - if (done) return; - done = true; - clearTimeout(once); - hints.parentNode.removeChild(hints); - cm.removeKeyMap(ourMap); - cm.off("cursorActivity", cursorActivity); - cm.off("blur", onBlur); - cm.off("focus", onFocus); - cm.off("scroll", onScroll); - } - function pick() { - pickCompletion(cm, data, completions[selectedHint]); - close(); - } - var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length; - function cursorActivity() { - clearTimeout(once); - - var pos = cm.getCursor(), line = cm.getLine(pos.line); - if (pos.line != lastPos.line || line.length - pos.ch != lastLen - lastPos.ch || - pos.ch < startCh || cm.somethingSelected() || - (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) - close(); - else - once = setTimeout(function(){close(); continued = true; startHinting();}, 70); - } + CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; } - return startHinting(); -}; + Widget.prototype = { + close: function() { + if (this.completion.widget != this) return; + this.completion.widget = null; + this.hints.parentNode.removeChild(this.hints); + this.completion.cm.removeKeyMap(this.keyMap); + + var cm = this.completion.cm; + if (this.completion.options.closeOnUnfocus !== false) { + cm.off("blur", this.onBlur); + cm.off("focus", this.onFocus); + } + cm.off("scroll", this.onScroll); + }, + + pick: function() { + this.completion.pick(this.data, this.selectedHint); + }, + + changeActive: function(i, avoidWrap) { + if (i >= this.data.list.length) + i = avoidWrap ? this.data.list.length - 1 : 0; + else if (i < 0) + i = avoidWrap ? 0 : this.data.list.length - 1; + if (this.selectedHint == i) return; + var node = this.hints.childNodes[this.selectedHint]; + node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); + node = this.hints.childNodes[this.selectedHint = i]; + node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; + if (node.offsetTop < this.hints.scrollTop) + this.hints.scrollTop = node.offsetTop - 3; + else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) + this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; + CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); + }, + + screenAmount: function() { + return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; + } + }; + + CodeMirror.registerHelper("hint", "auto", function(cm, options) { + var helpers = cm.getHelpers(cm.getCursor(), "hint"); + if (helpers.length) { + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, options); + if (cur && cur.list.length) return cur; + } + } else { + var words = cm.getHelper(cm.getCursor(), "hintWords"); + if (words) return CodeMirror.hint.fromList(cm, {words: words}); + } + }); + + CodeMirror.registerHelper("hint", "fromList", function(cm, options) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var found = []; + for (var i = 0; i < options.words.length; i++) { + var word = options.words[i]; + if (word.slice(0, token.string.length) == token.string) + found.push(word); + } + + if (found.length) return { + list: found, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end) + }; + }); + + CodeMirror.commands.autocomplete = CodeMirror.showHint; +})(); diff --git a/gulliver/js/codemirror/addon/hint/sql-hint.js b/gulliver/js/codemirror/addon/hint/sql-hint.js new file mode 100644 index 000000000..d00781cff --- /dev/null +++ b/gulliver/js/codemirror/addon/hint/sql-hint.js @@ -0,0 +1,146 @@ +(function () { + "use strict"; + + var tables; + var keywords; + var CONS = { + QUERY_DIV: ";", + ALIAS_KEYWORD: "AS" + }; + + function getKeywords(editor) { + var mode = editor.doc.modeOption; + if(mode === "sql") mode = "text/x-sql"; + return CodeMirror.resolveMode(mode).keywords; + } + + function match(string, word) { + var len = string.length; + var sub = word.substr(0, len); + return string.toUpperCase() === sub.toUpperCase(); + } + + function addMatches(result, search, wordlist, formatter) { + for(var word in wordlist) { + if(!wordlist.hasOwnProperty(word)) continue; + if(Array.isArray(wordlist)) { + word = wordlist[word]; + } + if(match(search, word)) { + result.push(formatter(word)); + } + } + } + + function columnCompletion(result, editor) { + var cur = editor.getCursor(); + var token = editor.getTokenAt(cur); + var string = token.string.substr(1); + var prevCur = CodeMirror.Pos(cur.line, token.start); + var table = editor.getTokenAt(prevCur).string; + if( !tables.hasOwnProperty( table ) ){ + table = findTableByAlias(table, editor); + } + var columns = tables[table]; + if(!columns) { + return; + } + addMatches(result, string, columns, + function(w) {return "." + w;}); + } + + function eachWord(lineText, f) { + if( !lineText ){return;} + var excepted = /[,;]/g; + var words = lineText.split( " " ); + for( var i = 0; i < words.length; i++ ){ + f( words[i]?words[i].replace( excepted, '' ) : '' ); + } + } + + function convertCurToNumber( cur ){ + // max characters of a line is 999,999. + return cur.line + cur.ch / Math.pow( 10, 6 ); + } + + function convertNumberToCur( num ){ + return CodeMirror.Pos(Math.floor( num ), +num.toString().split( '.' ).pop()); + } + + function findTableByAlias(alias, editor) { + var doc = editor.doc; + var fullQuery = doc.getValue(); + var aliasUpperCase = alias.toUpperCase(); + var previousWord = ""; + var table = ""; + var separator = []; + var validRange = { + start: CodeMirror.Pos( 0, 0 ), + end: CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length ) + }; + + //add separator + var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV ); + while( indexOfSeparator != -1 ){ + separator.push( doc.posFromIndex(indexOfSeparator)); + indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1); + } + separator.unshift( CodeMirror.Pos( 0, 0 ) ); + separator.push( CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) ); + + //find valieRange + var prevItem = 0; + var current = convertCurToNumber( editor.getCursor() ); + for( var i=0; i< separator.length; i++){ + var _v = convertCurToNumber( separator[i] ); + if( current > prevItem && current <= _v ){ + validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) }; + break; + } + prevItem = _v; + } + + var query = doc.getRange(validRange.start, validRange.end, false); + + for(var i=0; i < query.length; i++){ + var lineText = query[i]; + eachWord( lineText, function( word ){ + var wordUpperCase = word.toUpperCase(); + if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){ + table = previousWord; + } + if( wordUpperCase !== CONS.ALIAS_KEYWORD ){ + previousWord = word; + } + }); + if( table ){ break; } + } + return table; + } + + function sqlHint(editor, options) { + tables = (options && options.tables) || {}; + keywords = keywords || getKeywords(editor); + var cur = editor.getCursor(); + var token = editor.getTokenAt(cur); + var result = []; + var search = token.string.trim(); + + addMatches(result, search, keywords, + function(w) {return w.toUpperCase();}); + + addMatches(result, search, tables, + function(w) {return w;}); + + if(search.lastIndexOf('.') === 0) { + columnCompletion(result, editor); + } + + return { + list: result, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end) + }; + } + CodeMirror.registerHelper("hint", "sql", sqlHint); +})(); diff --git a/gulliver/js/codemirror/addon/hint/xml-hint.js b/gulliver/js/codemirror/addon/hint/xml-hint.js index 42eab6f3b..69f2b771f 100644 --- a/gulliver/js/codemirror/addon/hint/xml-hint.js +++ b/gulliver/js/codemirror/addon/hint/xml-hint.js @@ -1,118 +1,69 @@ (function() { + "use strict"; - CodeMirror.xmlHints = []; + var Pos = CodeMirror.Pos; - CodeMirror.xmlHint = function(cm) { - - var cursor = cm.getCursor(); - - if (cursor.ch > 0) { - - var text = cm.getRange(CodeMirror.Pos(0, 0), cursor); - var typed = ''; - var simbol = ''; - for(var i = text.length - 1; i >= 0; i--) { - if(text[i] == ' ' || text[i] == '<') { - simbol = text[i]; - break; - } - else { - typed = text[i] + typed; - } - } - - text = text.slice(0, text.length - typed.length); - - var path = getActiveElement(text) + simbol; - var hints = CodeMirror.xmlHints[path]; - - if(typeof hints === 'undefined') - hints = ['']; - else { - hints = hints.slice(0); - for (var i = hints.length - 1; i >= 0; i--) { - if(hints[i].indexOf(typed) != 0) - hints.splice(i, 1); - } - } - - return { - list: hints, - from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length), - to: cursor - }; + function getHints(cm, options) { + var tags = options && options.schemaInfo; + var quote = (options && options.quoteChar) || '"'; + if (!tags) return; + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var inner = CodeMirror.innerMode(cm.getMode(), token.state); + if (inner.mode.name != "xml") return; + var result = [], replaceToken = false, prefix; + var isTag = token.string.charAt(0) == "<"; + if (!inner.state.tagName || isTag) { // Tag completion + if (isTag) { + prefix = token.string.slice(1); + replaceToken = true; + } + var cx = inner.state.context, curTag = cx && tags[cx.tagName]; + var childList = cx ? curTag && curTag.children : tags["!top"]; + if (childList) { + for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) + result.push("<" + childList[i]); + } else { + for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) + result.push("<" + name); + } + if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0)) + result.push(""); + } else { + // Attribute completion + var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; + if (!attrs) return; + if (token.type == "string" || token.string == "=") { // A value + var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), + Pos(cur.line, token.type == "string" ? token.start : token.end)); + var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; + if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; + if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget + if (token.type == "string") { + prefix = token.string; + if (/['"]/.test(token.string.charAt(0))) { + quote = token.string.charAt(0); + prefix = token.string.slice(1); + } + replaceToken = true; } - }; - - var getActiveElement = function(text) { - - var element = ''; - - if(text.length >= 0) { - - var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g'); - - var matches = []; - var match; - while ((match = regex.exec(text)) != null) { - matches.push({ - tag: match[1], - selfclose: (match[0].slice(match[0].length - 2) === '/>') - }); - } - - for (var i = matches.length - 1, skip = 0; i >= 0; i--) { - - var item = matches[i]; - - if (item.tag[0] == '/') - { - skip++; - } - else if (item.selfclose == false) - { - if (skip > 0) - { - skip--; - } - else - { - element = '<' + item.tag + '>' + element; - } - } - } - - element += getOpenTag(text); + for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) + result.push(quote + atValues[i] + quote); + } else { // An attribute name + if (token.type == "attribute") { + prefix = token.string; + replaceToken = true; } - - return element; - }; - - var getOpenTag = function(text) { - - var open = text.lastIndexOf('<'); - var close = text.lastIndexOf('>'); - - if (close < open) - { - text = text.slice(open); - - if(text != '<') { - - var space = text.indexOf(' '); - if(space < 0) - space = text.indexOf('\t'); - if(space < 0) - space = text.indexOf('\n'); - - if (space < 0) - space = text.length; - - return text.slice(0, space); - } - } - - return ''; + for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) + result.push(attr); + } + } + return { + list: result, + from: replaceToken ? Pos(cur.line, token.start) : cur, + to: replaceToken ? Pos(cur.line, token.end) : cur }; + } + CodeMirror.xmlHint = getHints; // deprecated + CodeMirror.registerHelper("hint", "xml", getHints); })(); diff --git a/gulliver/js/codemirror/addon/lint/coffeescript-lint.js b/gulliver/js/codemirror/addon/lint/coffeescript-lint.js new file mode 100644 index 000000000..7f55a294d --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/coffeescript-lint.js @@ -0,0 +1,27 @@ +// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js + +// declare global: coffeelint + +CodeMirror.registerHelper("lint", "coffeescript", function(text) { + var found = []; + var parseError = function(err) { + var loc = err.lineNumber; + found.push({from: CodeMirror.Pos(loc-1, 0), + to: CodeMirror.Pos(loc, 0), + severity: err.level, + message: err.message}); + }; + try { + var res = coffeelint.lint(text); + for(var i = 0; i < res.length; i++) { + parseError(res[i]); + } + } catch(e) { + found.push({from: CodeMirror.Pos(e.location.first_line, 0), + to: CodeMirror.Pos(e.location.last_line, e.location.last_column), + severity: 'error', + message: e.message}); + } + return found; +}); +CodeMirror.coffeeValidator = CodeMirror.lint.coffeescript; // deprecated diff --git a/gulliver/js/codemirror/addon/lint/css-lint.js b/gulliver/js/codemirror/addon/lint/css-lint.js new file mode 100644 index 000000000..1de71fbcb --- /dev/null +++ b/gulliver/js/codemirror/addon/lint/css-lint.js @@ -0,0 +1,19 @@ +// Depends on csslint.js from https://github.com/stubbornella/csslint + +// declare global: CSSLint + +CodeMirror.registerHelper("lint", "css", function(text) { + var found = []; + var results = CSSLint.verify(text), messages = results.messages, message = null; + for ( var i = 0; i < messages.length; i++) { + message = messages[i]; + var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col; + found.push({ + from: CodeMirror.Pos(startLine, startCol), + to: CodeMirror.Pos(endLine, endCol), + message: message.message, + severity : message.type + }); + } + return found; +}); diff --git a/gulliver/js/codemirror/addon/lint/javascript-lint.js b/gulliver/js/codemirror/addon/lint/javascript-lint.js index db9ff6b22..7123ab7e6 100644 --- a/gulliver/js/codemirror/addon/lint/javascript-lint.js +++ b/gulliver/js/codemirror/addon/lint/javascript-lint.js @@ -1,26 +1,25 @@ (function() { + "use strict"; + // declare global: JSHINT var bogus = [ "Dangerous comment" ]; var warnings = [ [ "Expected '{'", - "Statement body should be inside '{ }' braces." ] ]; + "Statement body should be inside '{ }' braces." ] ]; var errors = [ "Missing semicolon", "Extra comma", "Missing property name", - "Unmatched ", " and instead saw", " is not defined", - "Unclosed string", "Stopping, unable to continue" ]; + "Unmatched ", " and instead saw", " is not defined", + "Unclosed string", "Stopping, unable to continue" ]; - function validator(options, text) { + function validator(text, options) { JSHINT(text, options); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; } - CodeMirror.javascriptValidatorWithOptions = function(options) { - return function(text) { return validator(options, text); }; - }; - - CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null); + CodeMirror.registerHelper("lint", "javascript", validator); + CodeMirror.javascriptValidator = CodeMirror.lint.javascript; // deprecated function cleanup(error) { // All problems are warnings by default @@ -42,10 +41,10 @@ found = description.indexOf(find) !== -1; if (force || found) { - error.severity = severity; + error.severity = severity; } if (found && replace) { - error.description = replace; + error.description = replace; } } } @@ -54,7 +53,7 @@ var description = error.description; for ( var i = 0; i < bogus.length; i++) { if (description.indexOf(bogus[i]) !== -1) { - return true; + return true; } } return false; @@ -64,59 +63,59 @@ for ( var i = 0; i < errors.length; i++) { var error = errors[i]; if (error) { - var linetabpositions, index; + var linetabpositions, index; - linetabpositions = []; + linetabpositions = []; - // This next block is to fix a problem in jshint. Jshint - // replaces - // all tabs with spaces then performs some checks. The error - // positions (character/space) are then reported incorrectly, - // not taking the replacement step into account. Here we look - // at the evidence line and try to adjust the character position - // to the correct value. - if (error.evidence) { - // Tab positions are computed once per line and cached - var tabpositions = linetabpositions[error.line]; - if (!tabpositions) { - var evidence = error.evidence; - tabpositions = []; - // ugggh phantomjs does not like this - // forEachChar(evidence, function(item, index) { - Array.prototype.forEach.call(evidence, function(item, - index) { - if (item === '\t') { - // First col is 1 (not 0) to match error - // positions - tabpositions.push(index + 1); - } - }); - linetabpositions[error.line] = tabpositions; - } - if (tabpositions.length > 0) { - var pos = error.character; - tabpositions.forEach(function(tabposition) { - if (pos > tabposition) pos -= 1; - }); - error.character = pos; - } - } + // This next block is to fix a problem in jshint. Jshint + // replaces + // all tabs with spaces then performs some checks. The error + // positions (character/space) are then reported incorrectly, + // not taking the replacement step into account. Here we look + // at the evidence line and try to adjust the character position + // to the correct value. + if (error.evidence) { + // Tab positions are computed once per line and cached + var tabpositions = linetabpositions[error.line]; + if (!tabpositions) { + var evidence = error.evidence; + tabpositions = []; + // ugggh phantomjs does not like this + // forEachChar(evidence, function(item, index) { + Array.prototype.forEach.call(evidence, function(item, + index) { + if (item === '\t') { + // First col is 1 (not 0) to match error + // positions + tabpositions.push(index + 1); + } + }); + linetabpositions[error.line] = tabpositions; + } + if (tabpositions.length > 0) { + var pos = error.character; + tabpositions.forEach(function(tabposition) { + if (pos > tabposition) pos -= 1; + }); + error.character = pos; + } + } - var start = error.character - 1, end = start + 1; - if (error.evidence) { - index = error.evidence.substring(start).search(/.\b/); - if (index > -1) { - end += index; - } - } + var start = error.character - 1, end = start + 1; + if (error.evidence) { + index = error.evidence.substring(start).search(/.\b/); + if (index > -1) { + end += index; + } + } - // Convert to format expected by validation service - error.description = error.reason;// + "(jshint)"; - error.start = error.character; - error.end = end; - error = cleanup(error); + // Convert to format expected by validation service + error.description = error.reason;// + "(jshint)"; + error.start = error.character; + error.end = end; + error = cleanup(error); - if (error) + if (error) output.push({message: error.description, severity: error.severity, from: CodeMirror.Pos(error.line - 1, start), diff --git a/gulliver/js/codemirror/addon/lint/json-lint.js b/gulliver/js/codemirror/addon/lint/json-lint.js index 42b36abb3..e10e11bca 100644 --- a/gulliver/js/codemirror/addon/lint/json-lint.js +++ b/gulliver/js/codemirror/addon/lint/json-lint.js @@ -1,6 +1,8 @@ // Depends on jsonlint.js from https://github.com/zaach/jsonlint -CodeMirror.jsonValidator = function(text) { +// declare global: jsonlint + +CodeMirror.registerHelper("lint", "json", function(text) { var found = []; jsonlint.parseError = function(str, hash) { var loc = hash.loc; @@ -11,4 +13,5 @@ CodeMirror.jsonValidator = function(text) { try { jsonlint.parse(text); } catch(e) {} return found; -}; +}); +CodeMirror.jsonValidator = CodeMirror.lint.json; // deprecated diff --git a/gulliver/js/codemirror/addon/lint/lint.css b/gulliver/js/codemirror/addon/lint/lint.css index 4fd72e774..414a9a0e0 100644 --- a/gulliver/js/codemirror/addon/lint/lint.css +++ b/gulliver/js/codemirror/addon/lint/lint.css @@ -14,6 +14,7 @@ padding: 2px 5px; position: fixed; white-space: pre; + white-space: pre-wrap; z-index: 100; max-width: 600px; opacity: 0; @@ -24,18 +25,18 @@ -ms-transition: opacity .4s; } -.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning { +.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { background-position: left bottom; background-repeat: repeat-x; } -.CodeMirror-lint-span-error { +.CodeMirror-lint-mark-error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") ; } -.CodeMirror-lint-span-warning { +.CodeMirror-lint-mark-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); } @@ -57,40 +58,16 @@ } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); } .CodeMirror-lint-marker-multiple { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; } - -/* Styles for the overview ruler -.annotationOverview { - cursor: pointer; - border-radius: 2px; - left: 2px; - width: 8px; -} -.annotationOverview.error { - background-color: lightcoral; - border: 1px solid darkred; -} -.annotationOverview.warning { - background-color: Gold; - border: 1px solid black; -} - -.annotationHTML.overlay { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); - background-position: right bottom; - position: relative; - top: -16px; -} -*/ \ No newline at end of file diff --git a/gulliver/js/codemirror/addon/lint/lint.js b/gulliver/js/codemirror/addon/lint/lint.js index dd838e7ea..6121735d3 100644 --- a/gulliver/js/codemirror/addon/lint/lint.js +++ b/gulliver/js/codemirror/addon/lint/lint.js @@ -1,4 +1,5 @@ -CodeMirror.validate = (function() { +(function() { + "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; var SEVERITIES = /^(?:error|warning)$/; @@ -10,12 +11,12 @@ CodeMirror.validate = (function() { function position(e) { if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); - tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; tt.style.left = (e.clientX + 5) + "px"; } CodeMirror.on(document, "mousemove", position); position(e); - tt.style.opacity = 1; + if (tt.style.opacity != null) tt.style.opacity = 1; return tt; } function rm(elt) { @@ -28,6 +29,22 @@ CodeMirror.validate = (function() { setTimeout(function() { rm(tt); }, 600); } + function showTooltipFor(e, content, node) { + var tooltip = showTooltip(e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + function LintState(cm, options, hasGutter) { this.marked = []; this.options = options; @@ -36,21 +53,23 @@ CodeMirror.validate = (function() { this.onMouseOver = function(e) { onMouseOver(cm, e); }; } - function parseOptions(options) { + function parseOptions(cm, options) { if (options instanceof Function) return {getAnnotations: options}; - else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + if (!options || options === true) options = {}; + if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); return options; } function clearMarks(cm) { - var state = cm._lintState; + var state = cm.state.lint; if (state.hasGutter) cm.clearGutter(GUTTER_ID); for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; } - function makeMarker(labels, severity, multiple) { + function makeMarker(labels, severity, multiple, tooltips) { var marker = document.createElement("div"), inner = marker; marker.className = "CodeMirror-lint-marker-" + severity; if (multiple) { @@ -58,9 +77,9 @@ CodeMirror.validate = (function() { inner.className = "CodeMirror-lint-marker-multiple"; } - var tooltip; - CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); - CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(e, labels, inner); + }); return marker; } @@ -89,16 +108,16 @@ CodeMirror.validate = (function() { } function startLinting(cm) { - var state = cm._lintState, options = state.options; - if (options.async) - options.getAnnotations(cm, updateLinting, options); - else - updateLinting(cm, options.getAnnotations(cm.getValue())); + var state = cm.state.lint, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue(), options.options)); } - + function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); - var state = cm._lintState, options = state.options; + var state = cm.state.lint, options = state.options; var annotations = groupByLine(annotationsNotSorted); @@ -115,35 +134,31 @@ CodeMirror.validate = (function() { if (!SEVERITIES.test(severity)) severity = "error"; maxSeverity = getMaxSeverity(maxSeverity, severity); - if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (options.formatAnnotation) ann = options.formatAnnotation(ann); if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { - className: "CodeMirror-lint-span-" + severity, + className: "CodeMirror-lint-mark-" + severity, __annotation: ann })); } if (state.hasGutter) - cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1)); + cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, + state.options.tooltips)); } if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); } function onChange(cm) { - var state = cm._lintState; + var state = cm.state.lint; clearTimeout(state.timeout); state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); } function popupSpanTooltip(ann, e) { - var tooltip = showTooltip(e, annotationTooltip(ann)); var target = e.target || e.srcElement; - CodeMirror.on(target, "mouseout", hide); - function hide() { - CodeMirror.off(target, "mouseout", hide); - hideTooltip(tooltip); - } + showTooltipFor(e, annotationTooltip(ann), target); } // When the mouseover fires, the cursor might not actually be over @@ -152,7 +167,7 @@ CodeMirror.validate = (function() { var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0]; function onMouseOver(cm, e) { - if (!/\bCodeMirror-lint-span-/.test((e.target || e.srcElement).className)) return; + if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return; for (var i = 0; i < nearby.length; i += 2) { var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i], top: e.clientY + nearby[i + 1]})); @@ -163,21 +178,26 @@ CodeMirror.validate = (function() { } } - CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + function optionHandler(cm, val, old) { if (old && old != CodeMirror.Init) { clearMarks(cm); cm.off("change", onChange); - CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); - delete cm._lintState; + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + delete cm.state.lint; } - + if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; - var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter); + var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); cm.on("change", onChange); - CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + if (state.options.tooltips != false) + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + startLinting(cm); } - }); + } + + CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated + CodeMirror.defineOption("lint", false, optionHandler); // deprecated })(); diff --git a/gulliver/js/codemirror/addon/merge/dep/diff_match_patch.js b/gulliver/js/codemirror/addon/merge/dep/diff_match_patch.js new file mode 100644 index 000000000..ac34105fc --- /dev/null +++ b/gulliver/js/codemirror/addon/merge/dep/diff_match_patch.js @@ -0,0 +1,50 @@ +// From https://code.google.com/p/google-diff-match-patch/ , licensed under the Apache License 2.0 +(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32} +diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a, +b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a}; +diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l= +u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]}; +diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; +diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null; +var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]}; +diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}}; +diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_); +return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]= +h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; +diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)}; +diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; +diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h}; +diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&& +e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g); +if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0, +c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; +diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&& +(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c now) return false; + + var sInfo = editor.getScrollInfo(), halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen; + var mid = editor.lineAtHeight(midY, "local"); + var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT); + var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig); + var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit); + var ratio = (midY - off.top) / (off.bot - off.top); + var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); + + var botDist, mix; + // Some careful tweaking to make sure no space is left out of view + // when scrolling to top or bottom. + if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) { + targetPos = targetPos * mix + sInfo.top * (1 - mix); + } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) { + var otherInfo = other.getScrollInfo(); + var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos; + if (botDistOther > botDist && (mix = botDist / halfScreen) < 1) + targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); + } + + other.scrollTo(sInfo.left, targetPos); + other.state.scrollSetAt = now; + other.state.scrollSetBy = dv; + return true; + } + + function getOffsets(editor, around) { + var bot = around.after; + if (bot == null) bot = editor.lastLine() + 1; + return {top: editor.heightAtLine(around.before || 0, "local"), + bot: editor.heightAtLine(bot, "local")}; + } + + function setScrollLock(dv, val, action) { + dv.lockScroll = val; + if (val && action != false) syncScroll(dv, DIFF_INSERT) && drawConnectors(dv); + dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db  \u21da"; + } + + // Updating the marks for editor content + + function clearMarks(editor, arr, classes) { + for (var i = 0; i < arr.length; ++i) { + var mark = arr[i]; + if (mark instanceof CodeMirror.TextMarker) { + mark.clear(); + } else { + editor.removeLineClass(mark, "background", classes.chunk); + editor.removeLineClass(mark, "background", classes.start); + editor.removeLineClass(mark, "background", classes.end); + } + } + arr.length = 0; + } + + // FIXME maybe add a margin around viewport to prevent too many updates + function updateMarks(editor, diff, state, type, classes) { + var vp = editor.getViewport(); + editor.operation(function() { + if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { + clearMarks(editor, state.marked, classes); + markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes); + state.from = vp.from; state.to = vp.to; + } else { + if (vp.from < state.from) { + markChanges(editor, diff, type, state.marked, vp.from, state.from, classes); + state.from = vp.from; + } + if (vp.to > state.to) { + markChanges(editor, diff, type, state.marked, state.to, vp.to, classes); + state.to = vp.to; + } + } + }); + } + + function markChanges(editor, diff, type, marks, from, to, classes) { + var pos = Pos(0, 0); + var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1)); + var cls = type == DIFF_DELETE ? classes.del : classes.insert; + function markChunk(start, end) { + var bfrom = Math.max(from, start), bto = Math.min(to, end); + for (var i = bfrom; i < bto; ++i) { + var line = editor.addLineClass(i, "background", classes.chunk); + if (i == start) editor.addLineClass(line, "background", classes.start); + if (i == end - 1) editor.addLineClass(line, "background", classes.end); + marks.push(line); + } + // When the chunk is empty, make sure a horizontal line shows up + if (start == end && bfrom == end && bto == end) { + if (bfrom) + marks.push(editor.addLineClass(bfrom - 1, "background", classes.end)); + else + marks.push(editor.addLineClass(bfrom, "background", classes.start)); + } + } + + var chunkStart = 0; + for (var i = 0; i < diff.length; ++i) { + var part = diff[i], tp = part[0], str = part[1]; + if (tp == DIFF_EQUAL) { + var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1); + moveOver(pos, str); + var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0); + if (cleanTo > cleanFrom) { + if (i) markChunk(chunkStart, cleanFrom); + chunkStart = cleanTo; + } + } else { + if (tp == type) { + var end = moveOver(pos, str, true); + var a = posMax(top, pos), b = posMin(bot, end); + if (!posEq(a, b)) + marks.push(editor.markText(a, b, {className: cls})); + pos = end; + } + } + } + if (chunkStart <= pos.line) markChunk(chunkStart, pos.line + 1); + } + + // Updating the gap between editor and original + + function drawConnectors(dv) { + if (!dv.showDifferences) return; + + if (dv.svg) { + clear(dv.svg); + var w = dv.gap.offsetWidth; + attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight); + } + clear(dv.copyButtons); + + var flip = dv.type == "left"; + var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); + var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top; + iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) { + if (topEdit > vpEdit.to || botEdit < vpEdit.from || + topOrig > vpOrig.to || botOrig < vpOrig.from) + return; + var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx; + if (dv.svg) { + var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit; + if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; } + var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig; + var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit; + if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; } + var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx; + var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx; + attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")), + "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z", + "class", dv.classes.connect); + } + var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", + "CodeMirror-merge-copy")); + copy.title = "Revert chunk"; + copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig}; + copy.style.top = top + "px"; + }); + } + + function copyChunk(dv, chunk) { + if (dv.diffOutOfDate) return; + dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)), + Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0)); + } + + // Merge view, containing 0, 1, or 2 diff views. + + var MergeView = CodeMirror.MergeView = function(node, options) { + if (!(this instanceof MergeView)) return new MergeView(node, options); + + var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight; + var hasLeft = origLeft != null, hasRight = origRight != null; + var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0); + var wrap = [], left = this.left = null, right = this.right = null; + + if (hasLeft) { + left = this.left = new DiffView(this, "left"); + var leftPane = elt("div", null, "CodeMirror-merge-pane"); + wrap.push(leftPane); + wrap.push(buildGap(left)); + } + + var editPane = elt("div", null, "CodeMirror-merge-pane"); + wrap.push(editPane); + + if (hasRight) { + right = this.right = new DiffView(this, "right"); + wrap.push(buildGap(right)); + var rightPane = elt("div", null, "CodeMirror-merge-pane"); + wrap.push(rightPane); + } + + (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; + + wrap.push(elt("div", null, null, "height: 0; clear: both;")); + var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); + this.edit = CodeMirror(editPane, copyObj(options)); + + if (left) left.init(leftPane, origLeft, options); + if (right) right.init(rightPane, origRight, options); + + var onResize = function() { + if (left) drawConnectors(left); + if (right) drawConnectors(right); + }; + CodeMirror.on(window, "resize", onResize); + var resizeInterval = setInterval(function() { + for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {} + if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); } + }, 5000); + }; + + function buildGap(dv) { + var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); + lock.title = "Toggle locked scrolling"; + var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); + CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); + dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); + CodeMirror.on(dv.copyButtons, "click", function(e) { + var node = e.target || e.srcElement; + if (node.chunk) copyChunk(dv, node.chunk); + }); + var gapElts = [dv.copyButtons, lockWrap]; + var svg = document.createElementNS && document.createElementNS(svgNS, "svg"); + if (svg && !svg.createSVGRect) svg = null; + dv.svg = svg; + if (svg) gapElts.push(svg); + + return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap"); + } + + MergeView.prototype = { + constuctor: MergeView, + editor: function() { return this.edit; }, + rightOriginal: function() { return this.right && this.right.orig; }, + leftOriginal: function() { return this.left && this.left.orig; }, + setShowDifferences: function(val) { + if (this.right) this.right.setShowDifferences(val); + if (this.left) this.left.setShowDifferences(val); + } + }; + + function asString(obj) { + if (typeof obj == "string") return obj; + else return obj.getValue(); + } + + // Operations on diffs + + var dmp = new diff_match_patch(); + function getDiff(a, b) { + var diff = dmp.diff_main(a, b); + dmp.diff_cleanupSemantic(diff); + // The library sometimes leaves in empty parts, which confuse the algorithm + for (var i = 0; i < diff.length; ++i) { + var part = diff[i]; + if (!part[1]) { + diff.splice(i--, 1); + } else if (i && diff[i - 1][0] == part[0]) { + diff.splice(i--, 1); + diff[i][1] += part[1]; + } + } + return diff; + } + + function iterateChunks(diff, f) { + var startEdit = 0, startOrig = 0; + var edit = Pos(0, 0), orig = Pos(0, 0); + for (var i = 0; i < diff.length; ++i) { + var part = diff[i], tp = part[0]; + if (tp == DIFF_EQUAL) { + var startOff = startOfLineClean(diff, i) ? 0 : 1; + var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff; + moveOver(edit, part[1], null, orig); + var endOff = endOfLineClean(diff, i) ? 1 : 0; + var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff; + if (cleanToEdit > cleanFromEdit) { + if (i) f(startOrig, cleanFromOrig, startEdit, cleanFromEdit); + startEdit = cleanToEdit; startOrig = cleanToOrig; + } + } else { + moveOver(tp == DIFF_INSERT ? edit : orig, part[1]); + } + } + if (startEdit <= edit.line || startOrig <= orig.line) + f(startOrig, orig.line + 1, startEdit, edit.line + 1); + } + + function endOfLineClean(diff, i) { + if (i == diff.length - 1) return true; + var next = diff[i + 1][1]; + if (next.length == 1 || next.charCodeAt(0) != 10) return false; + if (i == diff.length - 2) return true; + next = diff[i + 2][1]; + return next.length > 1 && next.charCodeAt(0) == 10; + } + + function startOfLineClean(diff, i) { + if (i == 0) return true; + var last = diff[i - 1][1]; + if (last.charCodeAt(last.length - 1) != 10) return false; + if (i == 1) return true; + last = diff[i - 2][1]; + return last.charCodeAt(last.length - 1) == 10; + } + + function chunkBoundariesAround(diff, n, nInEdit) { + var beforeE, afterE, beforeO, afterO; + iterateChunks(diff, function(fromOrig, toOrig, fromEdit, toEdit) { + var fromLocal = nInEdit ? fromEdit : fromOrig; + var toLocal = nInEdit ? toEdit : toOrig; + if (afterE == null) { + if (fromLocal > n) { afterE = fromEdit; afterO = fromOrig; } + else if (toLocal > n) { afterE = toEdit; afterO = toOrig; } + } + if (toLocal <= n) { beforeE = toEdit; beforeO = toOrig; } + else if (fromLocal <= n) { beforeE = fromEdit; beforeO = fromOrig; } + }); + return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}}; + } + + // General 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") e.appendChild(document.createTextNode(content)); + else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); + return e; + } + + function clear(node) { + for (var count = node.childNodes.length; count > 0; --count) + node.removeChild(node.firstChild); + } + + function attrs(elt) { + for (var i = 1; i < arguments.length; i += 2) + elt.setAttribute(arguments[i], arguments[i+1]); + } + + function copyObj(obj, target) { + if (!target) target = {}; + for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop]; + return target; + } + + function moveOver(pos, str, copy, other) { + var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0; + for (;;) { + var nl = str.indexOf("\n", at); + if (nl == -1) break; + ++out.line; + if (other) ++other.line; + at = nl + 1; + } + out.ch = (at ? 0 : out.ch) + (str.length - at); + if (other) other.ch = (at ? 0 : other.ch) + (str.length - at); + return out; + } + + function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; } + function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; } + function posEq(a, b) { return a.line == b.line && a.ch == b.ch; } +})(); diff --git a/gulliver/js/codemirror/addon/mode/multiplex.js b/gulliver/js/codemirror/addon/mode/multiplex.js index e77ff2a9c..614ab1add 100644 --- a/gulliver/js/codemirror/addon/mode/multiplex.js +++ b/gulliver/js/codemirror/addon/mode/multiplex.js @@ -1,5 +1,5 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { - // Others should be {open, close, mode [, delimStyle]} objects + // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects var others = Array.prototype.slice.call(arguments, 1); var n_others = others.length; @@ -47,7 +47,11 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return outerToken; } else { var curInner = state.innerActive, oldContent = stream.string; - var found = indexOf(oldContent, curInner.close, stream.pos); + if (!curInner.close && stream.sol()) { + state.innerActive = state.inner = null; + return this.token(stream, state); + } + var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos) : -1; if (found == stream.pos) { stream.match(curInner.close); state.innerActive = state.inner = null; @@ -56,12 +60,16 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { if (found > -1) stream.string = oldContent.slice(0, found); var innerToken = curInner.mode.token(stream, state.inner); if (found > -1) stream.string = oldContent; - var cur = stream.current(), found = cur.indexOf(curInner.close); - if (found > -1) stream.backUp(cur.length - found); + + if (curInner.innerStyle) { + if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle; + else innerToken = curInner.innerStyle; + } + return innerToken; } }, - + indent: function(state, textAfter) { var mode = state.innerActive ? state.innerActive.mode : outer; if (!mode.indent) return CodeMirror.Pass; @@ -81,7 +89,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0); } } - } else if (mode.close === "\n") { + } else if (state.innerActive.close === "\n") { state.innerActive = state.inner = null; } }, diff --git a/gulliver/js/codemirror/addon/mode/multiplex_test.js b/gulliver/js/codemirror/addon/mode/multiplex_test.js new file mode 100644 index 000000000..c0656357c --- /dev/null +++ b/gulliver/js/codemirror/addon/mode/multiplex_test.js @@ -0,0 +1,30 @@ +(function() { + CodeMirror.defineMode("markdown_with_stex", function(){ + var inner = CodeMirror.getMode({}, "stex"); + var outer = CodeMirror.getMode({}, "markdown"); + + var innerOptions = { + open: '$', + close: '$', + mode: inner, + delimStyle: 'delim', + innerStyle: 'inner' + }; + + return CodeMirror.multiplexingMode(outer, innerOptions); + }); + + var mode = CodeMirror.getMode({}, "markdown_with_stex"); + + function MT(name) { + test.mode( + name, + mode, + Array.prototype.slice.call(arguments, 1), + 'multiplexing'); + } + + MT( + "stexInsideMarkdown", + "[strong **Equation:**] [delim $][inner&tag \\pi][delim $]"); +})(); diff --git a/gulliver/js/codemirror/addon/mode/overlay.js b/gulliver/js/codemirror/addon/mode/overlay.js index fba38987b..b7928a7bb 100644 --- a/gulliver/js/codemirror/addon/mode/overlay.js +++ b/gulliver/js/codemirror/addon/mode/overlay.js @@ -43,14 +43,14 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; else return state.overlayCur; }, - + indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, electricChars: base.electricChars, innerMode: function(state) { return {state: state.base, mode: base}; }, - + blankLine: function(state) { if (base.blankLine) base.blankLine(state.base); if (overlay.blankLine) overlay.blankLine(state.overlay); diff --git a/gulliver/js/codemirror/addon/runmode/runmode-standalone.js b/gulliver/js/codemirror/addon/runmode/runmode-standalone.js index 7a9b82ffa..ec06ba11c 100644 --- a/gulliver/js/codemirror/addon/runmode/runmode-standalone.js +++ b/gulliver/js/codemirror/addon/runmode/runmode-standalone.js @@ -2,11 +2,15 @@ window.CodeMirror = {}; +(function() { +"use strict"; + function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -38,22 +42,29 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, 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);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; CodeMirror.StringStream = StringStream; @@ -64,17 +75,26 @@ CodeMirror.startState = function (mode, a1, a2) { var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; CodeMirror.defineMode = function (name, mode) { modes[name] = mode; }; CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; -CodeMirror.getMode = function (options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) +CodeMirror.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; - if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + spec = mimeModes[spec.name]; + } + 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) throw new Error("Unknown mode: " + spec); + return mfactory(options, spec); +}; +CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min; +CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; +}); +CodeMirror.defineMIME("text/plain", "null"); CodeMirror.runMode = function (string, modespec, callback, options) { var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec); @@ -117,14 +137,15 @@ CodeMirror.runMode = function (string, modespec, callback, options) { }; } - var lines = splitLines(string), state = CodeMirror.startState(mode); + var lines = splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new CodeMirror.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } }; +})(); diff --git a/gulliver/js/codemirror/addon/runmode/runmode.js b/gulliver/js/codemirror/addon/runmode/runmode.js index 3e1bed736..2cafa811f 100644 --- a/gulliver/js/codemirror/addon/runmode/runmode.js +++ b/gulliver/js/codemirror/addon/runmode/runmode.js @@ -1,5 +1,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); if (callback.nodeType == 1) { var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; @@ -7,7 +9,9 @@ CodeMirror.runMode = function(string, modespec, callback, options) { node.innerHTML = ""; callback = function(text, style) { if (text == "\n") { - node.appendChild(document.createElement("br")); + // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. + // Emitting a carriage return makes everything ok. + node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); col = 0; return; } @@ -39,13 +43,13 @@ CodeMirror.runMode = function(string, modespec, callback, options) { }; } - var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); + var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new CodeMirror.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } diff --git a/gulliver/js/codemirror/addon/runmode/runmode.node.js b/gulliver/js/codemirror/addon/runmode/runmode.node.js index 6449e77c8..e8bccb0cf 100644 --- a/gulliver/js/codemirror/addon/runmode/runmode.node.js +++ b/gulliver/js/codemirror/addon/runmode/runmode.node.js @@ -5,6 +5,7 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; function StringStream(string) { this.pos = this.start = 0; this.string = string; + this.lineStart = 0; } StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, @@ -36,22 +37,29 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return this.start;}, + column: function() {return this.start - this.lineStart;}, indentation: function() {return 0;}, 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);} + current: function(){return this.string.slice(this.start, this.pos);}, + hideFirstChars: function(n, inner) { + this.lineStart += n; + try { return inner(); } + finally { this.lineStart -= n; } + } }; exports.StringStream = StringStream; @@ -60,29 +68,46 @@ exports.startState = function(mode, a1, a2) { }; var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; -exports.defineMode = function(name, mode) { modes[name] = mode; }; -exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; -exports.getMode = function(options, spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) - spec = mimeModes[spec]; - if (typeof spec == "string") - var mname = spec, config = {}; - else if (spec != null) - var mname = spec.name, config = spec; - var mfactory = modes[mname]; - if (!mfactory) throw new Error("Unknown mode: " + spec); - return mfactory(options, config || {}); +exports.defineMode = function(name, mode) { + if (arguments.length > 2) { + mode.dependencies = []; + for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]); + } + modes[name] = mode; }; +exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; + +exports.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; +}); +exports.defineMIME("text/plain", "null"); + +exports.resolveMode = function(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + spec = mimeModes[spec.name]; + } + if (typeof spec == "string") return {name: spec}; + else return spec || {name: "null"}; +}; +exports.getMode = function(options, spec) { + spec = exports.resolveMode(mimeModes[spec]); + var mfactory = modes[spec.name]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, spec); +}; +exports.registerHelper = exports.registerGlobalHelper = Math.min; exports.runMode = function(string, modespec, callback) { var mode = exports.getMode({indentUnit: 2}, modespec); - var lines = splitLines(string), state = exports.startState(mode); + var lines = splitLines(string), state = (options && options.state) || exports.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); var stream = new exports.StringStream(lines[i]); while (!stream.eol()) { var style = mode.token(stream, state); - callback(stream.current(), style, i, stream.start); + callback(stream.current(), style, i, stream.start, state); stream.start = stream.pos; } } diff --git a/gulliver/js/codemirror/addon/scroll/scrollpastend.js b/gulliver/js/codemirror/addon/scroll/scrollpastend.js new file mode 100644 index 000000000..14d7d5aee --- /dev/null +++ b/gulliver/js/codemirror/addon/scroll/scrollpastend.js @@ -0,0 +1,34 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("change", onChange); + cm.display.lineSpace.parentNode.style.paddingBottom = ""; + cm.state.scrollPastEndPadding = null; + } + if (val) { + cm.on("change", onChange); + updateBottomMargin(cm); + } + }); + + function onChange(cm, change) { + if (CodeMirror.changeEnd(change).line == cm.lastLine()) + updateBottomMargin(cm); + } + + function updateBottomMargin(cm) { + var padding = ""; + if (cm.lineCount() > 1) { + var totalH = cm.display.scroller.clientHeight - 30, + lastLineH = cm.getLineHandle(cm.lastLine()).height; + padding = (totalH - lastLineH) + "px"; + } + if (cm.state.scrollPastEndPadding != padding) { + cm.state.scrollPastEndPadding = padding; + cm.display.lineSpace.parentNode.style.paddingBottom = padding; + cm.setSize(); + } + } +})(); diff --git a/gulliver/js/codemirror/addon/search/match-highlighter.js b/gulliver/js/codemirror/addon/search/match-highlighter.js index c6e35cd97..e5cbeacab 100644 --- a/gulliver/js/codemirror/addon/search/match-highlighter.js +++ b/gulliver/js/codemirror/addon/search/match-highlighter.js @@ -5,54 +5,85 @@ // document. // // The option can be set to true to simply enable it, or to a -// {minChars, style} object to explicitly configure it. minChars is -// the minimum amount of characters that should be selected for the -// behavior to occur, and style is the token style to apply to the -// matches. This will be prefixed by "cm-" to create an actual CSS -// class name. +// {minChars, style, showToken} object to explicitly configure it. +// minChars is the minimum amount of characters that should be +// selected for the behavior to occur, and style is the token style to +// apply to the matches. This will be prefixed by "cm-" to create an +// actual CSS class name. showToken, when enabled, will cause the +// current token to be highlighted when nothing is selected. (function() { var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; - + var DEFAULT_DELAY = 100; + function State(options) { - this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS; - this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE; - this.overlay = null; + if (typeof options == "object") { + this.minChars = options.minChars; + this.style = options.style; + this.showToken = options.showToken; + this.delay = options.delay; + } + if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; + if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; + if (this.delay == null) this.delay = DEFAULT_DELAY; + this.overlay = this.timeout = null; } CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { - var prev = old && old != CodeMirror.Init; - if (val && !prev) { - cm._matchHighlightState = new State(val); - cm.on("cursorActivity", highlightMatches); - } else if (!val && prev) { - var over = cm._matchHighlightState.overlay; + if (old && old != CodeMirror.Init) { + var over = cm.state.matchHighlighter.overlay; if (over) cm.removeOverlay(over); - cm._matchHighlightState = null; - cm.off("cursorActivity", highlightMatches); + clearTimeout(cm.state.matchHighlighter.timeout); + cm.state.matchHighlighter = null; + cm.off("cursorActivity", cursorActivity); + } + if (val) { + cm.state.matchHighlighter = new State(val); + highlightMatches(cm); + cm.on("cursorActivity", cursorActivity); } }); + function cursorActivity(cm) { + var state = cm.state.matchHighlighter; + clearTimeout(state.timeout); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); + } + function highlightMatches(cm) { cm.operation(function() { - var state = cm._matchHighlightState; + var state = cm.state.matchHighlighter; if (state.overlay) { cm.removeOverlay(state.overlay); state.overlay = null; } - - if (!cm.somethingSelected()) return; + if (!cm.somethingSelected() && state.showToken) { + var re = state.showToken === true ? /[\w$]/ : state.showToken; + var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; + while (start && re.test(line.charAt(start - 1))) --start; + while (end < line.length && re.test(line.charAt(end))) ++end; + if (start < end) + cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style)); + return; + } + if (cm.getCursor("head").line != cm.getCursor("anchor").line) return; var selection = cm.getSelection().replace(/^\s+|\s+$/g, ""); - if (selection.length < state.minChars) return; - - cm.addOverlay(state.overlay = makeOverlay(selection, state.style)); + if (selection.length >= state.minChars) + cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style)); }); } - function makeOverlay(query, style) { + function boundariesAround(stream, re) { + return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && + (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); + } + + function makeOverlay(query, hasBoundary, style) { return {token: function(stream) { - if (stream.match(query)) return style; + if (stream.match(query) && + (!hasBoundary || boundariesAround(stream, hasBoundary))) + return style; stream.next(); stream.skipTo(query.charAt(0)) || stream.skipToEnd(); }}; diff --git a/gulliver/js/codemirror/addon/search/search.js b/gulliver/js/codemirror/addon/search/search.js index 6331b8655..049f72f3d 100644 --- a/gulliver/js/codemirror/addon/search/search.js +++ b/gulliver/js/codemirror/addon/search/search.js @@ -7,7 +7,15 @@ // Ctrl-G. (function() { - function searchOverlay(query) { + function searchOverlay(query, caseInsensitive) { + var startChar; + if (typeof query == "string") { + startChar = query.charAt(0); + query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), + caseInsensitive ? "i" : ""); + } else { + query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : ""); + } if (typeof query == "string") return {token: function(stream) { if (stream.match(query)) return "searching"; stream.next(); @@ -17,6 +25,8 @@ if (stream.match(query)) return "searching"; while (!stream.eol()) { stream.next(); + if (startChar) + stream.skipTo(startChar) || stream.skipToEnd(); if (stream.match(query, false)) break; } }}; @@ -27,15 +37,18 @@ this.overlay = null; } function getSearchState(cm) { - return cm._searchState || (cm._searchState = new SearchState()); + return cm.state.search || (cm.state.search = new SearchState()); + } + function queryCaseInsensitive(query) { + return typeof query == "string" && query == query.toLowerCase(); } function getSearchCursor(cm, query, pos) { // Heuristic: if the query string is all lowercase, do a case insensitive search. - return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); + return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); } - function dialog(cm, text, shortText, f) { - if (cm.openDialog) cm.openDialog(text, f); - else f(prompt(shortText, "")); + function dialog(cm, text, shortText, deflt, f) { + if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); + else f(prompt(shortText, deflt)); } function confirmDialog(cm, text, shortText, fs) { if (cm.openConfirm) cm.openConfirm(text, fs); @@ -50,12 +63,12 @@ function doSearch(cm, rev) { var state = getSearchState(cm); if (state.query) return findNext(cm, rev); - dialog(cm, queryDialog, "Search for:", function(query) { + dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { cm.operation(function() { if (!query || state.query) return; state.query = parseQuery(query); - cm.removeOverlay(state.overlay); - state.overlay = searchOverlay(query); + cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); + state.overlay = searchOverlay(state.query); cm.addOverlay(state.overlay); state.posFrom = state.posTo = cm.getCursor(); findNext(cm, rev); @@ -70,6 +83,7 @@ if (!cursor.find(rev)) return; } cm.setSelection(cursor.from(), cursor.to()); + cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); state.posFrom = cursor.from(); state.posTo = cursor.to(); });} function clearSearch(cm) {cm.operation(function() { @@ -84,10 +98,10 @@ var replacementQueryDialog = 'With: '; var doReplaceConfirm = "Replace? "; function replace(cm, all) { - dialog(cm, replaceQueryDialog, "Replace:", function(query) { + dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { if (!query) return; query = parseQuery(query); - dialog(cm, replacementQueryDialog, "Replace with:", function(text) { + dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { if (all) { cm.operation(function() { for (var cursor = getSearchCursor(cm, query); cursor.findNext();) { @@ -108,6 +122,7 @@ (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return; } cm.setSelection(cursor.from(), cursor.to()); + cm.scrollIntoView({from: cursor.from(), to: cursor.to()}); confirmDialog(cm, doReplaceConfirm, "Replace?", [function() {doReplace(match);}, advance]); }; diff --git a/gulliver/js/codemirror/addon/search/searchcursor.js b/gulliver/js/codemirror/addon/search/searchcursor.js index fd134636e..711cf4ce5 100644 --- a/gulliver/js/codemirror/addon/search/searchcursor.js +++ b/gulliver/js/codemirror/addon/search/searchcursor.js @@ -1,11 +1,11 @@ (function(){ var Pos = CodeMirror.Pos; - function SearchCursor(cm, query, pos, caseFold) { - this.atOccurrence = false; this.cm = cm; + function SearchCursor(doc, query, pos, caseFold) { + this.atOccurrence = false; this.doc = doc; if (caseFold == null && typeof query == "string") caseFold = false; - pos = pos ? cm.clipPos(pos) : Pos(0, 0); + pos = pos ? doc.clipPos(pos) : Pos(0, 0); this.pos = {from: pos, to: pos}; // The matches method is filled in based on the type of query. @@ -17,26 +17,37 @@ this.matches = function(reverse, pos) { if (reverse) { query.lastIndex = 0; - var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; + var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; for (;;) { query.lastIndex = cutOff; var newMatch = query.exec(line); if (!newMatch) break; match = newMatch; start = match.index; - cutOff = match.index + 1; + cutOff = match.index + (match[0].length || 1); + if (cutOff == line.length) break; + } + var matchLen = (match && match[0].length) || 0; + if (!matchLen) { + if (start == 0 && line.length == 0) {match = undefined;} + else if (start != doc.getLine(pos.line).length) { + matchLen++; + } } } else { query.lastIndex = pos.ch; - var line = cm.getLine(pos.line), match = query.exec(line), - start = match && match.index; + var line = doc.getLine(pos.line), match = query.exec(line); + var matchLen = (match && match[0].length) || 0; + var start = match && match.index; + if (start + matchLen != line.length && !matchLen) matchLen = 1; } - if (match && match[0]) + if (match && matchLen) return {from: Pos(pos.line, start), - to: Pos(pos.line, start + match[0].length), + to: Pos(pos.line, start + matchLen), match: match}; }; } else { // String query + var origQuery = query; if (caseFold) query = query.toLowerCase(); var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; var target = query.split("\n"); @@ -48,33 +59,45 @@ this.matches = function() {}; } else { this.matches = function(reverse, pos) { - var line = fold(cm.getLine(pos.line)), len = query.length, match; - if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) - : (match = line.indexOf(query, pos.ch)) != -1) - return {from: Pos(pos.line, match), - to: Pos(pos.line, match + len)}; + if (reverse) { + var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig); + var match = line.lastIndexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match); + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } else { + var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig); + var match = line.indexOf(query); + if (match > -1) { + match = adjustPos(orig, line, match) + pos.ch; + return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; + } + } }; } } else { + var origTarget = origQuery.split("\n"); this.matches = function(reverse, pos) { - var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); - var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); - if (reverse ? offsetA >= pos.ch || offsetA != match.length - : offsetA <= pos.ch || offsetA != line.length - match.length) - return; - for (;;) { - if (reverse ? !ln : ln == cm.lineCount() - 1) return; - line = fold(cm.getLine(ln += reverse ? -1 : 1)); - match = target[reverse ? --idx : ++idx]; - if (idx > 0 && idx < target.length - 1) { - if (line != match) return; - else continue; - } - var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); - if (reverse ? offsetB != line.length - match.length : offsetB != match.length) - return; - var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); - return {from: reverse ? end : start, to: reverse ? start : end}; + var last = target.length - 1; + if (reverse) { + if (pos.line - (target.length - 1) < doc.firstLine()) return; + if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return; + var to = Pos(pos.line, origTarget[last].length); + for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln) + if (target[i] != fold(doc.getLine(ln))) return; + var line = doc.getLine(ln), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + return {from: Pos(ln, cut), to: to}; + } else { + if (pos.line + (target.length - 1) > doc.lastLine()) return; + var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length; + if (fold(line.slice(cut)) != target[0]) return; + var from = Pos(pos.line, cut); + for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln) + if (target[i] != fold(doc.getLine(ln))) return; + if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return; + return {from: from, to: Pos(ln, origTarget[last].length)}; } }; } @@ -86,7 +109,7 @@ findPrevious: function() {return this.find(true);}, find: function(reverse) { - var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); + var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to); function savePosAndFail(line) { var pos = Pos(line, 0); self.pos = {from: pos, to: pos}; @@ -96,16 +119,15 @@ for (;;) { if (this.pos = this.matches(reverse, pos)) { - if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } this.atOccurrence = true; return this.pos.match || true; } if (reverse) { if (!pos.line) return savePosAndFail(0); - pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length); + pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length); } else { - var maxLine = this.cm.lineCount(); + var maxLine = this.doc.lineCount(); if (pos.line == maxLine - 1) return savePosAndFail(maxLine); pos = Pos(pos.line + 1, 0); } @@ -118,13 +140,28 @@ replace: function(newText) { if (!this.atOccurrence) return; var lines = CodeMirror.splitLines(newText); - this.cm.replaceRange(lines, this.pos.from, this.pos.to); + this.doc.replaceRange(lines, this.pos.from, this.pos.to); this.pos.to = Pos(this.pos.from.line + lines.length - 1, lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); } }; + // Maps a position in a case-folded line back to a position in the original line + // (compensating for codepoints increasing in number during folding) + function adjustPos(orig, folded, pos) { + if (orig.length == folded.length) return pos; + for (var pos1 = Math.min(pos, orig.length);;) { + var len1 = orig.slice(0, pos1).toLowerCase().length; + if (len1 < pos) ++pos1; + else if (len1 > pos) --pos1; + else return pos1; + } + } + CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { + return new SearchCursor(this.doc, query, pos, caseFold); + }); + CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this, query, pos, caseFold); }); })(); diff --git a/gulliver/js/codemirror/addon/selection/active-line.js b/gulliver/js/codemirror/addon/selection/active-line.js index 988a0ffbf..7cf7793c0 100644 --- a/gulliver/js/codemirror/addon/selection/active-line.js +++ b/gulliver/js/codemirror/addon/selection/active-line.js @@ -12,28 +12,34 @@ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - updateActiveLine(cm); - cm.on("cursorActivity", updateActiveLine); + updateActiveLine(cm, cm.getCursor().line); + cm.on("beforeSelectionChange", selectionChange); } else if (!val && prev) { - cm.off("cursorActivity", updateActiveLine); + cm.off("beforeSelectionChange", selectionChange); clearActiveLine(cm); - delete cm._activeLine; + delete cm.state.activeLine; } }); - + function clearActiveLine(cm) { - if ("_activeLine" in cm) { - cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); - cm.removeLineClass(cm._activeLine, "background", BACK_CLASS); + 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._activeLine == line) return; - clearActiveLine(cm); - cm.addLineClass(line, "wrap", WRAP_CLASS); - cm.addLineClass(line, "background", BACK_CLASS); - cm._activeLine = line; + function updateActiveLine(cm, selectedLine) { + var line = cm.getLineHandleVisualStart(selectedLine); + if (cm.state.activeLine == line) return; + cm.operation(function() { + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm.state.activeLine = line; + }); + } + + function selectionChange(cm, sel) { + updateActiveLine(cm, sel.head.line); } })(); diff --git a/gulliver/js/codemirror/addon/selection/mark-selection.js b/gulliver/js/codemirror/addon/selection/mark-selection.js index d7ff30c9a..c97776e49 100644 --- a/gulliver/js/codemirror/addon/selection/mark-selection.js +++ b/gulliver/js/codemirror/addon/selection/mark-selection.js @@ -1,7 +1,8 @@ // Because sometimes you need to mark the selected *text*. // // Adds an option 'styleSelectedText' which, when enabled, gives -// selected text the CSS class "CodeMirror-selectedtext". +// selected text the CSS class given as option value, or +// "CodeMirror-selectedtext" when the value is not a string. (function() { "use strict"; @@ -9,26 +10,99 @@ CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - updateSelectedText(cm); - cm.on("cursorActivity", updateSelectedText); + cm.state.markedSelection = []; + cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; + reset(cm); + cm.on("cursorActivity", onCursorActivity); + cm.on("change", onChange); } else if (!val && prev) { - cm.off("cursorActivity", updateSelectedText); - clearSelectedText(cm); - delete cm._selectionMark; + cm.off("cursorActivity", onCursorActivity); + cm.off("change", onChange); + clear(cm); + cm.state.markedSelection = cm.state.markedSelectionStyle = null; } }); - function clearSelectedText(cm) { - if (cm._selectionMark) cm._selectionMark.clear(); + function onCursorActivity(cm) { + cm.operation(function() { update(cm); }); } - function updateSelectedText(cm) { - clearSelectedText(cm); + function onChange(cm) { + if (cm.state.markedSelection.length) + cm.operation(function() { clear(cm); }); + } - if (cm.somethingSelected()) - cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"), - {className: "CodeMirror-selectedtext"}); - else - cm._selectionMark = null; + var CHUNK_SIZE = 8; + var Pos = CodeMirror.Pos; + + function cmp(pos1, pos2) { + return pos1.line - pos2.line || pos1.ch - pos2.ch; + } + + function coverRange(cm, from, to, addAt) { + if (cmp(from, to) == 0) return; + var array = cm.state.markedSelection; + var cls = cm.state.markedSelectionStyle; + for (var line = from.line;;) { + var start = line == from.line ? from : Pos(line, 0); + var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; + var end = atEnd ? to : Pos(endLine, 0); + var mark = cm.markText(start, end, {className: cls}); + if (addAt == null) array.push(mark); + else array.splice(addAt++, 0, mark); + if (atEnd) break; + line = endLine; + } + } + + function clear(cm) { + var array = cm.state.markedSelection; + for (var i = 0; i < array.length; ++i) array[i].clear(); + array.length = 0; + } + + function reset(cm) { + clear(cm); + var from = cm.getCursor("start"), to = cm.getCursor("end"); + coverRange(cm, from, to); + } + + function update(cm) { + var from = cm.getCursor("start"), to = cm.getCursor("end"); + if (cmp(from, to) == 0) return clear(cm); + + var array = cm.state.markedSelection; + if (!array.length) return coverRange(cm, from, to); + + var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); + if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || + cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) + return reset(cm); + + while (cmp(from, coverStart.from) > 0) { + array.shift().clear(); + coverStart = array[0].find(); + } + if (cmp(from, coverStart.from) < 0) { + if (coverStart.to.line - from.line < CHUNK_SIZE) { + array.shift().clear(); + coverRange(cm, from, coverStart.to, 0); + } else { + coverRange(cm, from, coverStart.from, 0); + } + } + + while (cmp(to, coverEnd.to) < 0) { + array.pop().clear(); + coverEnd = array[array.length - 1].find(); + } + if (cmp(to, coverEnd.to) > 0) { + if (to.line - coverEnd.from.line < CHUNK_SIZE) { + array.pop().clear(); + coverRange(cm, coverEnd.from, to); + } else { + coverRange(cm, coverEnd.to, to); + } + } } })(); diff --git a/gulliver/js/codemirror/addon/tern/tern.css b/gulliver/js/codemirror/addon/tern/tern.css new file mode 100644 index 000000000..eacc2f053 --- /dev/null +++ b/gulliver/js/codemirror/addon/tern/tern.css @@ -0,0 +1,85 @@ +.CodeMirror-Tern-completion { + padding-left: 22px; + position: relative; +} +.CodeMirror-Tern-completion:before { + position: absolute; + left: 2px; + bottom: 2px; + border-radius: 50%; + font-size: 12px; + font-weight: bold; + height: 15px; + width: 15px; + line-height: 16px; + text-align: center; + color: white; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.CodeMirror-Tern-completion-unknown:before { + content: "?"; + background: #4bb; +} +.CodeMirror-Tern-completion-object:before { + content: "O"; + background: #77c; +} +.CodeMirror-Tern-completion-fn:before { + content: "F"; + background: #7c7; +} +.CodeMirror-Tern-completion-array:before { + content: "A"; + background: #c66; +} +.CodeMirror-Tern-completion-number:before { + content: "1"; + background: #999; +} +.CodeMirror-Tern-completion-string:before { + content: "S"; + background: #999; +} +.CodeMirror-Tern-completion-bool:before { + content: "B"; + background: #999; +} + +.CodeMirror-Tern-completion-guess { + color: #999; +} + +.CodeMirror-Tern-tooltip { + border: 1px solid silver; + border-radius: 3px; + color: #444; + padding: 2px 5px; + font-size: 90%; + font-family: monospace; + background-color: white; + white-space: pre-wrap; + + max-width: 40em; + position: absolute; + z-index: 10; + -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); + -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); + box-shadow: 2px 3px 5px rgba(0,0,0,.2); + + transition: opacity 1s; + -moz-transition: opacity 1s; + -webkit-transition: opacity 1s; + -o-transition: opacity 1s; + -ms-transition: opacity 1s; +} + +.CodeMirror-Tern-hint-doc { + max-width: 25em; +} + +.CodeMirror-Tern-fname { color: black; } +.CodeMirror-Tern-farg { color: #70a; } +.CodeMirror-Tern-farg-current { text-decoration: underline; } +.CodeMirror-Tern-type { color: #07c; } +.CodeMirror-Tern-fhint-guess { opacity: .7; } diff --git a/gulliver/js/codemirror/addon/tern/tern.js b/gulliver/js/codemirror/addon/tern/tern.js new file mode 100644 index 000000000..7f83c4e4c --- /dev/null +++ b/gulliver/js/codemirror/addon/tern/tern.js @@ -0,0 +1,632 @@ +// Glue code between CodeMirror and Tern. +// +// Create a CodeMirror.TernServer to wrap an actual Tern server, +// register open documents (CodeMirror.Doc instances) with it, and +// call its methods to activate the assisting functions that Tern +// provides. +// +// Options supported (all optional): +// * defs: An array of JSON definition data structures. +// * plugins: An object mapping plugin names to configuration +// options. +// * getFile: A function(name, c) that can be used to access files in +// the project that haven't been loaded yet. Simply do c(null) to +// indicate that a file is not available. +// * fileFilter: A function(value, docName, doc) that will be applied +// to documents before passing them on to Tern. +// * switchToDoc: A function(name) that should, when providing a +// multi-file view, switch the view or focus to the named file. +// * showError: A function(editor, message) that can be used to +// override the way errors are displayed. +// * completionTip: Customize the content in tooltips for completions. +// Is passed a single argument—the completion's data as returned by +// Tern—and may return a string, DOM node, or null to indicate that +// no tip should be shown. By default the docstring is shown. +// * typeTip: Like completionTip, but for the tooltips shown for type +// queries. +// * responseFilter: A function(doc, query, request, error, data) that +// will be applied to the Tern responses before treating them +// +// +// It is possible to run the Tern server in a web worker by specifying +// these additional options: +// * useWorker: Set to true to enable web worker mode. You'll probably +// want to feature detect the actual value you use here, for example +// !!window.Worker. +// * workerScript: The main script of the worker. Point this to +// wherever you are hosting worker.js from this directory. +// * workerDeps: An array of paths pointing (relative to workerScript) +// to the Acorn and Tern libraries and any Tern plugins you want to +// load. Or, if you minified those into a single script and included +// them in the workerScript, simply leave this undefined. + +(function() { + "use strict"; + // declare global: tern + + CodeMirror.TernServer = function(options) { + var self = this; + this.options = options || {}; + var plugins = this.options.plugins || (this.options.plugins = {}); + if (!plugins.doc_comment) plugins.doc_comment = true; + if (this.options.useWorker) { + this.server = new WorkerServer(this); + } else { + this.server = new tern.Server({ + getFile: function(name, c) { return getFile(self, name, c); }, + async: true, + defs: this.options.defs || [], + plugins: plugins + }); + } + this.docs = Object.create(null); + this.trackChange = function(doc, change) { trackChange(self, doc, change); }; + + this.cachedArgHints = null; + this.activeArgHints = null; + this.jumpStack = []; + }; + + CodeMirror.TernServer.prototype = { + addDoc: function(name, doc) { + var data = {doc: doc, name: name, changed: null}; + this.server.addFile(name, docValue(this, data)); + CodeMirror.on(doc, "change", this.trackChange); + return this.docs[name] = data; + }, + + delDoc: function(name) { + var found = this.docs[name]; + if (!found) return; + CodeMirror.off(found.doc, "change", this.trackChange); + delete this.docs[name]; + this.server.delFile(name); + }, + + hideDoc: function(name) { + closeArgHints(this); + var found = this.docs[name]; + if (found && found.changed) sendDoc(this, found); + }, + + complete: function(cm) { + var self = this; + CodeMirror.showHint(cm, function(cm, c) { return hint(self, cm, c); }, {async: true}); + }, + + getHint: function(cm, c) { return hint(this, cm, c); }, + + showType: function(cm, pos) { showType(this, cm, pos); }, + + updateArgHints: function(cm) { updateArgHints(this, cm); }, + + jumpToDef: function(cm) { jumpToDef(this, cm); }, + + jumpBack: function(cm) { jumpBack(this, cm); }, + + rename: function(cm) { rename(this, cm); }, + + request: function (cm, query, c, pos) { + var self = this; + var doc = findDoc(this, cm.getDoc()); + var request = buildRequest(this, doc, query, pos); + + this.server.request(request, function (error, data) { + if (!error && self.options.responseFilter) + data = self.options.responseFilter(doc, query, request, error, data); + c(error, data); + }); + } + }; + + var Pos = CodeMirror.Pos; + var cls = "CodeMirror-Tern-"; + var bigDoc = 250; + + function getFile(ts, name, c) { + var buf = ts.docs[name]; + if (buf) + c(docValue(ts, buf)); + else if (ts.options.getFile) + ts.options.getFile(name, c); + else + c(null); + } + + function findDoc(ts, doc, name) { + for (var n in ts.docs) { + var cur = ts.docs[n]; + if (cur.doc == doc) return cur; + } + if (!name) for (var i = 0;; ++i) { + n = "[doc" + (i || "") + "]"; + if (!ts.docs[n]) { name = n; break; } + } + return ts.addDoc(name, doc); + } + + function trackChange(ts, doc, change) { + var data = findDoc(ts, doc); + + var argHints = ts.cachedArgHints; + if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0) + ts.cachedArgHints = null; + + var changed = data.changed; + if (changed == null) + data.changed = changed = {from: change.from.line, to: change.from.line}; + var end = change.from.line + (change.text.length - 1); + if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end); + if (end >= changed.to) changed.to = end + 1; + if (changed.from > change.from.line) changed.from = change.from.line; + + if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() { + if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data); + }, 200); + } + + function sendDoc(ts, doc) { + ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) { + if (error) console.error(error); + else doc.changed = null; + }); + } + + // Completion + + function hint(ts, cm, c) { + ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) { + if (error) return showError(ts, cm, error); + var completions = [], after = ""; + var from = data.start, to = data.end; + if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" && + cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]") + after = "\"]"; + + for (var i = 0; i < data.completions.length; ++i) { + var completion = data.completions[i], className = typeToIcon(completion.type); + if (data.guess) className += " " + cls + "guess"; + completions.push({text: completion.name + after, + displayText: completion.name, + className: className, + data: completion}); + } + + var obj = {from: from, to: to, list: completions}; + var tooltip = null; + CodeMirror.on(obj, "close", function() { remove(tooltip); }); + CodeMirror.on(obj, "update", function() { remove(tooltip); }); + CodeMirror.on(obj, "select", function(cur, node) { + remove(tooltip); + var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc; + if (content) { + tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, + node.getBoundingClientRect().top + window.pageYOffset, content); + tooltip.className += " " + cls + "hint-doc"; + } + }); + c(obj); + }); + } + + function typeToIcon(type) { + var suffix; + if (type == "?") suffix = "unknown"; + else if (type == "number" || type == "string" || type == "bool") suffix = type; + else if (/^fn\(/.test(type)) suffix = "fn"; + else if (/^\[/.test(type)) suffix = "array"; + else suffix = "object"; + return cls + "completion " + cls + "completion-" + suffix; + } + + // Type queries + + function showType(ts, cm, pos) { + ts.request(cm, "type", function(error, data) { + if (error) return showError(ts, cm, error); + if (ts.options.typeTip) { + var tip = ts.options.typeTip(data); + } else { + var tip = elt("span", null, elt("strong", null, data.type || "not found")); + if (data.doc) + tip.appendChild(document.createTextNode(" — " + data.doc)); + if (data.url) { + tip.appendChild(document.createTextNode(" ")); + tip.appendChild(elt("a", null, "[docs]")).href = data.url; + } + } + tempTooltip(cm, tip); + }, pos); + } + + // Maintaining argument hints + + function updateArgHints(ts, cm) { + closeArgHints(ts); + + if (cm.somethingSelected()) return; + var state = cm.getTokenAt(cm.getCursor()).state; + var inner = CodeMirror.innerMode(cm.getMode(), state); + if (inner.mode.name != "javascript") return; + var lex = inner.state.lexical; + if (lex.info != "call") return; + + var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize"); + for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) { + var str = cm.getLine(line), extra = 0; + for (var pos = 0;;) { + var tab = str.indexOf("\t", pos); + if (tab == -1) break; + extra += tabSize - (tab + extra) % tabSize - 1; + pos = tab + 1; + } + ch = lex.column - extra; + if (str.charAt(ch) == "(") {found = true; break;} + } + if (!found) return; + + var start = Pos(line, ch); + var cache = ts.cachedArgHints; + if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) + return showArgHints(ts, cm, argPos); + + ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { + if (error || !data.type || !(/^fn\(/).test(data.type)) return; + ts.cachedArgHints = { + start: pos, + type: parseFnType(data.type), + name: data.exprName || data.name || "fn", + guess: data.guess, + doc: cm.getDoc() + }; + showArgHints(ts, cm, argPos); + }); + } + + function showArgHints(ts, cm, pos) { + closeArgHints(ts); + + var cache = ts.cachedArgHints, tp = cache.type; + var tip = elt("span", cache.guess ? cls + "fhint-guess" : null, + elt("span", cls + "fname", cache.name), "("); + for (var i = 0; i < tp.args.length; ++i) { + if (i) tip.appendChild(document.createTextNode(", ")); + var arg = tp.args[i]; + tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?")); + if (arg.type != "?") { + tip.appendChild(document.createTextNode(":\u00a0")); + tip.appendChild(elt("span", cls + "type", arg.type)); + } + } + tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")")); + if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype)); + var place = cm.cursorCoords(null, "page"); + ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip); + } + + function parseFnType(text) { + var args = [], pos = 3; + + function skipMatching(upto) { + var depth = 0, start = pos; + for (;;) { + var next = text.charAt(pos); + if (upto.test(next) && !depth) return text.slice(start, pos); + if (/[{\[\(]/.test(next)) ++depth; + else if (/[}\]\)]/.test(next)) --depth; + ++pos; + } + } + + // Parse arguments + if (text.charAt(pos) != ")") for (;;) { + var name = text.slice(pos).match(/^([^, \(\[\{]+): /); + if (name) { + pos += name[0].length; + name = name[1]; + } + args.push({name: name, type: skipMatching(/[\),]/)}); + if (text.charAt(pos) == ")") break; + pos += 2; + } + + var rettype = text.slice(pos).match(/^\) -> (.*)$/); + + return {args: args, rettype: rettype && rettype[1]}; + } + + // Moving to the definition of something + + function jumpToDef(ts, cm) { + function inner(varName) { + var req = {type: "definition", variable: varName || null}; + var doc = findDoc(ts, cm.getDoc()); + ts.server.request(buildRequest(ts, doc, req), function(error, data) { + if (error) return showError(ts, cm, error); + if (!data.file && data.url) { window.open(data.url); return; } + + if (data.file) { + var localDoc = ts.docs[data.file], found; + if (localDoc && (found = findContext(localDoc.doc, data))) { + ts.jumpStack.push({file: doc.name, + start: cm.getCursor("from"), + end: cm.getCursor("to")}); + moveTo(ts, doc, localDoc, found.start, found.end); + return; + } + } + showError(ts, cm, "Could not find a definition."); + }); + } + + if (!atInterestingExpression(cm)) + dialog(cm, "Jump to variable", function(name) { if (name) inner(name); }); + else + inner(); + } + + function jumpBack(ts, cm) { + var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file]; + if (!doc) return; + moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end); + } + + function moveTo(ts, curDoc, doc, start, end) { + doc.doc.setSelection(end, start); + if (curDoc != doc && ts.options.switchToDoc) { + closeArgHints(ts); + ts.options.switchToDoc(doc.name); + } + } + + // The {line,ch} representation of positions makes this rather awkward. + function findContext(doc, data) { + var before = data.context.slice(0, data.contextOffset).split("\n"); + var startLine = data.start.line - (before.length - 1); + var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length); + + var text = doc.getLine(startLine).slice(start.ch); + for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur) + text += "\n" + doc.getLine(cur); + if (text.slice(0, data.context.length) == data.context) return data; + + var cursor = doc.getSearchCursor(data.context, 0, false); + var nearest, nearestDist = Infinity; + while (cursor.findNext()) { + var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000; + if (!dist) dist = Math.abs(from.ch - start.ch); + if (dist < nearestDist) { nearest = from; nearestDist = dist; } + } + if (!nearest) return null; + + if (before.length == 1) + nearest.ch += before[0].length; + else + nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length); + if (data.start.line == data.end.line) + var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch)); + else + var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch); + return {start: nearest, end: end}; + } + + function atInterestingExpression(cm) { + var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos); + if (tok.start < pos.ch && (tok.type == "comment" || tok.type == "string")) return false; + return /\w/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1)); + } + + // Variable renaming + + function rename(ts, cm) { + var token = cm.getTokenAt(cm.getCursor()); + if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable"); + dialog(cm, "New name for " + token.string, function(newName) { + ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) { + if (error) return showError(ts, cm, error); + applyChanges(ts, data.changes); + }); + }); + } + + var nextChangeOrig = 0; + function applyChanges(ts, changes) { + var perFile = Object.create(null); + for (var i = 0; i < changes.length; ++i) { + var ch = changes[i]; + (perFile[ch.file] || (perFile[ch.file] = [])).push(ch); + } + for (var file in perFile) { + var known = ts.docs[file], chs = perFile[file];; + if (!known) continue; + chs.sort(function(a, b) { return cmpPos(b.start, a.start); }); + var origin = "*rename" + (++nextChangeOrig); + for (var i = 0; i < chs.length; ++i) { + var ch = chs[i]; + known.doc.replaceRange(ch.text, ch.start, ch.end, origin); + } + } + } + + // Generic request-building helper + + function buildRequest(ts, doc, query, pos) { + var files = [], offsetLines = 0, allowFragments = !query.fullDocs; + if (!allowFragments) delete query.fullDocs; + if (typeof query == "string") query = {type: query}; + query.lineCharPositions = true; + if (query.end == null) { + query.end = pos || doc.doc.getCursor("end"); + if (doc.doc.somethingSelected()) + query.start = doc.doc.getCursor("start"); + } + var startPos = query.start || query.end; + + if (doc.changed) { + if (doc.doc.lineCount() > bigDoc && allowFragments !== false && + doc.changed.to - doc.changed.from < 100 && + doc.changed.from <= startPos.line && doc.changed.to > query.end.line) { + files.push(getFragmentAround(doc, startPos, query.end)); + query.file = "#0"; + var offsetLines = files[0].offsetLines; + if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch); + query.end = Pos(query.end.line - offsetLines, query.end.ch); + } else { + files.push({type: "full", + name: doc.name, + text: docValue(ts, doc)}); + query.file = doc.name; + doc.changed = null; + } + } else { + query.file = doc.name; + } + for (var name in ts.docs) { + var cur = ts.docs[name]; + if (cur.changed && cur != doc) { + files.push({type: "full", name: cur.name, text: docValue(ts, cur)}); + cur.changed = null; + } + } + + return {query: query, files: files}; + } + + function getFragmentAround(data, start, end) { + var doc = data.doc; + var minIndent = null, minLine = null, endLine, tabSize = 4; + for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) { + var line = doc.getLine(p), fn = line.search(/\bfunction\b/); + if (fn < 0) continue; + var indent = CodeMirror.countColumn(line, null, tabSize); + if (minIndent != null && minIndent <= indent) continue; + minIndent = indent; + minLine = p; + } + if (minLine == null) minLine = min; + var max = Math.min(doc.lastLine(), end.line + 20); + if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize)) + endLine = max; + else for (endLine = end.line + 1; endLine < max; ++endLine) { + var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize); + if (indent <= minIndent) break; + } + var from = Pos(minLine, 0); + + return {type: "part", + name: data.name, + offsetLines: from.line, + text: doc.getRange(from, Pos(endLine, 0))}; + } + + // Generic utilities + + function cmpPos(a, b) { return a.line - b.line || a.ch - b.ch; } + + function elt(tagname, cls /*, ... elts*/) { + var e = document.createElement(tagname); + if (cls) e.className = cls; + for (var i = 2; i < arguments.length; ++i) { + var elt = arguments[i]; + if (typeof elt == "string") elt = document.createTextNode(elt); + e.appendChild(elt); + } + return e; + } + + function dialog(cm, text, f) { + if (cm.openDialog) + cm.openDialog(text + ": ", f); + else + f(prompt(text, "")); + } + + // Tooltips + + function tempTooltip(cm, content) { + var where = cm.cursorCoords(); + var tip = makeTooltip(where.right + 1, where.bottom, content); + function clear() { + if (!tip.parentNode) return; + cm.off("cursorActivity", clear); + fadeOut(tip); + } + setTimeout(clear, 1700); + cm.on("cursorActivity", clear); + } + + function makeTooltip(x, y, content) { + var node = elt("div", cls + "tooltip", content); + node.style.left = x + "px"; + node.style.top = y + "px"; + document.body.appendChild(node); + return node; + } + + function remove(node) { + var p = node && node.parentNode; + if (p) p.removeChild(node); + } + + function fadeOut(tooltip) { + tooltip.style.opacity = "0"; + setTimeout(function() { remove(tooltip); }, 1100); + } + + function showError(ts, cm, msg) { + if (ts.options.showError) + ts.options.showError(cm, msg); + else + tempTooltip(cm, String(msg)); + } + + function closeArgHints(ts) { + if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; } + } + + function docValue(ts, doc) { + var val = doc.doc.getValue(); + if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc); + return val; + } + + // Worker wrapper + + function WorkerServer(ts) { + var worker = new Worker(ts.options.workerScript); + worker.postMessage({type: "init", + defs: ts.options.defs, + plugins: ts.options.plugins, + scripts: ts.options.workerDeps}); + var msgId = 0, pending = {}; + + function send(data, c) { + if (c) { + data.id = ++msgId; + pending[msgId] = c; + } + worker.postMessage(data); + } + worker.onmessage = function(e) { + var data = e.data; + if (data.type == "getFile") { + getFile(ts, data.name, function(err, text) { + send({type: "getFile", err: String(err), text: text, id: data.id}); + }); + } else if (data.type == "debug") { + console.log(data.message); + } else if (data.id && pending[data.id]) { + pending[data.id](data.err, data.body); + delete pending[data.id]; + } + }; + worker.onerror = function(e) { + for (var id in pending) pending[id](e); + pending = {}; + }; + + this.addFile = function(name, text) { send({type: "add", name: name, text: text}); }; + this.delFile = function(name) { send({type: "del", name: name}); }; + this.request = function(body, c) { send({type: "req", body: body}, c); }; + } +})(); diff --git a/gulliver/js/codemirror/addon/tern/worker.js b/gulliver/js/codemirror/addon/tern/worker.js new file mode 100644 index 000000000..1ff63de41 --- /dev/null +++ b/gulliver/js/codemirror/addon/tern/worker.js @@ -0,0 +1,41 @@ +// declare global: tern, server + +var server; + +this.onmessage = function(e) { + var data = e.data; + switch (data.type) { + case "init": return startServer(data.defs, data.plugins, data.scripts); + case "add": return server.addFile(data.name, data.text); + case "del": return server.delFile(data.name); + case "req": return server.request(data.body, function(err, reqData) { + postMessage({id: data.id, body: reqData, err: err && String(err)}); + }); + case "getFile": + var c = pending[data.id]; + delete pending[data.id]; + return c(data.err, data.text); + default: throw new Error("Unknown message type: " + data.type); + } +}; + +var nextId = 0, pending = {}; +function getFile(file, c) { + postMessage({type: "getFile", name: file, id: ++nextId}); + pending[nextId] = c; +} + +function startServer(defs, plugins, scripts) { + if (scripts) importScripts.apply(null, scripts); + + server = new tern.Server({ + getFile: getFile, + async: true, + defs: defs, + plugins: plugins + }); +} + +var console = { + log: function(v) { postMessage({type: "debug", message: v}); } +}; diff --git a/gulliver/js/codemirror/addon/wrap/hardwrap.js b/gulliver/js/codemirror/addon/wrap/hardwrap.js new file mode 100644 index 000000000..f6d99212a --- /dev/null +++ b/gulliver/js/codemirror/addon/wrap/hardwrap.js @@ -0,0 +1,111 @@ +(function() { + "use strict"; + + var Pos = CodeMirror.Pos; + + function findParagraph(cm, pos, options) { + var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart"); + for (var start = pos.line, first = cm.firstLine(); start > first; --start) { + var line = cm.getLine(start); + if (startRE && startRE.test(line)) break; + if (!/\S/.test(line)) { ++start; break; } + } + var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd"); + for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) { + var line = cm.getLine(end); + if (endRE && endRE.test(line)) { ++end; break; } + if (!/\S/.test(line)) break; + } + return {from: start, to: end}; + } + + function findBreakPoint(text, column, wrapOn, killTrailingSpace) { + for (var at = column; at > 0; --at) + if (wrapOn.test(text.slice(at - 1, at + 1))) break; + if (at == 0) at = column; + var endOfText = at; + if (killTrailingSpace) + while (text.charAt(endOfText - 1) == " ") --endOfText; + return {from: endOfText, to: at}; + } + + function wrapRange(cm, from, to, options) { + from = cm.clipPos(from); to = cm.clipPos(to); + var column = options.column || 80; + var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/; + var killTrailing = options.killTrailingSpace !== false; + var changes = [], curLine = "", curNo = from.line; + var lines = cm.getRange(from, to, false); + if (!lines.length) return null; + var leadingSpace = lines[0].match(/^[ \t]*/)[0]; + + for (var i = 0; i < lines.length; ++i) { + var text = lines[i], oldLen = curLine.length, spaceInserted = 0; + if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) { + curLine += " "; + spaceInserted = 1; + } + var spaceTrimmed = ""; + if (i) { + spaceTrimmed = text.match(/^\s*/)[0]; + text = text.slice(spaceTrimmed.length); + } + curLine += text; + if (i) { + var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed && + findBreakPoint(curLine, column, wrapOn, killTrailing); + // If this isn't broken, or is broken at a different point, remove old break + if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) { + changes.push({text: [spaceInserted ? " " : ""], + from: Pos(curNo, oldLen), + to: Pos(curNo + 1, spaceTrimmed.length)}); + } else { + curLine = leadingSpace + text; + ++curNo; + } + } + while (curLine.length > column) { + var bp = findBreakPoint(curLine, column, wrapOn, killTrailing); + changes.push({text: ["", leadingSpace], + from: Pos(curNo, bp.from), + to: Pos(curNo, bp.to)}); + curLine = leadingSpace + curLine.slice(bp.to); + ++curNo; + } + } + if (changes.length) cm.operation(function() { + for (var i = 0; i < changes.length; ++i) { + var change = changes[i]; + cm.replaceRange(change.text, change.from, change.to); + } + }); + return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null; + } + + CodeMirror.defineExtension("wrapParagraph", function(pos, options) { + options = options || {}; + if (!pos) pos = this.getCursor(); + var para = findParagraph(this, pos, options); + return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options); + }); + + CodeMirror.defineExtension("wrapRange", function(from, to, options) { + return wrapRange(this, from, to, options || {}); + }); + + CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) { + options = options || {}; + var cm = this, paras = []; + for (var line = from.line; line <= to.line;) { + var para = findParagraph(cm, Pos(line, 0), options); + paras.push(para); + line = para.to; + } + var madeChange = false; + if (paras.length) cm.operation(function() { + for (var i = paras.length - 1; i >= 0; --i) + madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options); + }); + return madeChange; + }); +})(); diff --git a/gulliver/js/codemirror/bin/authors.sh b/gulliver/js/codemirror/bin/authors.sh new file mode 100644 index 000000000..b3ee99c6d --- /dev/null +++ b/gulliver/js/codemirror/bin/authors.sh @@ -0,0 +1,6 @@ +# Combine existing list of authors with everyone known in git, sort, add header. +tail --lines=+3 AUTHORS > AUTHORS.tmp +git log --format='%aN' >> AUTHORS.tmp +echo -e "List of CodeMirror contributors. Updated before every release.\n" > AUTHORS +sort -u AUTHORS.tmp >> AUTHORS +rm -f AUTHORS.tmp diff --git a/gulliver/js/codemirror/bin/compress b/gulliver/js/codemirror/bin/compress index d059b618b..809fbe83d 100755 --- a/gulliver/js/codemirror/bin/compress +++ b/gulliver/js/codemirror/bin/compress @@ -29,14 +29,15 @@ function help(ok) { process.exit(ok ? 0 : 1); } -var local = null, args = null, files = [], blob = ""; +var local = null, args = [], extraArgs = null, files = [], blob = ""; for (var i = 2; i < process.argv.length; ++i) { var arg = process.argv[i]; if (arg == "--local" && i + 1 < process.argv.length) { var parts = process.argv[++i].split(/\s+/); local = parts[0]; - args = parts.slice(1); + extraArgs = parts.slice(1); + if (!extraArgs.length) extraArgs = ["-c", "-m"]; } else if (arg == "--help") { help(true); } else if (arg[0] != "-") { @@ -73,7 +74,7 @@ if (files.length) { } if (local) { - require("child_process").spawn(local, args, {stdio: ["ignore", process.stdout, process.stderr]}); + require("child_process").spawn(local, args.concat(extraArgs), {stdio: ["ignore", process.stdout, process.stderr]}); } else { var data = new Buffer("js_code=" + require("querystring").escape(blob), "utf8"); var req = require("http").request({ diff --git a/gulliver/js/codemirror/bin/lint b/gulliver/js/codemirror/bin/lint new file mode 100644 index 000000000..4f70994c5 --- /dev/null +++ b/gulliver/js/codemirror/bin/lint @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +var lint = require("../test/lint/lint"), + path = require("path"); + +if (process.argv.length > 2) { + lint.checkDir(process.argv[2]); +} else { + process.chdir(path.resolve(__dirname, "..")); + lint.checkDir("lib"); + lint.checkDir("mode"); + lint.checkDir("addon"); + lint.checkDir("keymap"); +} + +process.exit(lint.success() ? 0 : 1); diff --git a/gulliver/js/codemirror/bin/release b/gulliver/js/codemirror/bin/release new file mode 100644 index 000000000..f92ab006d --- /dev/null +++ b/gulliver/js/codemirror/bin/release @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +var fs = require("fs"), child = require("child_process"); + +var number, bumpOnly; + +for (var i = 2; i < process.argv.length; i++) { + if (process.argv[i] == "-bump") bumpOnly = true; + else if (/^\d+\.\d+\.\d+$/.test(process.argv[i])) number = process.argv[i]; + else { console.log("Bogus command line arg: " + process.argv[i]); process.exit(1); } +} + +if (!number) { console.log("Must give a version"); process.exit(1); } + +function rewrite(file, f) { + fs.writeFileSync(file, f(fs.readFileSync(file, "utf8")), "utf8"); +} + +rewrite("lib/codemirror.js", function(lib) { + return lib.replace(/CodeMirror\.version = "\d+\.\d+\.\d+"/, + "CodeMirror.version = \"" + number + "\""); +}); +rewrite("package.json", function(pack) { + return pack.replace(/"version":"\d+\.\d+\.\d+"/, "\"version\":\"" + number + "\""); +}); + +if (bumpOnly) process.exit(0); + +child.exec("bash bin/authors.sh", function(){}); + +var simple = number.slice(0, number.lastIndexOf(".")); + +rewrite("doc/compress.html", function(cmp) { + return cmp.replace(/\n "); +}); + +rewrite("index.html", function(index) { + return index.replace(/version 3.20<\/strong>/, + "version " + simple + ""); +}); diff --git a/gulliver/js/codemirror/bin/source-highlight b/gulliver/js/codemirror/bin/source-highlight new file mode 100644 index 000000000..7596ed776 --- /dev/null +++ b/gulliver/js/codemirror/bin/source-highlight @@ -0,0 +1,61 @@ +#!/usr/bin/env node + +// Simple command-line code highlighting tool. Reads code from stdin, +// spits html to stdout. For example: +// +// echo 'function foo(a) { return a; }' | bin/source-highlight -s javascript +// bin/source-highlight -s + +var fs = require("fs"); + +CodeMirror = require("../addon/runmode/runmode.node.js"); +require("../mode/meta.js"); + +var sPos = process.argv.indexOf("-s"); +if (sPos == -1 || sPos == process.argv.length - 1) { + console.error("Usage: source-highlight -s language"); + process.exit(1); +} +var lang = process.argv[sPos + 1].toLowerCase(), modeName = lang; +CodeMirror.modeInfo.forEach(function(info) { + if (info.mime == lang) { + modeName = info.mode; + } else if (info.name.toLowerCase() == lang) { + modeName = info.mode; + lang = info.mime; + } +}); + +function ensureMode(name) { + if (CodeMirror.modes[name] || name == "null") return; + try { + require("../mode/" + name + "/" + name + ".js"); + } catch(e) { + console.error("Could not load mode " + name + "."); + process.exit(1); + } + var obj = CodeMirror.modes[name]; + if (obj.dependencies) obj.dependencies.forEach(ensureMode); +} +ensureMode(modeName); + +function esc(str) { + return str.replace(/[<&]/, function(ch) { return ch == "&" ? "&" : "<"; }); +} + +var code = fs.readFileSync("/dev/stdin", "utf8"); +var curStyle = null, accum = ""; +function flush() { + if (curStyle) process.stdout.write("" + esc(accum) + ""); + else process.stdout.write(esc(accum)); +} + +CodeMirror.runMode(code, lang, function(text, style) { + if (style != curStyle) { + flush(); + curStyle = style; accum = text; + } else { + accum += text; + } +}); +flush(); diff --git a/gulliver/js/codemirror/bower.json b/gulliver/js/codemirror/bower.json new file mode 100644 index 000000000..66e049dfb --- /dev/null +++ b/gulliver/js/codemirror/bower.json @@ -0,0 +1,15 @@ +{ + "name": "CodeMirror", + "main": ["lib/codemirror.js", "lib/codemirror.css"], + "ignore": [ + "**/.*", + "node_modules", + "components", + "bin", + "demo", + "doc", + "test", + "index.html", + "package.json" + ] +} diff --git a/gulliver/js/codemirror/demo/activeline.html b/gulliver/js/codemirror/demo/activeline.html index b0ea9b907..14851c35e 100644 --- a/gulliver/js/codemirror/demo/activeline.html +++ b/gulliver/js/codemirror/demo/activeline.html @@ -1,23 +1,32 @@ - - - - CodeMirror: Active Line Demo - - - - - - - - -

CodeMirror: Active Line Demo

+ @@ -83,5 +83,4 @@ function fillEditor() {

- - + diff --git a/gulliver/js/codemirror/demo/buffers.html b/gulliver/js/codemirror/demo/buffers.html index bfd8248e4..951209cad 100644 --- a/gulliver/js/codemirror/demo/buffers.html +++ b/gulliver/js/codemirror/demo/buffers.html @@ -1,20 +1,32 @@ - - - - CodeMirror: Multiple Buffer & Split View Demo - - - - - - - - -

CodeMirror: Multiple Buffer & Split View Demo

+ + +
+

Multiple Buffer & Split View Demo

+
@@ -94,5 +106,4 @@ selectBuffer(ed_bot, "js"); using swapDoc to use a single editor to display multiple documents.

- - +
diff --git a/gulliver/js/codemirror/demo/changemode.html b/gulliver/js/codemirror/demo/changemode.html index 364c5cdb0..9ea99b2d0 100644 --- a/gulliver/js/codemirror/demo/changemode.html +++ b/gulliver/js/codemirror/demo/changemode.html @@ -1,22 +1,32 @@ - - - - CodeMirror: Mode-Changing Demo - - - - - - - - -

CodeMirror: Mode-Changing demo

+ + +
+

Close-Tag Demo

+
- - +
diff --git a/gulliver/js/codemirror/demo/complete.html b/gulliver/js/codemirror/demo/complete.html index 02ce65888..44b3b2db7 100644 --- a/gulliver/js/codemirror/demo/complete.html +++ b/gulliver/js/codemirror/demo/complete.html @@ -1,20 +1,32 @@ - - - - CodeMirror: Autocomplete Demo - - - - - - - - - -

CodeMirror: Autocomplete demo

-
-

Press ctrl-space to activate autocompletion. See -the code (here -and here) to figure out -how it works.

+

Press ctrl-space to activate autocompletion. Built +on top of the show-hint +and javascript-hint +addons.

- - + diff --git a/gulliver/js/codemirror/demo/completePHP.html b/gulliver/js/codemirror/demo/completePHP.html deleted file mode 100644 index 9fcd486c0..000000000 --- a/gulliver/js/codemirror/demo/completePHP.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - CodeMirror: Autocomplete Demo - - - - - - - - - - - - - - - - -

CodeMirror: Autocomplete demo

- -
- - - -

Press ctrl-space to activate autocompletion. See -the code (here -and here) to figure out -how it works.

- - - - diff --git a/gulliver/js/codemirror/demo/emacs.html b/gulliver/js/codemirror/demo/emacs.html index b37a46b04..5b622a999 100644 --- a/gulliver/js/codemirror/demo/emacs.html +++ b/gulliver/js/codemirror/demo/emacs.html @@ -1,22 +1,38 @@ - - - - CodeMirror: Emacs bindings demo - - - - - - - - -

CodeMirror: Emacs bindings demo

+ -
HTML:
- - + + + + + + + + + + + + + +
+

Code Folding Demo

+
+
JavaScript:
+
+
HTML:
+
+
+ - - + +
+ diff --git a/gulliver/js/codemirror/demo/fullscreen.html b/gulliver/js/codemirror/demo/fullscreen.html index 2709ebb4b..827d55d0c 100644 --- a/gulliver/js/codemirror/demo/fullscreen.html +++ b/gulliver/js/codemirror/demo/fullscreen.html @@ -1,28 +1,32 @@ - - - - CodeMirror: Full Screen Editing - - - - - - - - -

CodeMirror: Full Screen Editing

+CodeMirror: Full Screen Editing + + -
-

Press F11 when cursor is in the editor to toggle full screen editing. Esc can also be used to exit full screen editing.

- - +

Demonstration of + the fullscreen + addon. Press F11 when cursor is in the editor to + toggle full screen editing. Esc can also be used + to exit full screen editing.

+ diff --git a/gulliver/js/codemirror/demo/hardwrap.html b/gulliver/js/codemirror/demo/hardwrap.html new file mode 100644 index 000000000..598bf957d --- /dev/null +++ b/gulliver/js/codemirror/demo/hardwrap.html @@ -0,0 +1,72 @@ + + +CodeMirror: Hard-wrapping Demo + + + + + + + + + + +
+

Hard-wrapping Demo

+
+ +

Demonstration of +the hardwrap addon. +The above editor has its change event hooked up to +the wrapParagraphsInRange method, so that the paragraphs +are reflown as you are typing.

+ + + +
diff --git a/gulliver/js/codemirror/demo/html5complete.html b/gulliver/js/codemirror/demo/html5complete.html index 5091354ae..94ced527d 100644 --- a/gulliver/js/codemirror/demo/html5complete.html +++ b/gulliver/js/codemirror/demo/html5complete.html @@ -1,92 +1,56 @@ - - - - CodeMirror: Close-Tag Demo - - - - - - - - - - - - - - - - -

HTML5 code completation demo

+ + + CodeMirror: HTML completion demo + + + + + + + + + + + + + + + + + + -
- - + + diff --git a/gulliver/js/codemirror/demo/indentwrap.html b/gulliver/js/codemirror/demo/indentwrap.html index c367c945d..efdba5d59 100644 --- a/gulliver/js/codemirror/demo/indentwrap.html +++ b/gulliver/js/codemirror/demo/indentwrap.html @@ -1,21 +1,32 @@ - - - - CodeMirror: Indented wrapped line demo - - - - - - - -

CodeMirror: Indented wrapped line demo

+