From 2c28fa7643b168006883a81a2421012d357a22d6 Mon Sep 17 00:00:00 2001 From: Jerome Mouneyrac Date: Fri, 7 Oct 2011 14:21:41 +0800 Subject: [PATCH 01/67] MDL-29435 SOAP/XMLRPC server return debuginfo into the exception message when DEBUG >= NORMAL --- lib/zend/readme_moodle.txt | 1 + webservice/soap/locallib.php | 44 +++++++++++++++++++++++++++++++++- webservice/xmlrpc/locallib.php | 37 +++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/lib/zend/readme_moodle.txt b/lib/zend/readme_moodle.txt index 6e50e27f3a9b2..5c4fe008705e2 100644 --- a/lib/zend/readme_moodle.txt +++ b/lib/zend/readme_moodle.txt @@ -7,4 +7,5 @@ Do not use outside of our /webservice/* or mnet !! Changes: * lots of files removed * small fix to error reporting in reflection (MDL-21460, ZF-8980) +* SOAP and XMLRPC servers overwrite the fault() functions diff --git a/webservice/soap/locallib.php b/webservice/soap/locallib.php index 426b05ad20ec0..e2d3f4fabfe2f 100644 --- a/webservice/soap/locallib.php +++ b/webservice/soap/locallib.php @@ -24,6 +24,43 @@ */ require_once("$CFG->dirroot/webservice/lib.php"); +require_once 'Zend/Soap/Server.php'; + +/** + * The Zend XMLRPC server but with a fault that returns debuginfo + */ +class moodle_zend_soap_server extends Zend_Soap_Server { + + /** + * Generate a server fault + * + * Note that the arguments are reverse to those of SoapFault. + * + * Moodle note: the difference with the Zend server is that we throw a SoapFault exception + * with the debuginfo integrated to the exception message when DEBUG >= NORMAL + * + * If an exception is passed as the first argument, its message and code + * will be used to create the fault object if it has been registered via + * {@Link registerFaultException()}. + * + * @link http://www.w3.org/TR/soap12-part1/#faultcodes + * @param string|Exception $fault + * @param string $code SOAP Fault Codes + * @return SoapFault + */ + public function fault($fault = null, $code = "Receiver") + { + //intercept any exceptions with debug info and transform it in Moodle exception + if ($fault instanceof Exception) { + //add the debuginfo to the exception message if debuginfo must be returned + if (debugging() and isset($fault->debuginfo)) { + $fault = new SoapFault('Receiver', $fault->getMessage() . ' | DEBUG INFO: ' . $fault->debuginfo); + } + } + + return parent::fault($fault, $code); + } +} /** * SOAP service server implementation. @@ -43,7 +80,7 @@ public function __construct($authmethod) { if (optional_param('wsdl', 0, PARAM_BOOL)) { parent::__construct($authmethod, 'Zend_Soap_AutoDiscover'); } else { - parent::__construct($authmethod, 'Zend_Soap_Server'); + parent::__construct($authmethod, 'moodle_zend_soap_server'); } $this->wsname = 'soap'; } @@ -79,6 +116,11 @@ protected function init_zend_server() { $this->zend_server->registerFaultException('webservice_parameter_exception'); $this->zend_server->registerFaultException('invalid_parameter_exception'); $this->zend_server->registerFaultException('invalid_response_exception'); + //when DEBUG >= NORMAL then the thrown exceptions are "casted" into a PHP SoapFault expception + //in order to diplay the $debuginfo (see moodle_zend_soap_server class - MDL-29435) + if (debugging()) { + $this->zend_server->registerFaultException('SoapFault'); + } } } diff --git a/webservice/xmlrpc/locallib.php b/webservice/xmlrpc/locallib.php index 0fbcee7731da7..eeab1895c0e2d 100644 --- a/webservice/xmlrpc/locallib.php +++ b/webservice/xmlrpc/locallib.php @@ -24,6 +24,36 @@ */ require_once("$CFG->dirroot/webservice/lib.php"); +require_once 'Zend/XmlRpc/Server.php'; + +/** + * The Zend XMLRPC server but with a fault that return debuginfo + */ +class moodle_zend_xmlrpc_server extends Zend_XmlRpc_Server { + + /** + * Raise an xmlrpc server fault + * + * Moodle note: the difference with the Zend server is that we throw a plain PHP Exception + * with the debuginfo integrated to the exception message when DEBUG >= NORMAL + * + * @param string|Exception $fault + * @param int $code + * @return Zend_XmlRpc_Server_Fault + */ + public function fault($fault = null, $code = 404) + { + //intercept any exceptions with debug info and transform it in Moodle exception + if ($fault instanceof Exception) { + //add the debuginfo to the exception message if debuginfo must be returned + if (debugging() and isset($fault->debuginfo)) { + $fault = new Exception($fault->getMessage() . ' | DEBUG INFO: ' . $fault->debuginfo, 0); + } + } + + return parent::fault($fault, $code); + } +} /** * XML-RPC service server implementation. @@ -36,7 +66,7 @@ class webservice_xmlrpc_server extends webservice_zend_server { */ public function __construct($authmethod) { require_once 'Zend/XmlRpc/Server.php'; - parent::__construct($authmethod, 'Zend_XmlRpc_Server'); + parent::__construct($authmethod, 'moodle_zend_xmlrpc_server'); $this->wsname = 'xmlrpc'; } @@ -48,6 +78,11 @@ protected function init_zend_server() { parent::init_zend_server(); // this exception indicates request failed Zend_XmlRpc_Server_Fault::attachFaultException('moodle_exception'); + //when DEBUG >= NORMAL then the thrown exceptions are "casted" into a plain PHP Exception class + //in order to display the $debuginfo (see moodle_zend_xmlrpc_server class - MDL-29435) + if (debugging()) { + Zend_XmlRpc_Server_Fault::attachFaultException('Exception'); + } } } From 9360256937dc907dcd9eb6b79f99bda4d4804fb2 Mon Sep 17 00:00:00 2001 From: Jerome Mouneyrac Date: Fri, 7 Oct 2011 16:06:19 +0800 Subject: [PATCH 02/67] MDL-29277 when validate externallib function parameters or return values, build the path to the error into the debuginfo. This patch also revert the exception to the original invalid_parameter_exception and invalid_response_exception --- lang/en/debug.php | 4 +-- lang/en/webservice.php | 9 ------ lib/externallib.php | 59 +++++++++++++++++++++++------------- lib/setuplib.php | 6 ++-- webservice/soap/locallib.php | 2 +- 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/lang/en/debug.php b/lang/en/debug.php index f951261939d54..a7e25638dce5e 100644 --- a/lang/en/debug.php +++ b/lang/en/debug.php @@ -35,8 +35,8 @@ $string['erroroccur'] = 'An error has occurred during this process'; $string['invalidarraysize'] = 'Incorrect size of arrays in params of {$a}'; $string['invalideventdata'] = 'Incorrect eventadata submitted: {$a}'; -$string['invalidparameter'] = 'Invalid parameter value detected, execution can not continue.'; -$string['invalidresponse'] = 'Invalid response value detected, execution can not continue.'; +$string['invalidparameter'] = 'Invalid parameter value detected'; +$string['invalidresponse'] = 'Invalid response value detected'; $string['missingconfigversion'] = 'Config table does not contain version, can not continue, sorry.'; $string['modulenotexist'] = '{$a} module doesn\'t exist'; $string['morethanonerecordinfetch'] = 'Found more than one record in fetch() !'; diff --git a/lang/en/webservice.php b/lang/en/webservice.php index d4f16bd2d4f1f..075ca231585ab 100644 --- a/lang/en/webservice.php +++ b/lang/en/webservice.php @@ -79,17 +79,8 @@ $string['errorcodes'] = 'Error message'; $string['errorcoursecontextnotvalid'] = 'You cannot execute functions in the course context (course id:{$a->courseid}). The context error message was: {$a->message}'; $string['errorinvalidparam'] = 'The param "{$a}" is invalid.'; -$string['errorinvalidparamsapi'] = 'Invalid external api parameter'; -$string['errorinvalidparamsdesc'] = 'Invalid external api description'; -$string['errorinvalidresponseapi'] = 'Invalid external api response'; -$string['errorinvalidresponsedesc'] = 'Invalid external api response description'; -$string['errormissingkey'] = 'Missing required key in single structure: {$a}'; $string['errornotemptydefaultparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. The default can only be empty array. Check web service description.'; -$string['erroronlyarray'] = 'Only arrays accepted.'; $string['erroroptionalparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. It can not be set as VALUE_OPTIONAL. Check web service description.'; -$string['errorresponsemissingkey'] = 'Error in response - Missing following required key in a single structure: {$a}'; -$string['errorscalartype'] = 'Scalar type expected, array or object received.'; -$string['errorunexpectedkey'] = 'Unexpected keys ({$a}) detected in parameter array.'; $string['execute'] = 'Execute'; $string['executewarnign'] = 'WARNING: If you press execute your database will be modified and changes can not be reverted automatically!'; $string['externalservice'] = 'External service'; diff --git a/lib/externallib.php b/lib/externallib.php index 0efc009655c3e..eb6a996de4b19 100644 --- a/lib/externallib.php +++ b/lib/externallib.php @@ -149,7 +149,7 @@ public static function set_timeout($seconds=360) { public static function validate_parameters(external_description $description, $params) { if ($description instanceof external_value) { if (is_array($params) or is_object($params)) { - throw new invalid_parameter_exception(get_string('errorscalartype', 'webservice')); + throw new invalid_parameter_exception('Scalar type expected, array or object received.'); } if ($description->type == PARAM_BOOL) { @@ -158,31 +158,37 @@ public static function validate_parameters(external_description $description, $p return (bool)$params; } } - return validate_param($params, $description->type, $description->allownull, get_string('errorinvalidparamsapi', 'webservice')); + $debuginfo = 'Invalid external api parameter: the value is "' . $params . + '", the server was expecting "' . $description->type . '" type'; + return validate_param($params, $description->type, $description->allownull, $debuginfo); } else if ($description instanceof external_single_structure) { if (!is_array($params)) { - throw new invalid_parameter_exception(get_string('erroronlyarray', 'webservice')); + throw new invalid_parameter_exception('Only arrays accepted. The bad value is: \'' + . print_r($params, true) . '\''); } $result = array(); foreach ($description->keys as $key=>$subdesc) { if (!array_key_exists($key, $params)) { if ($subdesc->required == VALUE_REQUIRED) { - throw new invalid_parameter_exception(get_string('errormissingkey', 'webservice', $key)); + throw new invalid_parameter_exception('Missing required key in single structure: '. $key); } if ($subdesc->required == VALUE_DEFAULT) { try { $result[$key] = self::validate_parameters($subdesc, $subdesc->default); } catch (invalid_parameter_exception $e) { - throw new webservice_parameter_exception('invalidextparam',$key); + //we are only interested by exceptions returned by validate_param() and validate_parameters() + //(in order to build the path to the faulty attribut) + throw new invalid_parameter_exception($key." => ".$e->getMessage() . ': ' .$e->debuginfo); } } } else { try { $result[$key] = self::validate_parameters($subdesc, $params[$key]); } catch (invalid_parameter_exception $e) { - //it's ok to display debug info as here the information is useful for ws client/dev - throw new webservice_parameter_exception('invalidextparam',$key." (".$e->debuginfo.")"); + //we are only interested by exceptions returned by validate_param() and validate_parameters() + //(in order to build the path to the faulty attribut) + throw new invalid_parameter_exception($key." => ".$e->getMessage() . ': ' .$e->debuginfo); } } unset($params[$key]); @@ -193,13 +199,14 @@ public static function validate_parameters(external_description $description, $p foreach($params as $key => $value) { $keys .= $key . ','; } - throw new invalid_parameter_exception(get_string('errorunexpectedkey', 'webservice', $keys)); + throw new invalid_parameter_exception('Unexpected keys (' . $keys . ') detected in parameter array.'); } return $result; } else if ($description instanceof external_multiple_structure) { if (!is_array($params)) { - throw new invalid_parameter_exception(get_string('erroronlyarray', 'webservice')); + throw new invalid_parameter_exception('Only arrays accepted. The bad value is: \'' + . print_r($params, true) . '\''); } $result = array(); foreach ($params as $param) { @@ -208,7 +215,7 @@ public static function validate_parameters(external_description $description, $p return $result; } else { - throw new invalid_parameter_exception(get_string('errorinvalidparamsdesc', 'webservice')); + throw new invalid_parameter_exception('Invalid external api description'); } } @@ -225,7 +232,7 @@ public static function validate_parameters(external_description $description, $p public static function clean_returnvalue(external_description $description, $response) { if ($description instanceof external_value) { if (is_array($response) or is_object($response)) { - throw new invalid_response_exception(get_string('errorscalartype', 'webservice')); + throw new invalid_response_exception('Scalar type expected, array or object received.'); } if ($description->type == PARAM_BOOL) { @@ -234,33 +241,42 @@ public static function clean_returnvalue(external_description $description, $res return (bool)$response; } } - return validate_param($response, $description->type, $description->allownull, get_string('errorinvalidresponseapi', 'webservice')); + $debuginfo = 'Invalid external api response: the value is "' . $response . + '", the server was expecting "' . $description->type . '" type'; + try { + return validate_param($response, $description->type, $description->allownull, $debuginfo); + } catch (invalid_parameter_exception $e) { + //proper exception name, to be recursively catched to build the path to the faulty attribut + throw new invalid_response_exception($e->debuginfo); + } } else if ($description instanceof external_single_structure) { if (!is_array($response)) { - throw new invalid_response_exception(get_string('erroronlyarray', 'webservice')); + throw new invalid_response_exception('Only arrays accepted. The bad value is: \'' . + print_r($response, true) . '\''); } $result = array(); foreach ($description->keys as $key=>$subdesc) { if (!array_key_exists($key, $response)) { if ($subdesc->required == VALUE_REQUIRED) { - throw new webservice_parameter_exception('errorresponsemissingkey', $key); + throw new invalid_response_exception('Error in response - Missing following required key in a single structure: ' . $key); } if ($subdesc instanceof external_value) { if ($subdesc->required == VALUE_DEFAULT) { try { $result[$key] = self::clean_returnvalue($subdesc, $subdesc->default); - } catch (Exception $e) { - throw new webservice_parameter_exception('invalidextresponse',$key." (".$e->debuginfo.")"); + } catch (invalid_response_exception $e) { + //build the path to the faulty attribut + throw new invalid_response_exception($key." => ".$e->getMessage() . ': ' . $e->debuginfo); } } } } else { try { $result[$key] = self::clean_returnvalue($subdesc, $response[$key]); - } catch (Exception $e) { - //it's ok to display debug info as here the information is useful for ws client/dev - throw new webservice_parameter_exception('invalidextresponse',$key." (".$e->debuginfo.")"); + } catch (invalid_response_exception $e) { + //build the path to the faulty attribut + throw new invalid_response_exception($key." => ".$e->getMessage() . ': ' . $e->debuginfo); } } unset($response[$key]); @@ -270,7 +286,8 @@ public static function clean_returnvalue(external_description $description, $res } else if ($description instanceof external_multiple_structure) { if (!is_array($response)) { - throw new invalid_response_exception(get_string('erroronlyarray', 'webservice')); + throw new invalid_response_exception('Only arrays accepted. The bad value is: \'' . + print_r($response, true) . '\''); } $result = array(); foreach ($response as $param) { @@ -279,7 +296,7 @@ public static function clean_returnvalue(external_description $description, $res return $result; } else { - throw new invalid_response_exception(get_string('errorinvalidresponsedesc', 'webservice')); + throw new invalid_response_exception('Invalid external api response description'); } } diff --git a/lib/setuplib.php b/lib/setuplib.php index f45221b8b547d..1abf90b6bd939 100644 --- a/lib/setuplib.php +++ b/lib/setuplib.php @@ -139,7 +139,7 @@ function __construct($debuginfo) { /** * Web service parameter exception class - * + * @deprecated - use moodle exception instead * This exception must be thrown to the web service client when a web service parameter is invalid * The error string is gotten from webservice.php */ @@ -149,8 +149,8 @@ class webservice_parameter_exception extends moodle_exception { * @param string $errorcode The name of the string from webservice.php to print * @param string $a The name of the parameter */ - function __construct($errorcode=null, $a = '') { - parent::__construct($errorcode, 'webservice', '', $a, null); + function __construct($errorcode=null, $a = '', $debuginfo = null) { + parent::__construct($errorcode, 'webservice', '', $a, $debuginfo); } } diff --git a/webservice/soap/locallib.php b/webservice/soap/locallib.php index 426b05ad20ec0..a9437a8cb8a4b 100644 --- a/webservice/soap/locallib.php +++ b/webservice/soap/locallib.php @@ -76,7 +76,7 @@ protected function init_zend_server() { $this->zend_server->setReturnResponse(true); //TODO: the error handling in Zend Soap server is useless, XML-RPC is much, much better :-( $this->zend_server->registerFaultException('moodle_exception'); - $this->zend_server->registerFaultException('webservice_parameter_exception'); + $this->zend_server->registerFaultException('webservice_parameter_exception'); //deprecated - kept for backward compatibility $this->zend_server->registerFaultException('invalid_parameter_exception'); $this->zend_server->registerFaultException('invalid_response_exception'); } From b438bce9498321d67b02307c3f831dd9a01afc8e Mon Sep 17 00:00:00 2001 From: Jason Fowler Date: Wed, 5 Oct 2011 16:20:50 +0800 Subject: [PATCH 03/67] MDL-29496 Search, Course - Code applied from patch to allow courses with a blank summary to be found in searchs - Thanks to Andrew Nicols for the patch --- lib/datalib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/datalib.php b/lib/datalib.php index 478156cd71232..503462708fb3c 100644 --- a/lib/datalib.php +++ b/lib/datalib.php @@ -717,7 +717,7 @@ function get_courses_search($searchterms, $sort='fullname ASC', $page=0, $record $params = array(); $i = 0; - $concat = $DB->sql_concat('c.summary', "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname'); + $concat = $DB->sql_concat("COALESCE(c.summary, ". $DB->sql_empty() .")", "' '", 'c.fullname', "' '", 'c.idnumber', "' '", 'c.shortname'); foreach ($searchterms as $searchterm) { $i++; From cc9fc649a155be2571daec7eded1a88418f11899 Mon Sep 17 00:00:00 2001 From: Henning Bostelmann Date: Sat, 8 Oct 2011 23:02:52 +0100 Subject: [PATCH 04/67] MDL-28219 QE2 adaptive behaviour: fix scores and penalties --- question/behaviour/adaptive/behaviour.php | 73 +++-- question/behaviour/adaptive/renderer.php | 16 +- .../adaptive/simpletest/testwalkthrough.php | 309 +++++++++++++++++- .../simpletest/testwalkthrough.php | 7 +- question/engine/questionattempt.php | 15 + question/type/numerical/question.php | 2 +- 6 files changed, 379 insertions(+), 43 deletions(-) diff --git a/question/behaviour/adaptive/behaviour.php b/question/behaviour/adaptive/behaviour.php index 5bcee3be11774..c663b52226886 100644 --- a/question/behaviour/adaptive/behaviour.php +++ b/question/behaviour/adaptive/behaviour.php @@ -127,16 +127,24 @@ public function process_submit(question_attempt_pending_step $pendingstep) { return $status; } + $prevstep = $this->qa->get_last_step_with_behaviour_var('_try'); + $prevresponse = $prevstep->get_qt_data(); $prevtries = $this->qa->get_last_behaviour_var('_try', 0); $prevbest = $pendingstep->get_fraction(); if (is_null($prevbest)) { $prevbest = 0; } + if ($this->question->is_same_response($response, $prevresponse)) { + return question_attempt::DISCARD; + } + list($fraction, $state) = $this->question->grade_response($response); $pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries))); - if ($state == question_state::$gradedright) { + if ($prevstep->get_state() == question_state::$complete) { + $pendingstep->set_state(question_state::$complete); + } else if ($state == question_state::$gradedright) { $pendingstep->set_state(question_state::$complete); } else { $pendingstep->set_state(question_state::$todo); @@ -153,32 +161,59 @@ public function process_finish(question_attempt_pending_step $pendingstep) { return question_attempt::DISCARD; } - $laststep = $this->qa->get_last_step(); - $response = $laststep->get_qt_data(); - if (!$this->question->is_gradable_response($response)) { - $pendingstep->set_state(question_state::$gaveup); - return question_attempt::KEEP; - } - $prevtries = $this->qa->get_last_behaviour_var('_try', 0); - $prevbest = $pendingstep->get_fraction(); + $prevbest = $this->qa->get_fraction(); if (is_null($prevbest)) { $prevbest = 0; } - if ($laststep->has_behaviour_var('_try')) { - // Last answer was graded, we want to regrade it. Otherwise the answer - // has changed, and we are grading a new try. - $prevtries -= 1; - } + $laststep = $this->qa->get_last_step(); + $response = $laststep->get_qt_data(); + if (!$this->question->is_gradable_response($response)) { + $state = question_state::$gaveup; + $fraction = 0; + } else { - list($fraction, $state) = $this->question->grade_response($response); + if ($laststep->has_behaviour_var('_try')) { + // Last answer was graded, we want to regrade it. Otherwise the answer + // has changed, and we are grading a new try. + $prevtries -= 1; + } + + list($fraction, $state) = $this->question->grade_response($response); + + $pendingstep->set_behaviour_var('_try', $prevtries + 1); + $pendingstep->set_behaviour_var('_rawfraction', $fraction); + $pendingstep->set_new_response_summary($this->question->summarise_response($response)); + } - $pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries))); $pendingstep->set_state($state); - $pendingstep->set_behaviour_var('_try', $prevtries + 1); - $pendingstep->set_behaviour_var('_rawfraction', $fraction); - $pendingstep->set_new_response_summary($this->question->summarise_response($response)); + $pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries))); return question_attempt::KEEP; } + + /** + * Got the most recently graded step. This is mainly intended for use by the + * renderer. + * @return question_attempt_step the most recently graded step. + */ + public function get_graded_step() { + $step = $this->qa->get_last_step_with_behaviour_var('_try'); + if ($step->has_behaviour_var('_try')) { + return $step; + } else { + return null; + } + } + + /** + * Determine whether a question state represents an "improvable" result, + * that is, whether the user can still improve their score. + * + * @param question_state $state the question state. + * @return bool whether the state is improvable + */ + public function is_state_improvable(question_state $state) { + return $state == question_state::$todo; + } } diff --git a/question/behaviour/adaptive/renderer.php b/question/behaviour/adaptive/renderer.php index da1a632905073..f5a984e91d905 100644 --- a/question/behaviour/adaptive/renderer.php +++ b/question/behaviour/adaptive/renderer.php @@ -36,13 +36,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qbehaviour_adaptive_renderer extends qbehaviour_renderer { - protected function get_graded_step(question_attempt $qa) { - foreach ($qa->get_reverse_step_iterator() as $step) { - if ($step->has_behaviour_var('_try')) { - return $step; - } - } - } public function controls(question_attempt $qa, question_display_options $options) { return $this->submit_button($qa, $options); @@ -51,7 +44,7 @@ public function controls(question_attempt $qa, question_display_options $options public function feedback(question_attempt $qa, question_display_options $options) { // Try to find the last graded step. - $gradedstep = $this->get_graded_step($qa); + $gradedstep = $qa->get_behaviour()->get_graded_step($qa); if (is_null($gradedstep) || $qa->get_max_mark() == 0 || $options->marks < question_display_options::MARK_AND_MAX) { return ''; @@ -100,14 +93,13 @@ protected function penalty_info(question_attempt $qa, $mark, } $output = ''; - // print details of grade adjustment due to penalties + // Print details of grade adjustment due to penalties if ($mark->raw != $mark->cur) { $output .= ' ' . get_string('gradingdetailsadjustment', 'qbehaviour_adaptive', $mark); } - // print info about new penalty - // penalty is relevant only if the answer is not correct and further attempts are possible - if (!$qa->get_state()->is_finished()) { + // Print information about any new penalty, only relevant if the answer can be improved. + if ($qa->get_behaviour()->is_state_improvable($qa->get_state())) { $output .= ' ' . get_string('gradingdetailspenalty', 'qbehaviour_adaptive', format_float($qa->get_question()->penalty, $options->markdp)); } diff --git a/question/behaviour/adaptive/simpletest/testwalkthrough.php b/question/behaviour/adaptive/simpletest/testwalkthrough.php index 26eb6411a7126..6a6e27c6b0d43 100644 --- a/question/behaviour/adaptive/simpletest/testwalkthrough.php +++ b/question/behaviour/adaptive/simpletest/testwalkthrough.php @@ -39,6 +39,18 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_base { + protected function get_contains_penalty_info_expectation($penalty) { + $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive', + format_float($penalty, $this->displayoptions->markdp)); + return new PatternExpectation('/'.preg_quote($penaltyinfo).'/'); + } + + protected function get_does_not_contain_penalty_info_expectation() { + $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive', 'XXXXX'); + $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo)).'/'; + return new NoPatternExpectation($penaltypattern); + } + public function test_adaptive_multichoice() { // Create a multiple choice, single response question. @@ -72,7 +84,8 @@ public function test_adaptive_multichoice() { $this->get_contains_mc_radio_expectation($wrongindex, true, true), $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), $this->get_contains_mc_radio_expectation(($wrongindex + 2) % 3, true, false), - $this->get_contains_incorrect_expectation()); + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33)); $this->assertPattern('/B|C/', $this->quba->get_response_summary($this->slot)); @@ -102,9 +115,7 @@ public function test_adaptive_multichoice() { $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false), $this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false), $this->get_contains_correct_expectation(), - new PatternExpectation('/' . preg_quote( - get_string('gradingdetailspenalty', 'qbehaviour_adaptive', - format_float($mc->penalty, $this->displayoptions->markdp))) . '/')); + $this->get_does_not_contain_penalty_info_expectation()); $this->assertEqual('A', $this->quba->get_response_summary($this->slot)); @@ -133,7 +144,8 @@ public function test_adaptive_multichoice() { // Now change the correct answer to the question, and regrade. $mc->answers[13]->fraction = -0.33333333; - $mc->answers[15]->fraction = 1; + $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above! + $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score. $this->quba->regrade_all_questions(); // Verify. @@ -144,7 +156,7 @@ public function test_adaptive_multichoice() { $this->get_contains_partcorrect_expectation()); $autogradedstep = $this->get_step($this->get_step_count() - 2); - $this->assertWithinMargin($autogradedstep->get_fraction(), 0, 0.0000001); + $this->assertWithinMargin($autogradedstep->get_fraction(), 1, 0.0000001); } public function test_adaptive_multichoice2() { @@ -173,14 +185,16 @@ public function test_adaptive_multichoice2() { $this->check_current_output( $this->get_contains_mark_summary(2), $this->get_contains_submit_button_expectation(true), - $this->get_contains_correct_expectation()); + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation()); - // Save the same correct answer again. Should no do anything. + // Save the same correct answer again. Should not do anything. $numsteps = $this->get_step_count(); $this->process_submission(array('choice0' => 1, 'choice2' => 1)); // Verify. $this->check_step_count($numsteps); + $this->check_current_mark(2); $this->check_current_state(question_state::$complete); // Finish the attempt. @@ -196,6 +210,229 @@ public function test_adaptive_multichoice2() { $this->get_contains_correct_expectation()); } + public function test_adaptive_shortanswer_partially_right() { + + // Create a short answer question + $sa = test_question_maker::make_a_shortanswer_question(); + $this->start_attempt_at_question($sa, 'adaptive'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit a partially correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'toad')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0.8); + $this->check_current_output( + $this->get_contains_mark_summary(0.8), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_partcorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit an incorrect answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0.8); + $this->check_current_output( + $this->get_contains_mark_summary(0.8), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit a correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'frog')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.8); + $this->check_current_output( + $this->get_contains_mark_summary(0.8), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(0.8); + $this->check_current_output( + $this->get_contains_mark_summary(0.8), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + } + + public function test_adaptive_shortanswer_wrong_right_wrong() { + + // Create a short answer question + $sa = test_question_maker::make_a_shortanswer_question(); + $this->start_attempt_at_question($sa, 'adaptive'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit a wrong answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit the same wrong answer again. Nothing should change. + $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit a correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'frog')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit another incorrect answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedwrong); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + } + + public function test_adaptive_shortanswer_invalid_after_complete() { + + // Create a short answer question + $sa = test_question_maker::make_a_shortanswer_question(); + $this->start_attempt_at_question($sa, 'adaptive'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit a wrong answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(0); + $this->check_current_output( + $this->get_contains_mark_summary(0), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit a correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'frog')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit an empty answer. + $this->process_submission(array('-submit' => 1, 'answer' => '')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_contains_validation_error_expectation()); + + // Submit another wrong answer. + $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedwrong); + $this->check_current_mark(0.66666667); + $this->check_current_output( + $this->get_contains_mark_summary(0.67), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + } + public function test_adaptive_shortanswer_try_to_submit_blank() { // Create a short answer question with correct answer true. @@ -220,6 +457,7 @@ public function test_adaptive_shortanswer_try_to_submit_blank() { $this->get_contains_marked_out_of_summary(), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), $this->get_contains_validation_error_expectation()); $this->assertNull($this->quba->get_response_summary($this->slot)); @@ -233,6 +471,7 @@ public function test_adaptive_shortanswer_try_to_submit_blank() { $this->get_contains_mark_summary(0.8), $this->get_contains_submit_button_expectation(true), $this->get_contains_partcorrect_expectation(), + $this->get_contains_penalty_info_expectation(0.33), $this->get_does_not_contain_validation_error_expectation()); // Now submit blank again. @@ -245,6 +484,60 @@ public function test_adaptive_shortanswer_try_to_submit_blank() { $this->get_contains_mark_summary(0.8), $this->get_contains_submit_button_expectation(true), $this->get_contains_partcorrect_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), $this->get_contains_validation_error_expectation()); } + + public function test_adaptive_numerical() { + + // Create a numerical question + $sa = test_question_maker::make_question('numerical', 'pi'); + $this->start_attempt_at_question($sa, 'adaptive'); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation()); + + // Submit the correct answer. + $this->process_submission(array('-submit' => 1, 'answer' => '3.14')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(1); + $this->check_current_output( + $this->get_contains_mark_summary(1), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_correct_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Submit an incorrect answer. + $this->process_submission(array('-submit' => 1, 'answer' => '-5')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(1); + $this->check_current_output( + $this->get_contains_mark_summary(1), + $this->get_contains_submit_button_expectation(true), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_penalty_info_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedwrong); + $this->check_current_mark(1); + $this->check_current_output( + $this->get_contains_mark_summary(1), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_incorrect_expectation(), + $this->get_does_not_contain_validation_error_expectation()); + } } diff --git a/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php b/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php index 0cd5ff7d44376..ecc45bdc72e1b 100644 --- a/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php +++ b/question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php @@ -129,7 +129,8 @@ public function test_multichoice() { // Now change the correct answer to the question, and regrade. $mc->answers[13]->fraction = -0.33333333; - $mc->answers[15]->fraction = 1; + $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above! + $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score. $this->quba->regrade_all_questions(); // Verify. @@ -139,8 +140,8 @@ public function test_multichoice() { $this->get_contains_mark_summary(1), $this->get_contains_partcorrect_expectation()); - $autogradedstep = $this->get_step($this->get_step_count() - 2); - $this->assertWithinMargin($autogradedstep->get_fraction(), 0, 0.0000001); + $autogradedstep = $this->get_step($this->get_step_count() - 3); + $this->assertWithinMargin($autogradedstep->get_fraction(), 1, 0.0000001); } public function test_multichoice2() { diff --git a/question/engine/questionattempt.php b/question/engine/questionattempt.php index 5f29d378d0a95..6d4811e8cec52 100644 --- a/question/engine/questionattempt.php +++ b/question/engine/questionattempt.php @@ -415,6 +415,21 @@ public function get_last_step_with_qt_var($name) { return new question_attempt_step_read_only(); } + /** + * Get the last step with a particular behaviour variable set. + * @param string $name the name of the variable to get. + * @return question_attempt_step the last step, or a step with no variables + * if there was not a real step. + */ + public function get_last_step_with_behaviour_var($name) { + foreach ($this->get_reverse_step_iterator() as $step) { + if ($step->has_behaviour_var($name)) { + return $step; + } + } + return new question_attempt_step_read_only(); + } + /** * Get the latest value of a particular question type variable. That is, get * the value from the latest step that has it set. Return null if it is not diff --git a/question/type/numerical/question.php b/question/type/numerical/question.php index 0e43a847a8593..ea1b026fd1f3a 100644 --- a/question/type/numerical/question.php +++ b/question/type/numerical/question.php @@ -153,7 +153,7 @@ public function is_same_response(array $prevresponse, array $newresponse) { $prevresponse, $newresponse, 'unit'); } - return false; + return true; } public function get_correct_response() { From 4146e4352be61a2ba005af64691a4e26cee078a4 Mon Sep 17 00:00:00 2001 From: Mary Evans Date: Thu, 13 Oct 2011 02:43:04 +0100 Subject: [PATCH 05/67] MDL-26983 FIX for side-post-only layout --- theme/sky_high/style/pagelayout.css | 67 +++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/theme/sky_high/style/pagelayout.css b/theme/sky_high/style/pagelayout.css index 349eb10f70b2e..1bf154ac62337 100644 --- a/theme/sky_high/style/pagelayout.css +++ b/theme/sky_high/style/pagelayout.css @@ -1,12 +1,7 @@ /** Path: theme pagelayout **/ /********************************************************************************************* - - left column: 230px - right column: 330px - padding left/right column: 10px - padding center column: 30px - + column witdh: [[setting:regionwidth]] **********************************************************************************************/ body { @@ -91,8 +86,6 @@ body { width: 100%; } - - /* @end */ /* @group Pre Side Only */ @@ -118,8 +111,6 @@ body { width: 0%; } - - /* @end */ /* @group Post Side Only */ @@ -136,8 +127,13 @@ body { margin-left: [[setting:regionwidth]]; } +.side-post-only #page-content #region-main-box #region-post-box #region-pre { + left: 0; + width: 0; +} + .side-post-only #page-content #region-main-box #region-post-box #region-post { - left: [[setting:regionwidthdouble]]; + left: [[setting:regionwidth]]; width: [[setting:regionwidth]]; } @@ -145,7 +141,49 @@ body { margin-left: 200px; } +.blocks-moving.side-post-only #page-content #region-main-box { + float: left; + margin-left: -[[setting:regionwidth]]; + position: relative; + width: 200%; + right: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box { + float: left; + margin-left: -[[setting:regionwidth]]; + width: 100%; + border-right: 2px solid #98bcd6; + background: url([[pix:theme|top_bg]]) repeat-x top #fff; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap { + float: left; + width: 50%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { + overflow: hidden; + position: relative; + margin-left: [[setting:regionwidthdouble]]; + left: 100%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre { + float: right; + position: relative; + left: [[setting:leftregionwidthmargin]]; + width: [[setting:regionwidth]]; + background: transparent; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post { + float: right; + position: relative; + left: [[setting:rightregionwidthmargin]]; + width: [[setting:regionwidth]]; + background: transparent; +} /* @end */ @@ -171,12 +209,13 @@ body { width: 0px; } +/* @end */ +/* @pagelayout-report - overflow */ -/* @end */ .pagelayout-report #page-content #region-main { - overflow:auto; + overflow: auto; } .pagelayout-report #page-content #region-main .region-content { - overflow:visible; + overflow: visible; } \ No newline at end of file From f6f2bebd0c24b4ef46574c8e002d48bfcadf2791 Mon Sep 17 00:00:00 2001 From: Aparup Banerjee Date: Mon, 10 Oct 2011 13:50:01 +0800 Subject: [PATCH 06/67] MDL-28436 webservice : fixed missing field in query --- lib/adminlib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/adminlib.php b/lib/adminlib.php index d4adbe679c13d..68c17619ca5a1 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -7517,7 +7517,7 @@ public function output_html($data, $query='') { //TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN //here retrieve token list (including linked users firstname/lastname and linked services name) - $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.validuntil, s.id AS serviceid + $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid FROM {external_tokens} t, {user} u, {external_services} s WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id"; $tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT)); From ec3c9dad7bc17690eaffc91526fddf0a4824577c Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 22 Sep 2011 14:51:32 +0530 Subject: [PATCH 07/67] MDL-28483 URL making url field required --- mod/url/db/install.xml | 2 +- mod/url/mod_form.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mod/url/db/install.xml b/mod/url/db/install.xml index eb8f4d2f4c177..d042027dd7c98 100644 --- a/mod/url/db/install.xml +++ b/mod/url/db/install.xml @@ -11,7 +11,7 @@ - + diff --git a/mod/url/mod_form.php b/mod/url/mod_form.php index da6a7209dc781..f8f5f868abb35 100644 --- a/mod/url/mod_form.php +++ b/mod/url/mod_form.php @@ -50,6 +50,7 @@ function definition() { //------------------------------------------------------- $mform->addElement('header', 'content', get_string('contentheader', 'url')); $mform->addElement('url', 'externalurl', get_string('externalurl', 'url'), array('size'=>'60'), array('usefilepicker'=>true)); + $mform->addRule('externalurl', null, 'required', null, 'client'); //------------------------------------------------------- $mform->addElement('header', 'optionssection', get_string('optionsheader', 'url')); From 4373200f88cf235d08e9f5c81f252886036a822e Mon Sep 17 00:00:00 2001 From: Ankit Kumar Agarwal Date: Thu, 22 Sep 2011 15:40:48 +0530 Subject: [PATCH 08/67] MDL-28483 URL adding validation checks and changing field externalurl to not-null --- mod/url/db/upgrade.php | 14 ++++++++++++++ mod/url/lang/en/url.php | 1 + mod/url/mod_form.php | 10 ++++++++++ mod/url/version.php | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/mod/url/db/upgrade.php b/mod/url/db/upgrade.php index 8c995bfae49b5..01926f80c9c2c 100644 --- a/mod/url/db/upgrade.php +++ b/mod/url/db/upgrade.php @@ -53,6 +53,20 @@ function xmldb_url_upgrade($oldversion) { // Moodle v2.1.0 release upgrade line // Put any upgrade step following this + if ($oldversion < 2011092800) { + + // Changing nullability of field externalurl on table urls to not-null + $table = new xmldb_table('url'); + $field = new xmldb_field('externalurl', XMLDB_TYPE_TEXT, 'small', null, + XMLDB_NOTNULL, null, null, 'introformat'); + + $DB->set_field_select('url', 'externalurl', $DB->sql_empty(), 'externalurl IS NULL'); + // Launch change of nullability for field =externalurl + $dbman->change_field_notnull($table, $field); + + // url savepoint reached + upgrade_mod_savepoint(true, 2011092800, 'url'); + } return true; } diff --git a/mod/url/lang/en/url.php b/mod/url/lang/en/url.php index be6867d474396..4498394bc59de 100644 --- a/mod/url/lang/en/url.php +++ b/mod/url/lang/en/url.php @@ -45,6 +45,7 @@ $string['externalurl'] = 'External URL'; $string['framesize'] = 'Frame height'; $string['chooseavariable'] = 'Choose a variable...'; +$string['invalidurl'] = 'Entered URL is invalid'; $string['modulename'] = 'URL'; $string['modulenameplural'] = 'URLs'; $string['neverseen'] = 'Never seen'; diff --git a/mod/url/mod_form.php b/mod/url/mod_form.php index f8f5f868abb35..790b85dc5349e 100644 --- a/mod/url/mod_form.php +++ b/mod/url/mod_form.php @@ -166,4 +166,14 @@ function data_preprocessing(&$default_values) { } } + function validation($data, $files) { + $errors = parent::validation($data, $files); + //Validating Entered url + $data['externalurl'] = clean_param($data['externalurl'], PARAM_URL); + if (empty($data['externalurl'])) { + $errors['externalurl'] = get_string('invalidurl', 'url'); + } + return $errors; + } + } diff --git a/mod/url/version.php b/mod/url/version.php index 5b4c1b54024d6..a769049edc986 100644 --- a/mod/url/version.php +++ b/mod/url/version.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die; -$module->version = 2010101400; +$module->version = 2011092800; $module->requires = 2010080300; // Requires this Moodle version $module->cron = 0; From 774118e9601caaa1cabc2100f65ab27d4a55118a Mon Sep 17 00:00:00 2001 From: Aparup Banerjee Date: Mon, 10 Oct 2011 10:34:01 +0800 Subject: [PATCH 09/67] MDL-27351 Quiz : updated string to reflect validation looking for embedded questions. --- question/type/multianswer/lang/en/qtype_multianswer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/multianswer/lang/en/qtype_multianswer.php b/question/type/multianswer/lang/en/qtype_multianswer.php index 2ce85647824a1..4f84fe8997f46 100644 --- a/question/type/multianswer/lang/en/qtype_multianswer.php +++ b/question/type/multianswer/lang/en/qtype_multianswer.php @@ -53,7 +53,7 @@
  • change their question type (numerical, shortanswer, multiple choice).
  • '; $string['questionsless'] = '{$a} question(s) less than in the multianswer question stored in the database'; -$string['questionsmissing'] = 'No valid questions, create at least one question'; +$string['questionsmissing'] = 'The question text must include at least one embedded answer.'; $string['questionsmore'] = '{$a} question(s) more than in the multianswer question stored in the database'; $string['questionnotfound'] = 'Unable to find question of question part #{$a}'; $string['questionsaveasedited'] = 'The question will be saved as edited'; From 485ec5941e1fe76b1c0f5c2a9d943f47b47e8a0c Mon Sep 17 00:00:00 2001 From: Andreas Grabs Date: Sat, 8 Oct 2011 18:21:23 +0200 Subject: [PATCH 10/67] MDL-19488 - allow the creation of a "Public" feedback --- mod/feedback/delete_template.php | 5 +- mod/feedback/edit.php | 20 ++++---- mod/feedback/edit_form.php | 24 ++++++--- mod/feedback/lib.php | 86 ++++++++++++++++++++++++++------ mod/feedback/version.php | 2 +- 5 files changed, 103 insertions(+), 34 deletions(-) diff --git a/mod/feedback/delete_template.php b/mod/feedback/delete_template.php index e866a62530a1e..0aa4543c0a11b 100644 --- a/mod/feedback/delete_template.php +++ b/mod/feedback/delete_template.php @@ -104,7 +104,7 @@ $mform->display(); echo $OUTPUT->box_end(); }else { - $templates = feedback_get_template_list($course, true); + $templates = feedback_get_template_list($course, 'own'); echo '
    '; if(!is_array($templates)) { echo $OUTPUT->box(get_string('no_templates_available_yet', 'feedback'), 'generalbox boxaligncenter'); @@ -112,7 +112,8 @@ echo ''; echo ''; foreach($templates as $template) { - echo ''; + $suffix = $template->ispublic ? (' ('.get_string('public', 'feedback').')') : ''; + echo ''; echo '
    '.get_string('templates', 'feedback').' 
    '.$template->name.'
    '.$template->name.$suffix.''; echo '
    '; echo ''; diff --git a/mod/feedback/edit.php b/mod/feedback/edit.php index 0c5509fc7026f..34eae4b620521 100644 --- a/mod/feedback/edit.php +++ b/mod/feedback/edit.php @@ -85,28 +85,26 @@ //the create_template-form $create_template_form = new feedback_edit_create_template_form(); -$create_template_form->set_feedbackdata(array('context' => $context)); +$create_template_form->set_feedbackdata(array('context'=>$context, 'course'=>$course)); $create_template_form->set_form_elements(); $create_template_form->set_data(array('id'=>$id, 'do_show'=>'templates')); $create_template_formdata = $create_template_form->get_data(); if(isset($create_template_formdata->savetemplate) && $create_template_formdata->savetemplate == 1) { //check the capabilities to create templates if(!has_capability('mod/feedback:createprivatetemplate', $context) AND - !has_capability('mod/feedback:createpublictemplate', $context)) { + !has_capability('mod/feedback:createpublictemplate', $context)) { print_error('cannotsavetempl', 'feedback'); } - if(trim($create_template_formdata->templatename) == '') - { + if(trim($create_template_formdata->templatename) == '') { $savereturn = 'notsaved_name'; }else { - //public templates are currently deaktivated - // if(has_capability('mod/feedback:createpublictemplate', $context)) { - // $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0; - // }else { + //if the feedback is located on the frontpage then templates can be public + if($CFG->frontpage === $course->id && has_capability('mod/feedback:createpublictemplate', $context)) { + $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0; + }else { $create_template_formdata->ispublic = 0; - // } - if(!feedback_save_as_template($feedback, $create_template_formdata->templatename, $create_template_formdata->ispublic)) - { + } + if(!feedback_save_as_template($feedback, $create_template_formdata->templatename, $create_template_formdata->ispublic)) { $savereturn = 'failed'; }else { $savereturn = 'saved'; diff --git a/mod/feedback/edit_form.php b/mod/feedback/edit_form.php index 0ce6ab71677ac..cf8bbff4abd53 100644 --- a/mod/feedback/edit_form.php +++ b/mod/feedback/edit_form.php @@ -77,11 +77,19 @@ function set_form_elements(){ // visible elements $templates_options = array(); - if($templates = feedback_get_template_list($this->feedbackdata->course)){//get the templates + $owntemplates = feedback_get_template_list($this->feedbackdata->course, 'own'); + $publictemplates = feedback_get_template_list($this->feedbackdata->course, 'public'); + if($owntemplates OR $publictemplates){//get the templates $templates_options[' '] = get_string('select'); - foreach($templates as $template) { + foreach($owntemplates as $template) { + if($template->ispublic) { + continue; + } $templates_options[$template->id] = $template->name; } + foreach($publictemplates as $template) { + $templates_options[$template->id] = '*'.$template->name; + } $attributes = 'onChange="this.form.submit()"'; $elementgroup[] =& $mform->createElement('select', 'templateid', '', $templates_options, $attributes); // buttons @@ -114,6 +122,8 @@ function set_feedbackdata($data) { } function set_form_elements(){ + global $CFG; + $mform =& $this->_form; // $capabilities = $this->feedbackdata->capabilities; @@ -134,10 +144,12 @@ function set_form_elements(){ $elementgroup[] =& $mform->createElement('static', 'templatenamelabel', get_string('name', 'feedback')); $elementgroup[] =& $mform->createElement('text', 'templatename', get_string('name', 'feedback'), array('size'=>'40', 'maxlength'=>'200')); - //public templates are currently deactivated - // if(has_capability('mod/feedback:createpublictemplate', $this->feedbackdata->context)) { - // $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback')); - // } + //If the feedback is located on the frontpage the we can create public templates + if($CFG->frontpage === $this->feedbackdata->course->id) { + if(has_capability('mod/feedback:createpublictemplate', $this->feedbackdata->context)) { + $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback')); + } + } // buttons $elementgroup[] =& $mform->createElement('submit', 'create_template', get_string('save_as_new_template', 'feedback')); diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 9d884dd001117..3ce2874c519ec 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -138,6 +138,9 @@ function feedback_update_instance($feedback) { /** * Serves the files included in feedback items like label. Implements needed access control ;-) * + * There are two situations in general where the files will be sent. + * 1) filearea = item, 2) filearea = template + * * @param object $course * @param object $cm * @param object $context @@ -149,17 +152,59 @@ function feedback_update_instance($feedback) { function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) { global $CFG, $DB; - require_login($course, false, $cm); - $itemid = (int)array_shift($args); - require_course_login($course, true, $cm); - + //get the item what includes the file if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) { return false; } + + //if the filearea is "item" so we check the permissions like view/complete the feedback + if($filearea === 'item') { + //get the feedback + if(!$feedback = $DB->get_record('feedback', array('id'=>$item->feedback))) { + return false; + } - if (!has_capability('mod/feedback:view', $context)) { + $canload = false; + //first check whether the user has the complete capability + if(has_capability('mod/feedback:complete', $context)) { + $canload = true; + } + + //now we check whether the user has the view capability + if(has_capability('mod/feedback:view', $context)) { + $canload = true; + } + + //if the feedback is on frontpage and anonymous and the fullanonymous is allowed + //so the file can be loaded too. + if(isset($CFG->feedback_allowfullanonymous) + AND $CFG->feedback_allowfullanonymous + AND $course->id == SITEID + AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES ) { + $canload = true; + } + + if(!$canload) { + return false; + } + }else if($filearea === 'template') { //now we check files in templates + if(!$template = $DB->get_record('feedback_template', array('id'=>$item->template))) { + return false; + } + + //if the file is not public so the capability edititems has to be there + if(!$template->ispublic) { + if(!has_capability('mod/feedback:edititems', $context)) { + return false; + } + }else { //on public templates, at least the user has to be logged in + if(!isloggedin()) { + return false; + } + } + }else { return false; } @@ -1073,6 +1118,9 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false $fs = get_file_storage(); + if(!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) { + return false; + } //get all templateitems if(!$templitems = $DB->get_records('feedback_item', array('template'=>$templateid))) { return false; @@ -1080,7 +1128,11 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false //files in the template_item are in the context of the current course //files in the feedback_item are in the feedback_context of the feedback - $c_context = get_context_instance(CONTEXT_COURSE, $feedback->course); + if($template->ispublic) { + $c_context = get_context_instance(CONTEXT_COURSE, $template->course); + }else { + $c_context = get_context_instance(CONTEXT_COURSE, $feedback->course); + } $course = $DB->get_record('course', array('id'=>$feedback->course)); $cm = get_coursemodule_from_instance('feedback', $feedback->id); $f_context = get_context_instance(CONTEXT_MODULE, $cm->id); @@ -1128,7 +1180,7 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false $item->position = $item->position + $positionoffset; $item->id = $DB->insert_record('feedback_item', $item); - + //TODO: moving the files to the new items if ($templatefiles = $fs->get_area_files($c_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) { foreach($templatefiles as $tfile) { @@ -1162,16 +1214,22 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false * * @global object * @param object $course - * @param boolean $onlyown + * @param string $onlyownorpublic * @return array the template recordsets */ -function feedback_get_template_list($course, $onlyown = false) { - global $DB; +function feedback_get_template_list($course, $onlyownorpublic = '') { + global $DB, $CFG; - if ($onlyown) { - $templates = $DB->get_records('feedback_template', array('course'=>$course->id)); - } else { - $templates = $DB->get_records_select('feedback_template', 'course = ? OR ispublic = 1', array($course->id)); + switch($onlyownorpublic) { + case '': + $templates = $DB->get_records_select('feedback_template', 'course = ? OR ispublic = 1', array($course->id), 'name'); + break; + case 'own': + $templates = $DB->get_records('feedback_template', array('course'=>$course->id), 'name'); + break; + case 'public': + $templates = $DB->get_records('feedback_template', array('course'=>$CFG->frontpage, 'ispublic'=>1), 'name'); + break; } return $templates; } diff --git a/mod/feedback/version.php b/mod/feedback/version.php index 78178bd812804..e16615031c061 100644 --- a/mod/feedback/version.php +++ b/mod/feedback/version.php @@ -9,7 +9,7 @@ */ - $module->version = 2011051600; // The current module version (Date: YYYYMMDDXX) + $module->version = 2011100800; // The current module version (Date: YYYYMMDDXX) $module->requires = 2010080300; // Requires this Moodle version $feedback_version_intern = 1; //this version is used for restore older backups $module->cron = 0; // Period for cron to check this module (secs) From e70dab06469d0c818a2bd39931f1fd5d8d921676 Mon Sep 17 00:00:00 2001 From: Andreas Grabs Date: Mon, 10 Oct 2011 21:42:29 +0200 Subject: [PATCH 11/67] MDL-19488 - allow the creation of a "Public" feedback --- mod/feedback/edit.php | 2 +- mod/feedback/edit_form.php | 2 +- mod/feedback/lib.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/feedback/edit.php b/mod/feedback/edit.php index 34eae4b620521..4b7299e394e8b 100644 --- a/mod/feedback/edit.php +++ b/mod/feedback/edit.php @@ -99,7 +99,7 @@ $savereturn = 'notsaved_name'; }else { //if the feedback is located on the frontpage then templates can be public - if($CFG->frontpage === $course->id && has_capability('mod/feedback:createpublictemplate', $context)) { + if(SITEID === $course->id && has_capability('mod/feedback:createpublictemplate', $context)) { $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0; }else { $create_template_formdata->ispublic = 0; diff --git a/mod/feedback/edit_form.php b/mod/feedback/edit_form.php index cf8bbff4abd53..ccea10f5e1000 100644 --- a/mod/feedback/edit_form.php +++ b/mod/feedback/edit_form.php @@ -145,7 +145,7 @@ function set_form_elements(){ $elementgroup[] =& $mform->createElement('text', 'templatename', get_string('name', 'feedback'), array('size'=>'40', 'maxlength'=>'200')); //If the feedback is located on the frontpage the we can create public templates - if($CFG->frontpage === $this->feedbackdata->course->id) { + if(SITEID === $this->feedbackdata->course->id) { if(has_capability('mod/feedback:createpublictemplate', $this->feedbackdata->context)) { $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback')); } diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 3ce2874c519ec..030cf810b2be2 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -1228,7 +1228,7 @@ function feedback_get_template_list($course, $onlyownorpublic = '') { $templates = $DB->get_records('feedback_template', array('course'=>$course->id), 'name'); break; case 'public': - $templates = $DB->get_records('feedback_template', array('course'=>$CFG->frontpage, 'ispublic'=>1), 'name'); + $templates = $DB->get_records('feedback_template', array('course'=>SITEID, 'ispublic'=>1), 'name'); break; } return $templates; From 7b1126fd2004ab304437cf479bc966cedbf688db Mon Sep 17 00:00:00 2001 From: Andreas Grabs Date: Tue, 11 Oct 2011 22:37:47 +0200 Subject: [PATCH 12/67] MDL-19488 - allow the creation of a "Public" feedback --- mod/feedback/delete_template.php | 121 ++++++++++++++++++++++++------- mod/feedback/edit.php | 2 +- mod/feedback/edit_form.php | 36 +++++---- mod/feedback/item/label/lib.php | 6 +- mod/feedback/lib.php | 69 ++++++++++-------- 5 files changed, 161 insertions(+), 73 deletions(-) diff --git a/mod/feedback/delete_template.php b/mod/feedback/delete_template.php index 0aa4543c0a11b..e9685083edce9 100644 --- a/mod/feedback/delete_template.php +++ b/mod/feedback/delete_template.php @@ -11,6 +11,7 @@ require_once("../../config.php"); require_once("lib.php"); require_once('delete_template_form.php'); +require_once($CFG->libdir.'/tablelib.php'); // $SESSION->feedback->current_tab = 'templates'; $current_tab = 'templates'; @@ -77,13 +78,24 @@ } if(isset($formdata->confirmdelete) AND $formdata->confirmdelete == 1){ - feedback_delete_template($formdata->deletetempl); + if(!$template = $DB->get_record("feedback_template", array("id"=>$deletetempl))) { + print_error('error'); + } + + if($template->ispublic) { + $systemcontext = get_system_context(); + require_capability('mod/feedback:createpublictemplate', $systemcontext); + require_capability('mod/feedback:deletetemplate', $systemcontext); + } + + feedback_delete_template($template); redirect($deleteurl->out(false)); } /// Print the page header $strfeedbacks = get_string("modulenameplural", "feedback"); $strfeedback = get_string("modulename", "feedback"); +$str_delete_feedback = get_string('delete_template','feedback'); $PAGE->set_heading(format_string($course->fullname)); $PAGE->set_title(format_string($feedback->name)); @@ -96,7 +108,7 @@ /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// -echo $OUTPUT->heading(get_string('delete_template','feedback')); +echo $OUTPUT->heading($str_delete_feedback); if($shoulddelete == 1) { echo $OUTPUT->box_start('generalbox errorboxcontent boxaligncenter boxwidthnormal'); @@ -104,37 +116,94 @@ $mform->display(); echo $OUTPUT->box_end(); }else { + //first we get the own templates $templates = feedback_get_template_list($course, 'own'); - echo '
    '; if(!is_array($templates)) { echo $OUTPUT->box(get_string('no_templates_available_yet', 'feedback'), 'generalbox boxaligncenter'); }else { - echo ''; - echo ''; + echo $OUTPUT->heading(get_string('course'), 3); + echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal'); + $tablecolumns = array('template', 'action'); + $tableheaders = array(get_string('template', 'feedback'), ''); + $table_course = new flexible_table('feedback_template_course_table'); + + $table_course->define_columns($tablecolumns); + $table_course->define_headers($tableheaders); + $table_course->define_baseurl($deleteurl); + $table_course->column_style('action', 'width', '10%'); + + $table_course->sortable(false); + $table_course->set_attribute('width', '100%'); + $table_course->set_attribute('class', 'generaltable'); + $table_course->setup(); + foreach($templates as $template) { - $suffix = $template->ispublic ? (' ('.get_string('public', 'feedback').')') : ''; - echo ''; - echo ''; + $data = array(); + $data[] = $template->name; + $url = new moodle_url($deleteurl, array( + 'id'=>$id, + 'deletetempl'=>$template->id, + 'shoulddelete'=>1, + )); + + $data[] = $OUTPUT->single_button($url, $str_delete_feedback, 'post'); + $table_course->add_data($data); } - echo '
    '.get_string('templates', 'feedback').' 
    '.$template->name.$suffix.''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo '
    '; + $table_course->finish_output(); + echo $OUTPUT->box_end(); } -?> -
    - - - - -
    -
    -box(get_string('no_templates_available_yet', 'feedback'), 'generalbox boxaligncenter'); + }else { + echo $OUTPUT->heading(get_string('public', 'feedback'), 3); + echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal'); + $tablecolumns = $tablecolumns = array('template', 'action'); + $tableheaders = array(get_string('template', 'feedback'), ''); + $table_public = new flexible_table('feedback_template_public_table'); + + $table_public->define_columns($tablecolumns); + $table_public->define_headers($tableheaders); + $table_public->define_baseurl($deleteurl); + $table_public->column_style('action', 'width', '10%'); + + $table_public->sortable(false); + $table_public->set_attribute('width', '100%'); + $table_public->set_attribute('class', 'generaltable'); + $table_public->setup(); + + + // echo $OUTPUT->heading(get_string('public', 'feedback'), 3); + // echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide'); + foreach($templates as $template) { + $data = array(); + $data[] = $template->name; + $url = new moodle_url($deleteurl, array( + 'id'=>$id, + 'deletetempl'=>$template->id, + 'shoulddelete'=>1, + )); + + $data[] = $OUTPUT->single_button($url, $str_delete_feedback, 'post'); + $table_public->add_data($data); + } + $table_public->finish_output(); + echo $OUTPUT->box_end(); + } + } + + echo $OUTPUT->box_start('boxaligncenter boxwidthnormal'); + $url = new moodle_url($deleteurl, array( + 'id'=>$id, + 'canceldelete'=>1, + )); + + echo $OUTPUT->single_button($url, get_string('back'), 'post'); + echo $OUTPUT->box_end(); } echo $OUTPUT->footer(); diff --git a/mod/feedback/edit.php b/mod/feedback/edit.php index 4b7299e394e8b..c0bc9951df77a 100644 --- a/mod/feedback/edit.php +++ b/mod/feedback/edit.php @@ -99,7 +99,7 @@ $savereturn = 'notsaved_name'; }else { //if the feedback is located on the frontpage then templates can be public - if(SITEID === $course->id && has_capability('mod/feedback:createpublictemplate', $context)) { + if(has_capability('mod/feedback:createpublictemplate', get_system_context())) { $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0; }else { $create_template_formdata->ispublic = 0; diff --git a/mod/feedback/edit_form.php b/mod/feedback/edit_form.php index ccea10f5e1000..0d69d04bb6d15 100644 --- a/mod/feedback/edit_form.php +++ b/mod/feedback/edit_form.php @@ -79,20 +79,29 @@ function set_form_elements(){ $templates_options = array(); $owntemplates = feedback_get_template_list($this->feedbackdata->course, 'own'); $publictemplates = feedback_get_template_list($this->feedbackdata->course, 'public'); - if($owntemplates OR $publictemplates){//get the templates - $templates_options[' '] = get_string('select'); - foreach($owntemplates as $template) { - if($template->ispublic) { - continue; + + $options = array(); + if($owntemplates or $publictemplates) { + $options[''] = array('' => get_string('choose')); + + if($owntemplates) { + $courseoptions = array(); + foreach($owntemplates as $template) { + $courseoptions[$template->id] = $template->name; } - $templates_options[$template->id] = $template->name; + $options[get_string('course')] = $courseoptions; } - foreach($publictemplates as $template) { - $templates_options[$template->id] = '*'.$template->name; + + if($publictemplates) { + $publicoptions = array(); + foreach($publictemplates as $template) { + $publicoptions[$template->id] = $template->name; + } + $options[get_string('public', 'feedback')] = $publicoptions; } + $attributes = 'onChange="this.form.submit()"'; - $elementgroup[] =& $mform->createElement('select', 'templateid', '', $templates_options, $attributes); - // buttons + $elementgroup[] =& $mform->createElement('selectgroups', 'templateid', '', $options, $attributes); $elementgroup[] =& $mform->createElement('submit', 'use_template', get_string('use_this_template', 'feedback')); }else { $mform->addElement('static', 'info', get_string('no_templates_available_yet', 'feedback')); @@ -144,11 +153,8 @@ function set_form_elements(){ $elementgroup[] =& $mform->createElement('static', 'templatenamelabel', get_string('name', 'feedback')); $elementgroup[] =& $mform->createElement('text', 'templatename', get_string('name', 'feedback'), array('size'=>'40', 'maxlength'=>'200')); - //If the feedback is located on the frontpage the we can create public templates - if(SITEID === $this->feedbackdata->course->id) { - if(has_capability('mod/feedback:createpublictemplate', $this->feedbackdata->context)) { - $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback')); - } + if(has_capability('mod/feedback:createpublictemplate', get_system_context())) { + $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback')); } // buttons diff --git a/mod/feedback/item/label/lib.php b/mod/feedback/item/label/lib.php index 5e5df4c6478e3..6a1081a31dc60 100644 --- a/mod/feedback/item/label/lib.php +++ b/mod/feedback/item/label/lib.php @@ -123,7 +123,11 @@ function print_item($item){ //is the item a template? if(!$item->feedback AND $item->template) { $template = $DB->get_record('feedback_template', array('id'=>$item->template)); - $context = get_context_instance(CONTEXT_COURSE, $template->course); + if($template->ispublic) { + $context = get_system_context(); + }else { + $context = get_context_instance(CONTEXT_COURSE, $template->course); + } $filearea = 'template'; }else { $cm = get_coursemodule_from_instance('feedback', $item->feedback); diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 030cf810b2be2..0342cf6f6e60c 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -240,7 +240,6 @@ function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedow return false; } - /** * this will delete a given instance. * all referenced data also will be deleted @@ -990,7 +989,7 @@ function feedback_create_template($courseid, $name, $ispublic = 0) { global $DB; $templ = new stdClass(); - $templ->course = $courseid; + $templ->course = ($ispublic ? 0 : $courseid); $templ->name = $name; $templ->ispublic = $ispublic; @@ -1023,9 +1022,14 @@ function feedback_save_as_template($feedback, $name, $ispublic = 0) { return false; } - //files in the template_item are in the context of the current course + //files in the template_item are in the context of the current course or + //if the template is public the files are in the system context //files in the feedback_item are in the feedback_context of the feedback - $c_context = get_context_instance(CONTEXT_COURSE, $newtempl->course); + if($ispublic) { + $s_context = get_system_context(); + }else { + $s_context = get_context_instance(CONTEXT_COURSE, $newtempl->course); + } $cm = get_coursemodule_from_instance('feedback', $feedback->id); $f_context = get_context_instance(CONTEXT_MODULE, $cm->id); @@ -1046,7 +1050,7 @@ function feedback_save_as_template($feedback, $name, $ispublic = 0) { if ($itemfiles = $fs->get_area_files($f_context->id, 'mod_feedback', 'item', $item->id, "id", false)) { foreach($itemfiles as $ifile) { $file_record = new stdClass(); - $file_record->contextid = $c_context->id; + $file_record->contextid = $s_context->id; $file_record->component = 'mod_feedback'; $file_record->filearea = 'template'; $file_record->itemid = $t_item->id; @@ -1076,27 +1080,19 @@ function feedback_save_as_template($feedback, $name, $ispublic = 0) { * * @global object * @uses CONTEXT_COURSE - * @param int $id the templateid + * @param object $template the template * @return void */ -function feedback_delete_template($id) { +function feedback_delete_template($template) { global $DB; - $template = $DB->get_record("feedback_template", array("id"=>$id)); - - //deleting the files from the item - $fs = get_file_storage(); - $context = get_context_instance(CONTEXT_COURSE, $template->course); - - - if($t_items = $DB->get_records("feedback_item", array("template"=>$id))) { + //deleting the files from the item is done by feedback_delete_item + if($t_items = $DB->get_records("feedback_item", array("template"=>$template->id))) { foreach($t_items as $t_item) { - if ($templatefiles = $fs->get_area_files($context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) { - $fs->delete_area_files($context->id, 'mod_feedback', 'template', $t_item->id); - } + feedback_delete_item($t_item->id, false, $template); } } - $DB->delete_records("feedback_template", array("id"=>$id)); + $DB->delete_records("feedback_template", array("id"=>$template->id)); } /** @@ -1129,9 +1125,9 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false //files in the template_item are in the context of the current course //files in the feedback_item are in the feedback_context of the feedback if($template->ispublic) { - $c_context = get_context_instance(CONTEXT_COURSE, $template->course); + $s_context = get_system_context(); }else { - $c_context = get_context_instance(CONTEXT_COURSE, $feedback->course); + $s_context = get_context_instance(CONTEXT_COURSE, $feedback->course); } $course = $DB->get_record('course', array('id'=>$feedback->course)); $cm = get_coursemodule_from_instance('feedback', $feedback->id); @@ -1182,7 +1178,7 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false $item->id = $DB->insert_record('feedback_item', $item); //TODO: moving the files to the new items - if ($templatefiles = $fs->get_area_files($c_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) { + if ($templatefiles = $fs->get_area_files($s_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) { foreach($templatefiles as $tfile) { $file_record = new stdClass(); $file_record->contextid = $f_context->id; @@ -1228,7 +1224,7 @@ function feedback_get_template_list($course, $onlyownorpublic = '') { $templates = $DB->get_records('feedback_template', array('course'=>$course->id), 'name'); break; case 'public': - $templates = $DB->get_records('feedback_template', array('course'=>SITEID, 'ispublic'=>1), 'name'); + $templates = $DB->get_records('feedback_template', array('ispublic'=>1), 'name'); break; } return $templates; @@ -1402,9 +1398,10 @@ function feedback_update_item($item){ * @uses CONTEXT_MODULE * @param int $itemid * @param boolean $renumber should the kept items renumbered Yes/No + * @param object $template if the template is given so the items are bound to it * @return void */ -function feedback_delete_item($itemid, $renumber = true){ +function feedback_delete_item($itemid, $renumber = true, $template = false){ global $DB; @@ -1412,13 +1409,25 @@ function feedback_delete_item($itemid, $renumber = true){ //deleting the files from the item $fs = get_file_storage(); - if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) { - return false; - } - $context = get_context_instance(CONTEXT_MODULE, $cm->id); + + if($template) { + if($template->ispublic) { + $context = get_system_context(); + }else { + $context = get_context_instance(CONTEXT_COURSE, $template->course); + } + if ($templatefiles = $fs->get_area_files($context->id, 'mod_feedback', 'template', $item->id, "id", false)) { + $fs->delete_area_files($context->id, 'mod_feedback', 'template', $item->id); + } + }else { + if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) { + return false; + } + $context = get_context_instance(CONTEXT_MODULE, $cm->id); - if ($itemfiles = $fs->get_area_files($context->id, 'mod_feedback', 'item', $item->id, "id", false)) { - $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id); + if ($itemfiles = $fs->get_area_files($context->id, 'mod_feedback', 'item', $item->id, "id", false)) { + $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id); + } } $DB->delete_records("feedback_value", array("item"=>$itemid)); From 6282381d05a62b032c20d25b7b29d8ead41cfbbd Mon Sep 17 00:00:00 2001 From: sam marshall Date: Tue, 11 Oct 2011 14:50:01 +0100 Subject: [PATCH 13/67] MDL-27242 Conditional availability dates should support time as well as day Change also adds minor feature to date-time selector so you can specify the time it uses as default when the value is 0 (disabled). Credit: This feature was developed collaboratively by Charles Fulton, Neill Magill, and me. --- course/modedit.php | 12 ---- course/moodleform_mod.php | 21 +++++-- lang/en/condition.php | 1 + lib/conditionlib.php | 103 ++++++++++++++++++++++++---------- lib/db/upgrade.php | 18 ++++++ lib/form/datetimeselector.php | 8 ++- version.php | 2 +- 7 files changed, 117 insertions(+), 48 deletions(-) diff --git a/course/modedit.php b/course/modedit.php index a68ddcca52911..111e655acf14a 100644 --- a/course/modedit.php +++ b/course/modedit.php @@ -302,12 +302,6 @@ if (!empty($CFG->enableavailability)) { $cm->availablefrom = $fromform->availablefrom; $cm->availableuntil = $fromform->availableuntil; - // The form time is midnight, but because we want it to be - // inclusive, set it to 23:59:59 on that day. - if ($cm->availableuntil) { - $cm->availableuntil = strtotime('23:59:59', - $cm->availableuntil); - } $cm->showavailability = $fromform->showavailability; condition_info::update_cm_from_form($cm,$fromform,true); } @@ -393,12 +387,6 @@ if(!empty($CFG->enableavailability)) { $newcm->availablefrom = $fromform->availablefrom; $newcm->availableuntil = $fromform->availableuntil; - // The form time is midnight, but because we want it to be - // inclusive, set it to 23:59:59 on that day. - if ($newcm->availableuntil) { - $newcm->availableuntil = strtotime('23:59:59', - $newcm->availableuntil); - } $newcm->showavailability = $fromform->showavailability; } if (isset($fromform->showdescription)) { diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php index ce5666bbba727..b4fbb2fabfd0c 100644 --- a/course/moodleform_mod.php +++ b/course/moodleform_mod.php @@ -308,7 +308,7 @@ function validation($data, $files) { // Conditions: Don't let them set dates which make no sense if (array_key_exists('availablefrom', $data) && $data['availablefrom'] && $data['availableuntil'] && - $data['availablefrom'] > $data['availableuntil']) { + $data['availablefrom'] >= $data['availableuntil']) { $errors['availablefrom'] = get_string('badavailabledates', 'condition'); } @@ -429,10 +429,23 @@ function standard_coursemodule_elements(){ if (!empty($CFG->enableavailability)) { // Conditional availability - $mform->addElement('header', 'availabilityconditionsheader', get_string('availabilityconditions', 'condition')); - $mform->addElement('date_selector', 'availablefrom', get_string('availablefrom', 'condition'), array('optional'=>true)); + + // Available from/to defaults to midnight because then the display + // will be nicer where it tells users when they can access it (it + // shows only the date and not time). + $date = usergetdate(time()); + $midnight = make_timestamp($date['year'], $date['mon'], $date['mday']); + + // From/until controls + $mform->addElement('header', 'availabilityconditionsheader', + get_string('availabilityconditions', 'condition')); + $mform->addElement('date_time_selector', 'availablefrom', + get_string('availablefrom', 'condition'), + array('optional' => true, 'defaulttime' => $midnight)); $mform->addHelpButton('availablefrom', 'availablefrom', 'condition'); - $mform->addElement('date_selector', 'availableuntil', get_string('availableuntil', 'condition'), array('optional'=>true)); + $mform->addElement('date_time_selector', 'availableuntil', + get_string('availableuntil', 'condition'), + array('optional' => true, 'defaulttime' => $midnight)); // Conditions based on grades $gradeoptions = array(); diff --git a/lang/en/condition.php b/lang/en/condition.php index 5c0fd77844673..ed05e324f2be9 100644 --- a/lang/en/condition.php +++ b/lang/en/condition.php @@ -57,6 +57,7 @@ $string['requires_date'] = 'Available from {$a}.'; $string['requires_date_before'] = 'Available until {$a}.'; $string['requires_date_both'] = 'Available from {$a->from} to {$a->until}.'; +$string['requires_date_both_single_day'] = 'Available on {$a}.'; $string['requires_grade_any'] = 'Not available until you have a grade in {$a}.'; $string['requires_grade_max'] = 'Not available unless you get an appropriate score in {$a}.'; $string['requires_grade_min'] = 'Not available until you achieve a required score in {$a}.'; diff --git a/lib/conditionlib.php b/lib/conditionlib.php index 83b79d343d282..f3279a19dbc2f 100644 --- a/lib/conditionlib.php +++ b/lib/conditionlib.php @@ -331,24 +331,83 @@ public function get_full_information($modinfo=null) { } } - // Dates + // The date logic is complicated. The intention of this logic is: + // 1) display date without time where possible (whenever the date is + // midnight) + // 2) when the 'until' date is e.g. 00:00 on the 14th, we display it as + // 'until the 13th' (experience at the OU showed that students are + // likely to interpret 'until ' as 'until the end of '). + // 3) This behaviour becomes confusing for 'same-day' dates where there + // are some exceptions. + // Users in different time zones will typically not get the 'abbreviated' + // behaviour but it should work OK for them aside from that. + + // The following cases are possible: + // a) From 13:05 on 14 Oct until 12:10 on 17 Oct (exact, exact) + // b) From 14 Oct until 12:11 on 17 Oct (midnight, exact) + // c) From 13:05 on 14 Oct until 17 Oct (exact, midnight 18 Oct) + // d) From 14 Oct until 17 Oct (midnight 14 Oct, midnight 18 Oct) + // e) On 14 Oct (midnight 14 Oct, midnight 15 Oct) + // f) From 13:05 on 14 Oct until 0:00 on 15 Oct (exact, midnight, same day) + // g) From 0:00 on 14 Oct until 12:05 on 14 Oct (midnight, exact, same day) + // h) From 13:05 on 14 Oct (exact) + // i) From 14 Oct (midnight) + // j) Until 13:05 on 14 Oct (exact) + // k) Until 14 Oct (midnight 15 Oct) + + // Check if start and end dates are 'midnights', if so we show in short form + $shortfrom = self::is_midnight($this->cm->availablefrom); + $shortuntil = self::is_midnight($this->cm->availableuntil); + + // For some checks and for display, we need the previous day for the 'until' + // value, if we are going to display it in short form + if ($this->cm->availableuntil) { + $daybeforeuntil = strtotime("-1 day", usergetmidnight($this->cm->availableuntil)); + } + + // Special case for if one but not both are exact and they are within a day + if ($this->cm->availablefrom && $this->cm->availableuntil && + $shortfrom != $shortuntil && $daybeforeuntil < $this->cm->availablefrom) { + // Don't use abbreviated version (see examples f, g above) + $shortfrom = false; + $shortuntil = false; + } + + // When showing short end date, the display time is the 'day before' one + $displayuntil = $shortuntil ? $daybeforeuntil : $this->cm->availableuntil; + if ($this->cm->availablefrom && $this->cm->availableuntil) { - $information .= get_string('requires_date_both', 'condition', - (object)array( - 'from' => self::show_time($this->cm->availablefrom, false), - 'until' => self::show_time($this->cm->availableuntil, true))); + if ($shortfrom && $shortuntil && $daybeforeuntil == $this->cm->availablefrom) { + $information .= get_string('requires_date_both_single_day', 'condition', + self::show_time($this->cm->availablefrom, true)); + } else { + $information .= get_string('requires_date_both', 'condition', (object)array( + 'from' => self::show_time($this->cm->availablefrom, $shortfrom), + 'until' => self::show_time($displayuntil, $shortuntil))); + } } else if ($this->cm->availablefrom) { $information .= get_string('requires_date', 'condition', - self::show_time($this->cm->availablefrom, false)); + self::show_time($this->cm->availablefrom, $shortfrom)); } else if ($this->cm->availableuntil) { $information .= get_string('requires_date_before', 'condition', - self::show_time($this->cm->availableuntil, true)); + self::show_time($displayuntil, $shortuntil)); } $information = trim($information); return $information; } + /** + * Checks whether a given time refers exactly to midnight (in current user + * timezone). + * @param int $time Time + * @return bool True if time refers to midnight, false if it's some other + * time or if it is set to zero + */ + private static function is_midnight($time) { + return $time && usergetmidnight($time) == $time; + } + /** * Determines whether this particular course-module is currently available * according to these criteria. @@ -467,7 +526,8 @@ public function is_available(&$information, $grabthelot=false, $userid=0, $modin $available = false; $information .= get_string('requires_date', 'condition', - self::show_time($this->cm->availablefrom, false)); + self::show_time($this->cm->availablefrom, + self::is_midnight($this->cm->availablefrom))); } } @@ -491,30 +551,15 @@ public function is_available(&$information, $grabthelot=false, $userid=0, $modin } /** - * Shows a time either as a date (if it falls exactly on the day) or - * a full date and time, according to user's timezone. - * + * Shows a time either as a date or a full date and time, according to + * user's timezone. * @param int $time Time - * @param bool $until True if this date should be treated as the second of - * an inclusive pair - if so the time will be shown unless date is 23:59:59. - * Without this the date shows for 0:00:00. + * @param bool $dateonly If true, uses date only * @return string Date */ - private function show_time($time, $until) { - // Break down the time into fields - $userdate = usergetdate($time); - - // Handle the 'inclusive' second date - if($until) { - $dateonly = $userdate['hours']==23 && $userdate['minutes']==59 && - $userdate['seconds']==59; - } else { - $dateonly = $userdate['hours']==0 && $userdate['minutes']==0 && - $userdate['seconds']==0; - } - - return userdate($time, get_string( - $dateonly ? 'strftimedate' : 'strftimedatetime', 'langconfig')); + private function show_time($time, $dateonly) { + return userdate($time, + get_string($dateonly ? 'strftimedate' : 'strftimedatetime', 'langconfig')); } /** diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index ad4056b047d7e..cea5a0f9e907f 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -6792,6 +6792,24 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2011100700.02); } + if ($oldversion < 2011101200.01) { + // The conditional availability date system used to rely on dates being + // set to 23:59:59 for the end date, but now that exact times are + // supported, it uses midnight on the following day. + + // The query is restricted on 'time mod 10 = 9' in order that + // it is safe to run this upgrade twice if something goes wrong. + $DB->execute('UPDATE {course_modules} SET availableuntil = availableuntil + 1 ' . + 'WHERE availableuntil > 0 AND ' . $DB->sql_modulo('availableuntil', 10) . ' = 9'); + + // Because availableuntil is stored in modinfo, we need to clear modinfo + // for all courses. + rebuild_course_cache(0, true); + + // Main savepoint reached + upgrade_main_savepoint(true, 2011101200.01); + } + return true; } diff --git a/lib/form/datetimeselector.php b/lib/form/datetimeselector.php index 94b7dfeb11fcd..957d7a39a6272 100644 --- a/lib/form/datetimeselector.php +++ b/lib/form/datetimeselector.php @@ -40,12 +40,13 @@ class MoodleQuickForm_date_time_selector extends MoodleQuickForm_group{ * * startyear => integer start of range of years that can be selected * stopyear => integer last year that can be selected + * defaulttime => default time value if the field is currently not set * timezone => float/string timezone * applydst => apply users daylight savings adjustment? * step => step to increment minutes by * optional => if true, show a checkbox beside the date to turn it on (or off) */ - var $_options = array('startyear' => 1970, 'stopyear' => 2020, + var $_options = array('startyear' => 1970, 'stopyear' => 2020, 'defaulttime' => 0, 'timezone' => 99, 'applydst' => true, 'step' => 5, 'optional' => false); /** @@ -159,7 +160,10 @@ function onQuickFormEvent($event, $arg, &$caller) } $requestvalue=$value; if ($value == 0) { - $value = time(); + $value = $this->_options['defaulttime']; + if (!$value) { + $value = time(); + } } if (!is_array($value)) { $currentdate = usergetdate($value); diff --git a/version.php b/version.php index 0c2e50847e355..91e35361a1273 100644 --- a/version.php +++ b/version.php @@ -31,7 +31,7 @@ -$version = 2011101200.00; // YYYYMMDD = weekly release date of this DEV branch +$version = 2011101200.01; // YYYYMMDD = weekly release date of this DEV branch // RR = release increments - 00 in DEV branches // .XX = incremental changes From 73ca5f01c5c51d9db99c736d627bf8f9c04ce1ca Mon Sep 17 00:00:00 2001 From: sam marshall Date: Mon, 10 Oct 2011 17:25:06 +0100 Subject: [PATCH 14/67] MDL-29501 Gradebook should not show links to activities that aren't available to user --- grade/lib.php | 72 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/grade/lib.php b/grade/lib.php index b558e0f3c7d8d..343cdc5b6d601 100644 --- a/grade/lib.php +++ b/grade/lib.php @@ -1002,6 +1002,14 @@ class grade_structure { public $courseid; + /** + * Reference to modinfo for current course (for performance, to save + * retrieving it from courseid every time). Not actually set except for + * the grade_tree type. + * @var course_modinfo + */ + public $modinfo; + /** * 1D array of grade items only */ @@ -1104,8 +1112,6 @@ public function get_element_icon(&$element, $spacerifnone=false) { * @return string header */ public function get_element_header(&$element, $withlink=false, $icon=true, $spacerifnone=false) { - global $CFG; - $header = ''; if ($icon) { @@ -1119,31 +1125,54 @@ public function get_element_header(&$element, $withlink=false, $icon=true, $spac return $header; } - $itemtype = $element['object']->itemtype; - $itemmodule = $element['object']->itemmodule; - $iteminstance = $element['object']->iteminstance; - - if ($withlink and $itemtype=='mod' and $iteminstance and $itemmodule) { - if ($cm = get_coursemodule_from_instance($itemmodule, $iteminstance, $this->courseid)) { - + if ($withlink) { + $url = $this->get_activity_link($element); + if ($url) { $a = new stdClass(); $a->name = get_string('modulename', $element['object']->itemmodule); $title = get_string('linktoactivity', 'grades', $a); - $dir = $CFG->dirroot.'/mod/'.$itemmodule; - if (file_exists($dir.'/grade.php')) { - $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/grade.php?id='.$cm->id; - } else { - $url = $CFG->wwwroot.'/mod/'.$itemmodule.'/view.php?id='.$cm->id; - } - - $header = ''.$header.''; + $header = html_writer::link($url, $header, array('title' => $title)); } } return $header; } + private function get_activity_link($element) { + global $CFG; + + $itemtype = $element['object']->itemtype; + $itemmodule = $element['object']->itemmodule; + $iteminstance = $element['object']->iteminstance; + + // Links only for module items that have valid instance, module and are + // called from grade_tree with valid modinfo + if ($itemtype != 'mod' || !$iteminstance || !$itemmodule || !$this->modinfo) { + return null; + } + + // Get $cm efficiently and with visibility information using modinfo + $instances = $this->modinfo->get_instances(); + if (empty($instances[$itemmodule][$iteminstance])) { + return null; + } + $cm = $instances[$itemmodule][$iteminstance]; + + // Do not add link if activity is not visible to the current user + if (!$cm->uservisible) { + return null; + } + + // If module has grade.php, link to that, otherwise view.php + $dir = $CFG->dirroot . '/mod/' . $itemmodule; + if (file_exists($dir.'/grade.php')) { + return new moodle_url('/mod/' . $itemmodule . '/grade.php', array('id' => $cm->id)); + } else { + return new moodle_url('/mod/' . $itemmodule . '/view.php', array('id' => $cm->id)); + } + } + /** * Returns the grade eid - the grade may not exist yet. * @@ -1600,12 +1629,19 @@ class grade_tree extends grade_structure { */ public function grade_tree($courseid, $fillers=true, $category_grade_last=false, $collapsed=null, $nooutcomes=false) { - global $USER, $CFG; + global $USER, $CFG, $COURSE, $DB; $this->courseid = $courseid; $this->levels = array(); $this->context = get_context_instance(CONTEXT_COURSE, $courseid); + if (!empty($COURSE->id) && $COURSE->id == $this->courseid) { + $course = $COURSE; + } else { + $course = $DB->get_record('course', array('id' => $this->courseid)); + } + $this->modinfo = get_fast_modinfo($course); + // get course grade tree $this->top_element = grade_category::fetch_course_tree($courseid, true); From a28a5d74af1db4eeb12e194c0c9db111522458d2 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 3 Oct 2011 16:10:48 +0100 Subject: [PATCH 15/67] MDL-29627 refactor quiz access rules into separate subplugins. --- mod/quiz/accessrule/accessrulebase.php | 108 ++++ .../en/quizaccess_delaybetweenattempts.php | 30 + .../accessrule/delaybetweenattempts/rule.php | 90 +++ .../delaybetweenattempts/version.php | 32 ++ .../lang/en/quizaccess_ipaddress.php | 30 + mod/quiz/accessrule/ipaddress/rule.php | 46 ++ mod/quiz/accessrule/ipaddress/version.php | 32 ++ .../lang/en/quizaccess_numattempts.php | 30 + mod/quiz/accessrule/numattempts/rule.php | 51 ++ mod/quiz/accessrule/numattempts/version.php | 32 ++ .../lang/en/quizaccess_openclosedate.php | 30 + mod/quiz/accessrule/openclosedate/rule.php | 85 +++ mod/quiz/accessrule/openclosedate/version.php | 32 ++ .../password/lang/en/quizaccess_password.php | 30 + mod/quiz/accessrule/password/rule.php | 145 +++++ mod/quiz/accessrule/password/version.php | 32 ++ .../lang/en/quizaccess_safebrowser.php | 30 + mod/quiz/accessrule/safebrowser/rule.php | 50 ++ mod/quiz/accessrule/safebrowser/version.php | 32 ++ .../lang/en/quizaccess_securewindow.php | 30 + mod/quiz/accessrule/securewindow/rule.php | 87 +++ mod/quiz/accessrule/securewindow/version.php | 32 ++ .../lang/en/quizaccess_timelimit.php | 30 + mod/quiz/accessrule/timelimit/rule.php | 47 ++ mod/quiz/accessrule/timelimit/version.php | 32 ++ mod/quiz/accessrules.php | 540 ++---------------- mod/quiz/db/subplugins.php | 5 +- 27 files changed, 1261 insertions(+), 489 deletions(-) create mode 100644 mod/quiz/accessrule/accessrulebase.php create mode 100644 mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php create mode 100644 mod/quiz/accessrule/delaybetweenattempts/rule.php create mode 100644 mod/quiz/accessrule/delaybetweenattempts/version.php create mode 100644 mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php create mode 100644 mod/quiz/accessrule/ipaddress/rule.php create mode 100644 mod/quiz/accessrule/ipaddress/version.php create mode 100644 mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php create mode 100644 mod/quiz/accessrule/numattempts/rule.php create mode 100644 mod/quiz/accessrule/numattempts/version.php create mode 100644 mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php create mode 100644 mod/quiz/accessrule/openclosedate/rule.php create mode 100644 mod/quiz/accessrule/openclosedate/version.php create mode 100644 mod/quiz/accessrule/password/lang/en/quizaccess_password.php create mode 100644 mod/quiz/accessrule/password/rule.php create mode 100644 mod/quiz/accessrule/password/version.php create mode 100644 mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php create mode 100644 mod/quiz/accessrule/safebrowser/rule.php create mode 100644 mod/quiz/accessrule/safebrowser/version.php create mode 100644 mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php create mode 100644 mod/quiz/accessrule/securewindow/rule.php create mode 100644 mod/quiz/accessrule/securewindow/version.php create mode 100644 mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php create mode 100644 mod/quiz/accessrule/timelimit/rule.php create mode 100644 mod/quiz/accessrule/timelimit/version.php diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php new file mode 100644 index 0000000000000..9ef9ef36e6ba4 --- /dev/null +++ b/mod/quiz/accessrule/accessrulebase.php @@ -0,0 +1,108 @@ +. + +/** + * Base class for rules that restrict the ability to attempt a quiz. + * + * @package mod + * @subpackage quiz + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * A base class that defines the interface for the various quiz access rules. + * Most of the methods are defined in a slightly unnatural way because we either + * want to say that access is allowed, or explain the reason why it is block. + * Therefore instead of is_access_allowed(...) we have prevent_access(...) that + * return false if access is permitted, or a string explanation (which is treated + * as true) if access should be blocked. Slighly unnatural, but acutally the easist + * way to implement this. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class quiz_access_rule_base { + protected $_quiz; + protected $_quizobj; + protected $_timenow; + /** + * Create an instance of this rule for a particular quiz. + * @param object $quiz the quiz we will be controlling access to. + */ + public function __construct($quizobj, $timenow) { + $this->_quizobj = $quizobj; + $this->_quiz = $quizobj->get_quiz(); + $this->_timenow = $timenow; + } + /** + * Whether or not a user should be allowed to start a new attempt at this quiz now. + * @param int $numattempts the number of previous attempts this user has made. + * @param object $lastattempt information about the user's last completed attempt. + * @return string false if access should be allowed, a message explaining the + * reason if access should be prevented. + */ + public function prevent_new_attempt($numprevattempts, $lastattempt) { + return false; + } + /** + * Whether or not a user should be allowed to start a new attempt at this quiz now. + * @return string false if access should be allowed, a message explaining the + * reason if access should be prevented. + */ + public function prevent_access() { + return false; + } + /** + * Information, such as might be shown on the quiz view page, relating to this restriction. + * There is no obligation to return anything. If it is not appropriate to tell students + * about this rule, then just return ''. + * @return mixed a message, or array of messages, explaining the restriction + * (may be '' if no message is appropriate). + */ + public function description() { + return ''; + } + /** + * If this rule can determine that this user will never be allowed another attempt at + * this quiz, then return true. This is used so we can know whether to display a + * final grade on the view page. This will only be called if there is not a currently + * active attempt for this user. + * @param int $numattempts the number of previous attempts this user has made. + * @param object $lastattempt information about the user's last completed attempt. + * @return bool true if this rule means that this user will never be allowed another + * attempt at this quiz. + */ + public function is_finished($numprevattempts, $lastattempt) { + return false; + } + + /** + * If, because of this rule, the user has to finish their attempt by a certain time, + * you should override this method to return the amount of time left in seconds. + * @param object $attempt the current attempt + * @param int $timenow the time now. We don't use $this->_timenow, so we can + * give the user a more accurate indication of how much time is left. + * @return mixed false if there is no deadline, of the time left in seconds if there is one. + */ + public function time_left($attempt, $timenow) { + return false; + } +} diff --git a/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php b/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php new file mode 100644 index 0000000000000..ab749318b7995 --- /dev/null +++ b/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_delaybetweenattempts plugin. + * + * @package quizaccess + * @subpackage delaybetweenattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Delay between attempts quiz access rule'; diff --git a/mod/quiz/accessrule/delaybetweenattempts/rule.php b/mod/quiz/accessrule/delaybetweenattempts/rule.php new file mode 100644 index 0000000000000..a441b85d52d00 --- /dev/null +++ b/mod/quiz/accessrule/delaybetweenattempts/rule.php @@ -0,0 +1,90 @@ +. + +/** + * Implementaton of the quizaccess_delaybetweenattempts plugin. + * + * @package quizaccess + * @subpackage delaybetweenattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** +* A rule imposing the delay between attempts settings. +* +* @copyright 2009 Tim Hunt +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +class quizaccess_delaybetweenattempts extends quiz_access_rule_base { + public function prevent_new_attempt($numprevattempts, $lastattempt) { + if ($this->_quiz->attempts > 0 && $numprevattempts >= $this->_quiz->attempts) { + // No more attempts allowed anyway. + return false; + } + if ($this->_quiz->timeclose != 0 && $this->_timenow > $this->_quiz->timeclose) { + // No more attempts allowed anyway. + return false; + } + $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); + if ($this->_timenow < $nextstarttime) { + if ($this->_quiz->timeclose == 0 || $nextstarttime <= $this->_quiz->timeclose) { + return get_string('youmustwait', 'quiz', userdate($nextstarttime)); + } else { + return get_string('youcannotwait', 'quiz'); + } + } + return false; + } + + /** + * Compute the next time a student would be allowed to start an attempt, + * according to this rule. + * @param int $numprevattempts number of previous attempts. + * @param object $lastattempt information about the previous attempt. + * @return number the time. + */ + protected function compute_next_start_time($numprevattempts, $lastattempt) { + if ($numprevattempts == 0) { + return 0; + } + + $lastattemptfinish = $lastattempt->timefinish; + if ($this->_quiz->timelimit > 0) { + $lastattemptfinish = min($lastattemptfinish, + $lastattempt->timestart + $this->_quiz->timelimit); + } + + if ($numprevattempts == 1 && $this->_quiz->delay1) { + return $lastattemptfinish + $this->_quiz->delay1; + } else if ($numprevattempts > 1 && $this->_quiz->delay2) { + return $lastattemptfinish + $this->_quiz->delay2; + } + return 0; + } + + public function is_finished($numprevattempts, $lastattempt) { + $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); + return $this->_timenow <= $nextstarttime && + $this->_quiz->timeclose != 0 && $nextstarttime >= $this->_quiz->timeclose; + } +} diff --git a/mod/quiz/accessrule/delaybetweenattempts/version.php b/mod/quiz/accessrule/delaybetweenattempts/version.php new file mode 100644 index 0000000000000..5ca8604bbf360 --- /dev/null +++ b/mod/quiz/accessrule/delaybetweenattempts/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_delaybetweenattempts plugin. + * + * @package quizaccess + * @subpackage delaybetweenattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_delaybetweenattempts'; diff --git a/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php b/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php new file mode 100644 index 0000000000000..f56a768b89733 --- /dev/null +++ b/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_ipaddress plugin. + * + * @package quizaccess + * @subpackage ipaddress + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'IP address quiz access rule'; diff --git a/mod/quiz/accessrule/ipaddress/rule.php b/mod/quiz/accessrule/ipaddress/rule.php new file mode 100644 index 0000000000000..785520ee436be --- /dev/null +++ b/mod/quiz/accessrule/ipaddress/rule.php @@ -0,0 +1,46 @@ +. + +/** + * Implementaton of the quizaccess_ipaddress plugin. + * + * @package quizaccess + * @subpackage ipaddress + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** + * A rule implementing the ipaddress check against the ->submet setting. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_ipaddress extends quiz_access_rule_base { + public function prevent_access() { + if (address_in_subnet(getremoteaddr(), $this->_quiz->subnet)) { + return false; + } else { + return get_string('subnetwrong', 'quiz'); + } + } +} diff --git a/mod/quiz/accessrule/ipaddress/version.php b/mod/quiz/accessrule/ipaddress/version.php new file mode 100644 index 0000000000000..47f7ab6258053 --- /dev/null +++ b/mod/quiz/accessrule/ipaddress/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_ipaddress plugin. + * + * @package quizaccess + * @subpackage ipaddress + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_ipaddress'; diff --git a/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php b/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php new file mode 100644 index 0000000000000..075af113b1698 --- /dev/null +++ b/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_numattempts plugin. + * + * @package quizaccess + * @subpackage numattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Number of attempts quiz access rule'; diff --git a/mod/quiz/accessrule/numattempts/rule.php b/mod/quiz/accessrule/numattempts/rule.php new file mode 100644 index 0000000000000..42be84d393f03 --- /dev/null +++ b/mod/quiz/accessrule/numattempts/rule.php @@ -0,0 +1,51 @@ +. + +/** + * Implementaton of the quizaccess_numattempts plugin. + * + * @package quizaccess + * @subpackage numattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** +* A rule controlling the number of attempts allowed. +* +* @copyright 2009 Tim Hunt +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +class quizaccess_numattempts extends quiz_access_rule_base { + public function description() { + return get_string('attemptsallowedn', 'quiz', $this->_quiz->attempts); + } + public function prevent_new_attempt($numprevattempts, $lastattempt) { + if ($numprevattempts >= $this->_quiz->attempts) { + return get_string('nomoreattempts', 'quiz'); + } + return false; + } + public function is_finished($numprevattempts, $lastattempt) { + return $numprevattempts >= $this->_quiz->attempts; + } +} diff --git a/mod/quiz/accessrule/numattempts/version.php b/mod/quiz/accessrule/numattempts/version.php new file mode 100644 index 0000000000000..0503ad7657e19 --- /dev/null +++ b/mod/quiz/accessrule/numattempts/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_numattempts plugin. + * + * @package quizaccess + * @subpackage numattempts + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_numattempts'; diff --git a/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php b/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php new file mode 100644 index 0000000000000..4e2a9137fac00 --- /dev/null +++ b/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_openclosedate plugin. + * + * @package quizaccess + * @subpackage openclosedate + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Open and close date access rule'; diff --git a/mod/quiz/accessrule/openclosedate/rule.php b/mod/quiz/accessrule/openclosedate/rule.php new file mode 100644 index 0000000000000..63eb98899b298 --- /dev/null +++ b/mod/quiz/accessrule/openclosedate/rule.php @@ -0,0 +1,85 @@ +. + +/** + * Implementaton of the quizaccess_openclosedate plugin. + * + * @package quizaccess + * @subpackage openclosedate + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** + * A rule enforcing open and close dates. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_openclosedate extends quiz_access_rule_base { + public function description() { + $result = array(); + if ($this->_timenow < $this->_quiz->timeopen) { + $result[] = get_string('quiznotavailable', 'quiz', userdate($this->_quiz->timeopen)); + } else if ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose) { + $result[] = get_string("quizclosed", "quiz", userdate($this->_quiz->timeclose)); + } else { + if ($this->_quiz->timeopen) { + $result[] = get_string('quizopenedon', 'quiz', userdate($this->_quiz->timeopen)); + } + if ($this->_quiz->timeclose) { + $result[] = get_string('quizcloseson', 'quiz', userdate($this->_quiz->timeclose)); + } + } + return $result; + } + + public function prevent_access() { + if ($this->_timenow < $this->_quiz->timeopen || + ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose)) { + return get_string('notavailable', 'quiz'); + } + return false; + } + + public function is_finished($numprevattempts, $lastattempt) { + return $this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose; + } + + public function time_left($attempt, $timenow) { + // If this is a teacher preview after the close date, do not show + // the time. + if ($attempt->preview && $timenow > $this->_quiz->timeclose) { + return false; + } + + // Otherwise, return to the time left until the close date, providing + // that is less than QUIZ_SHOW_TIME_BEFORE_DEADLINE + if ($this->_quiz->timeclose) { + $timeleft = $this->_quiz->timeclose - $timenow; + if ($timeleft < QUIZ_SHOW_TIME_BEFORE_DEADLINE) { + return $timeleft; + } + } + return false; + } +} diff --git a/mod/quiz/accessrule/openclosedate/version.php b/mod/quiz/accessrule/openclosedate/version.php new file mode 100644 index 0000000000000..7f0258dbd26a6 --- /dev/null +++ b/mod/quiz/accessrule/openclosedate/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_openclosedate plugin. + * + * @package quizaccess + * @subpackage openclosedate + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_openclosedate'; diff --git a/mod/quiz/accessrule/password/lang/en/quizaccess_password.php b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php new file mode 100644 index 0000000000000..415770321de68 --- /dev/null +++ b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_password plugin. + * + * @package quizaccess + * @subpackage password + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Password quiz access rule'; diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php new file mode 100644 index 0000000000000..c6960b434da59 --- /dev/null +++ b/mod/quiz/accessrule/password/rule.php @@ -0,0 +1,145 @@ +. + +/** + * Implementaton of the quizaccess_password plugin. + * + * @package quizaccess + * @subpackage password + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** + * A rule representing the password check. It does not actually implement the check, + * that has to be done directly in attempt.php, but this facilitates telling users about it. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_password extends quiz_access_rule_base { + public function description() { + return get_string('requirepasswordmessage', 'quiz'); + } + + /** + * Clear the flag in the session that says that the current user is allowed to do this quiz. + */ + public function clear_access_allowed() { + global $SESSION; + if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { + unset($SESSION->passwordcheckedquizzes[$this->_quiz->id]); + } + } + + /** + * Actually ask the user for the password, if they have not already given it this session. + * This function only returns is access is OK. + * + * @param bool $canpreview used to enfore securewindow stuff. + * @param object $accessmanager the accessmanager calling us. + * @return mixed return null, unless $return is true, and a form needs to be displayed. + */ + public function do_password_check($canpreview, $accessmanager) { + global $CFG, $SESSION, $OUTPUT, $PAGE; + + // We have already checked the password for this quiz this session, so don't ask again. + if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { + return; + } + + // If the user cancelled the password form, send them back to the view page. + if (optional_param('cancelpassword', false, PARAM_BOOL)) { + $accessmanager->back_to_view_page($canpreview); + } + + // If they entered the right password, let them in. + $enteredpassword = optional_param('quizpassword', '', PARAM_RAW); + $validpassword = false; + if (strcmp($this->_quiz->password, $enteredpassword) === 0) { + $validpassword = true; + } else if (isset($this->_quiz->extrapasswords)) { + // group overrides may have additional passwords + foreach ($this->_quiz->extrapasswords as $password) { + if (strcmp($password, $enteredpassword) === 0) { + $validpassword = true; + break; + } + } + } + if ($validpassword) { + $SESSION->passwordcheckedquizzes[$this->_quiz->id] = true; + return; + } + + // User entered the wrong password, or has not entered one yet, so display the form. + $output = ''; + + // Start the page and print the quiz intro, if any. + if ($accessmanager->securewindow_required($canpreview)) { + $accessmanager->setup_secure_page($this->_quizobj->get_course()->shortname . ': ' . + format_string($this->_quizobj->get_quiz_name())); + } else if ($accessmanager->safebrowser_required($canpreview)) { + $PAGE->set_title($this->_quizobj->get_course()->shortname . ': ' . + format_string($this->_quizobj->get_quiz_name())); + $PAGE->set_cacheable(false); + } else { + $PAGE->set_title(format_string($this->_quizobj->get_quiz_name())); + } + + echo $OUTPUT->header(); + if (trim(strip_tags($this->_quiz->intro))) { + $output .= $OUTPUT->box(format_module_intro('quiz', $this->_quiz, + $this->_quizobj->get_cmid()), 'generalbox', 'intro'); + } + $output .= $OUTPUT->box_start('generalbox', 'passwordbox'); + + // If they have previously tried and failed to enter a password, tell them it was wrong. + if (!empty($enteredpassword)) { + $output .= '

    ' . get_string('passworderror', 'quiz') . '

    '; + } + + // Print the password entry form. + $output .= '

    ' . get_string('requirepasswordmessage', 'quiz') . "

    \n"; + $output .= '
    ' . "\n"; + $output .= "
    \n"; + $output .= '\n"; + $output .= '' . "\n"; + $output .= '' . "\n"; + $output .= '' . "\n"; + $output .= ''; + $output .= '' . "\n"; + $output .= "
    \n"; + $output .= "
    \n"; + + // Finish page. + $output .= $OUTPUT->box_end(); + + // return or display form. + echo $output; + echo $OUTPUT->footer(); + exit; + } +} diff --git a/mod/quiz/accessrule/password/version.php b/mod/quiz/accessrule/password/version.php new file mode 100644 index 0000000000000..7fd0defaf169f --- /dev/null +++ b/mod/quiz/accessrule/password/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_password plugin. + * + * @package quizaccess + * @subpackage password + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_password'; diff --git a/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php b/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php new file mode 100644 index 0000000000000..e49137c74ce59 --- /dev/null +++ b/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_safebrowser plugin. + * + * @package quizaccess + * @subpackage safebrowser + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Safe Exam Browser quiz access rule'; diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php new file mode 100644 index 0000000000000..c7939341d66fd --- /dev/null +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -0,0 +1,50 @@ +. + +/** + * Implementaton of the quizaccess_safebrowser plugin. + * + * @package quizaccess + * @subpackage safebrowser + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** +* A rule representing the safe browser check. +* +* @copyright 2009 Oliver Rahs +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +class safebrowser_access_rule extends quiz_access_rule_base { + public function prevent_access() { + if (!$this->_quizobj->is_preview_user() && !quiz_check_safe_browser()) { + return get_string('safebrowsererror', 'quiz'); + } else { + return false; + } + } + + public function description() { + return get_string("safebrowsernotice", "quiz"); + } +} diff --git a/mod/quiz/accessrule/safebrowser/version.php b/mod/quiz/accessrule/safebrowser/version.php new file mode 100644 index 0000000000000..c2e217e437cef --- /dev/null +++ b/mod/quiz/accessrule/safebrowser/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_safebrowser plugin. + * + * @package quizaccess + * @subpackage safebrowser + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_safebrowser'; diff --git a/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php b/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php new file mode 100644 index 0000000000000..9fa8292acc7dd --- /dev/null +++ b/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_securewindow plugin. + * + * @package quizaccess + * @subpackage securewindow + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'JavaScript security quiz access rule'; diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php new file mode 100644 index 0000000000000..072f25f3ab9a9 --- /dev/null +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -0,0 +1,87 @@ +. + +/** + * Implementaton of the quizaccess_securewindow plugin. + * + * @package quizaccess + * @subpackage securewindow + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** +* A rule for ensuring that the quiz is opened in a popup, with some JavaScript +* to prevent copying and pasting, etc. +* +* @copyright 2009 Tim Hunt +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +class quizaccess_securewindow extends quiz_access_rule_base { + /** @var array options that should be used for opening the secure popup. */ + public static $popupoptions = array( + 'left' => 0, + 'top' => 0, + 'fullscreen' => true, + 'scrollbars' => true, + 'resizeable' => false, + 'directories' => false, + 'toolbar' => false, + 'titlebar' => false, + 'location' => false, + 'status' => false, + 'menubar' => false, + ); + + /** + * Make a link to the review page for an attempt. + * + * @param string $linktext the desired link text. + * @param int $attemptid the attempt id. + * @return string HTML for the link. + */ + public function make_review_link($linktext, $attemptid) { + global $OUTPUT; + $url = $this->_quizobj->review_url($attemptid); + $button = new single_button($url, $linktext); + $button->add_action(new popup_action('click', $url, 'quizpopup', self::$popupoptions)); + return $OUTPUT->render($button); + } + + /** + * Do the printheader call, etc. required for a secure page, including the necessary JS. + * + * @param string $title HTML title tag content, passed to printheader. + * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader. + * $headtags has been deprectaed since Moodle 2.0 + */ + public function setup_secure_page($title, $headtags=null) { + global $PAGE; + $PAGE->set_popup_notification_allowed(false);//prevent message notifications + $PAGE->set_title($title); + $PAGE->set_cacheable(false); + $PAGE->set_pagelayout('popup'); + $PAGE->add_body_class('quiz-secure-window'); + $PAGE->requires->js_init_call('M.mod_quiz.secure_window.init', + null, false, quiz_get_js_module()); + } +} diff --git a/mod/quiz/accessrule/securewindow/version.php b/mod/quiz/accessrule/securewindow/version.php new file mode 100644 index 0000000000000..84cb288e3b3b3 --- /dev/null +++ b/mod/quiz/accessrule/securewindow/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_securewindow plugin. + * + * @package quizaccess + * @subpackage securewindow + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_securewindow'; diff --git a/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php b/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php new file mode 100644 index 0000000000000..1d030905cde1d --- /dev/null +++ b/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php @@ -0,0 +1,30 @@ +. + +/** + * Strings for the quizaccess_timelimit plugin. + * + * @package quizaccess + * @subpackage timelimit + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$string['pluginname'] = 'Time limit quiz access rule'; diff --git a/mod/quiz/accessrule/timelimit/rule.php b/mod/quiz/accessrule/timelimit/rule.php new file mode 100644 index 0000000000000..4a7b9224c5090 --- /dev/null +++ b/mod/quiz/accessrule/timelimit/rule.php @@ -0,0 +1,47 @@ +. + +/** + * Implementaton of the quizaccess_timelimit plugin. + * + * @package quizaccess + * @subpackage timelimit + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/accessrulebase.php'); + + +/** +* A rule representing the time limit. It does not actually restrict access, but we use this +* class to encapsulate some of the relevant code. +* +* @copyright 2009 Tim Hunt +* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later +*/ +class quizaccess_timelimit extends quiz_access_rule_base { + public function description() { + return get_string('quiztimelimit', 'quiz', format_time($this->_quiz->timelimit)); + } + + public function time_left($attempt, $timenow) { + return $attempt->timestart + $this->_quiz->timelimit - $timenow; + } +} diff --git a/mod/quiz/accessrule/timelimit/version.php b/mod/quiz/accessrule/timelimit/version.php new file mode 100644 index 0000000000000..ac926353578a7 --- /dev/null +++ b/mod/quiz/accessrule/timelimit/version.php @@ -0,0 +1,32 @@ +. + +/** + * Version information for the quizaccess_delaybetweenattempts plugin. + * + * @package quizaccess + * @subpackage timelimit + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2011092300; +$plugin->requires = 2011091600; +$plugin->component = 'quizaccess_timelimit'; diff --git a/mod/quiz/accessrules.php b/mod/quiz/accessrules.php index d31729b628f0f..d4b8fd35ca30c 100644 --- a/mod/quiz/accessrules.php +++ b/mod/quiz/accessrules.php @@ -35,12 +35,12 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quiz_access_manager { - private $_quizobj; - private $_timenow; - private $_passwordrule = null; - private $_securewindowrule = null; - private $_safebrowserrule = null; - private $_rules = array(); + protected $quizobj; + protected $timenow; + protected $passwordrule = null; + protected $securewindowrule = null; + protected $safebrowserrule = null; + protected $rules = array(); /** * Create an instance for a particular quiz. @@ -51,49 +51,52 @@ class quiz_access_manager { * limits (has_capability('mod/quiz:ignoretimelimits', ...)). */ public function __construct($quizobj, $timenow, $canignoretimelimits) { - $this->_quizobj = $quizobj; - $this->_timenow = $timenow; + $this->quizobj = $quizobj; + $this->timenow = $timenow; $this->create_standard_rules($canignoretimelimits); } - private function create_standard_rules($canignoretimelimits) { - $quiz = $this->_quizobj->get_quiz(); + protected function create_standard_rules($canignoretimelimits) { + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + + $quiz = $this->quizobj->get_quiz(); if ($quiz->attempts > 0) { - $this->_rules[] = new num_attempts_access_rule($this->_quizobj, $this->_timenow); + $this->rules[] = new quizaccess_numattempts($this->quizobj, $this->timenow); } - $this->_rules[] = new open_close_date_access_rule($this->_quizobj, $this->_timenow); + $this->rules[] = new quizaccess_openclosedate($this->quizobj, $this->timenow); if (!empty($quiz->timelimit) && !$canignoretimelimits) { - $this->_rules[] = new time_limit_access_rule($this->_quizobj, $this->_timenow); + $this->rules[] = new quizaccess_timelimit($this->quizobj, $this->timenow); } if (!empty($quiz->delay1) || !empty($quiz->delay2)) { - $this->_rules[] = new inter_attempt_delay_access_rule($this->_quizobj, $this->_timenow); + $this->rules[] = new quizaccess_delaybetweenattempts($this->quizobj, $this->timenow); } if (!empty($quiz->subnet)) { - $this->_rules[] = new ipaddress_access_rule($this->_quizobj, $this->_timenow); + $this->rules[] = new quizaccess_ipaddress($this->quizobj, $this->timenow); } if (!empty($quiz->password)) { - $this->_passwordrule = new password_access_rule($this->_quizobj, $this->_timenow); - $this->_rules[] = $this->_passwordrule; + $this->passwordrule = new quizaccess_password($this->quizobj, $this->timenow); + $this->rules[] = $this->passwordrule; } if (!empty($quiz->popup)) { if ($quiz->popup == 1) { - $this->_securewindowrule = new securewindow_access_rule( - $this->_quizobj, $this->_timenow); - $this->_rules[] = $this->_securewindowrule; + $this->securewindowrule = new quizaccess_securewindow( + $this->quizobj, $this->timenow); + $this->rules[] = $this->securewindowrule; } else if ($quiz->popup == 2) { - $this->_safebrowserrule = new safebrowser_access_rule( - $this->_quizobj, $this->_timenow); - $this->_rules[] = $this->_safebrowserrule; + $this->safebrowserrule = new quizaccess_securebrowser( + $this->quizobj, $this->timenow); + $this->rules[] = $this->safebrowserrule; } } } - private function accumulate_messages(&$messages, $new) { + protected function accumulate_messages(&$messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); } else if (is_string($new) && $new) { $messages[] = $new; } + return $messages; } /** @@ -106,8 +109,8 @@ private function accumulate_messages(&$messages, $new) { */ public function describe_rules() { $result = array(); - foreach ($this->_rules as $rule) { - $this->accumulate_messages($result, $rule->description()); + foreach ($this->rules as $rule) { + $result = $this->accumulate_messages($result, $rule->description()); } return $result; } @@ -125,8 +128,8 @@ public function describe_rules() { */ public function prevent_new_attempt($numprevattempts, $lastattempt) { $reasons = array(); - foreach ($this->_rules as $rule) { - $this->accumulate_messages($reasons, + foreach ($this->rules as $rule) { + $reasons = $this->accumulate_messages($reasons, $rule->prevent_new_attempt($numprevattempts, $lastattempt)); } return $reasons; @@ -142,8 +145,8 @@ public function prevent_new_attempt($numprevattempts, $lastattempt) { */ public function prevent_access() { $reasons = array(); - foreach ($this->_rules as $rule) { - $this->accumulate_messages($reasons, $rule->prevent_access()); + foreach ($this->rules as $rule) { + $reasons = $this->accumulate_messages($reasons, $rule->prevent_access()); } return $reasons; } @@ -159,7 +162,7 @@ public function prevent_access() { * this quiz again. */ public function is_finished($numprevattempts, $lastattempt) { - foreach ($this->_rules as $rule) { + foreach ($this->rules as $rule) { if ($rule->is_finished($numprevattempts, $lastattempt)) { return true; } @@ -174,13 +177,13 @@ public function is_finished($numprevattempts, $lastattempt) { * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader. */ public function setup_secure_page($title, $headtags = null) { - $this->_securewindowrule->setup_secure_page($title, $headtags); + $this->securewindowrule->setup_secure_page($title, $headtags); } public function show_attempt_timer_if_needed($attempt, $timenow) { global $PAGE; $timeleft = false; - foreach ($this->_rules as $rule) { + foreach ($this->rules as $rule) { $ruletimeleft = $rule->time_left($attempt, $timenow); if ($ruletimeleft !== false && ($timeleft === false || $ruletimeleft < $timeleft)) { $timeleft = $ruletimeleft; @@ -199,14 +202,14 @@ public function show_attempt_timer_if_needed($attempt, $timenow) { * @return bolean if this quiz should only be shown to students in a secure window. */ public function securewindow_required($canpreview) { - return !$canpreview && !is_null($this->_securewindowrule); + return !$canpreview && !is_null($this->securewindowrule); } /** * @return bolean if this quiz should only be shown to students with safe browser. */ public function safebrowser_required($canpreview) { - return !$canpreview && !is_null($this->_safebrowserrule); + return !$canpreview && !is_null($this->safebrowserrule); } /** @@ -224,7 +227,7 @@ public function safebrowser_required($canpreview) { public function print_start_attempt_button($canpreview, $buttontext, $unfinished) { global $OUTPUT; - $url = $this->_quizobj->start_attempt_url(); + $url = $this->quizobj->start_attempt_url(); $button = new single_button($url, $buttontext); $button->class .= ' quizstartbuttondiv'; @@ -259,7 +262,7 @@ public function print_start_attempt_button($canpreview, $buttontext, $unfinished */ public function back_to_view_page($canpreview, $message = '') { global $CFG, $OUTPUT, $PAGE; - $url = $this->_quizobj->view_url(); + $url = $this->quizobj->view_url(); if ($this->securewindow_required($canpreview)) { $PAGE->set_pagelayout('popup'); echo $OUTPUT->header(); @@ -290,7 +293,7 @@ public function back_to_view_page($canpreview, $message = '') { public function print_finish_review_link($canpreview, $return = false) { global $CFG; $output = ''; - $url = $this->_quizobj->view_url(); + $url = $this->quizobj->view_url(); $output .= '
    '; if ($this->securewindow_required($canpreview)) { $url = addslashes_js(htmlspecialchars($url)); @@ -311,15 +314,15 @@ public function print_finish_review_link($canpreview, $return = false) { * @return bolean if this quiz is password protected. */ public function password_required() { - return !is_null($this->_passwordrule); + return !is_null($this->passwordrule); } /** * Clear the flag in the session that says that the current user is allowed to do this quiz. */ public function clear_password_access() { - if (!is_null($this->_passwordrule)) { - $this->_passwordrule->clear_access_allowed(); + if (!is_null($this->passwordrule)) { + $this->passwordrule->clear_access_allowed(); } } @@ -330,8 +333,8 @@ public function clear_password_access() { * @param bool $canpreview used to enfore securewindow stuff. */ public function do_password_check($canpreview) { - if (!is_null($this->_passwordrule)) { - $this->_passwordrule->do_password_check($canpreview, $this); + if (!is_null($this->passwordrule)) { + $this->passwordrule->do_password_check($canpreview, $this); } } @@ -340,7 +343,7 @@ public function do_password_check($canpreview) { * in a javascript alert on the start attempt button. */ public function confirm_start_attempt_message() { - $quiz = $this->_quizobj->get_quiz(); + $quiz = $this->quizobj->get_quiz(); if ($quiz->timelimit && $quiz->attempts) { return get_string('confirmstartattempttimelimit', 'quiz', $quiz->attempts); } else if ($quiz->timelimit) { @@ -367,9 +370,9 @@ public function make_review_link($attempt, $canpreview, $reviewoptions) { return ''; } - $when = quiz_attempt_state($this->_quizobj->get_quiz(), $attempt); + $when = quiz_attempt_state($this->quizobj->get_quiz(), $attempt); $reviewoptions = mod_quiz_display_options::make_from_quiz( - $this->_quizobj->get_quiz(), $when); + $this->quizobj->get_quiz(), $when); if (!$reviewoptions->attempt) { $message = $this->cannot_review_message($when, true); @@ -384,9 +387,9 @@ public function make_review_link($attempt, $canpreview, $reviewoptions) { // It is OK to link, does it need to be in a secure window? if ($this->securewindow_required($canpreview)) { - return $this->_securewindowrule->make_review_link($linktext, $attempt->id); + return $this->securewindowrule->make_review_link($linktext, $attempt->id); } else { - return 'quizobj->review_url($attempt->id) . '" title="' . get_string('reviewthisattempt', 'quiz') . '">' . $linktext . ''; } } @@ -401,7 +404,7 @@ public function make_review_link($attempt, $canpreview, $reviewoptions) { * @return string an appropraite message. */ public function cannot_review_message($when, $short = false) { - $quiz = $this->_quizobj->get_quiz(); + $quiz = $this->quizobj->get_quiz(); if ($short) { $langstrsuffix = 'short'; $dateformat = get_string('strftimedatetimeshort', 'langconfig'); @@ -421,442 +424,3 @@ public function cannot_review_message($when, $short = false) { } } } - - -/** - * A base class that defines the interface for the various quiz access rules. - * Most of the methods are defined in a slightly unnatural way because we either - * want to say that access is allowed, or explain the reason why it is block. - * Therefore instead of is_access_allowed(...) we have prevent_access(...) that - * return false if access is permitted, or a string explanation (which is treated - * as true) if access should be blocked. Slighly unnatural, but acutally the easist - * way to implement this. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -abstract class quiz_access_rule_base { - protected $_quiz; - protected $_quizobj; - protected $_timenow; - /** - * Create an instance of this rule for a particular quiz. - * @param object $quiz the quiz we will be controlling access to. - */ - public function __construct($quizobj, $timenow) { - $this->_quizobj = $quizobj; - $this->_quiz = $quizobj->get_quiz(); - $this->_timenow = $timenow; - } - /** - * Whether or not a user should be allowed to start a new attempt at this quiz now. - * @param int $numattempts the number of previous attempts this user has made. - * @param object $lastattempt information about the user's last completed attempt. - * @return string false if access should be allowed, a message explaining the - * reason if access should be prevented. - */ - public function prevent_new_attempt($numprevattempts, $lastattempt) { - return false; - } - /** - * Whether or not a user should be allowed to start a new attempt at this quiz now. - * @return string false if access should be allowed, a message explaining the - * reason if access should be prevented. - */ - public function prevent_access() { - return false; - } - /** - * Information, such as might be shown on the quiz view page, relating to this restriction. - * There is no obligation to return anything. If it is not appropriate to tell students - * about this rule, then just return ''. - * @return mixed a message, or array of messages, explaining the restriction - * (may be '' if no message is appropriate). - */ - public function description() { - return ''; - } - /** - * If this rule can determine that this user will never be allowed another attempt at - * this quiz, then return true. This is used so we can know whether to display a - * final grade on the view page. This will only be called if there is not a currently - * active attempt for this user. - * @param int $numattempts the number of previous attempts this user has made. - * @param object $lastattempt information about the user's last completed attempt. - * @return bool true if this rule means that this user will never be allowed another - * attempt at this quiz. - */ - public function is_finished($numprevattempts, $lastattempt) { - return false; - } - - /** - * If, because of this rule, the user has to finish their attempt by a certain time, - * you should override this method to return the amount of time left in seconds. - * @param object $attempt the current attempt - * @param int $timenow the time now. We don't use $this->_timenow, so we can - * give the user a more accurate indication of how much time is left. - * @return mixed false if there is no deadline, of the time left in seconds if there is one. - */ - public function time_left($attempt, $timenow) { - return false; - } -} - -/** - * A rule controlling the number of attempts allowed. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class num_attempts_access_rule extends quiz_access_rule_base { - public function description() { - return get_string('attemptsallowedn', 'quiz', $this->_quiz->attempts); - } - public function prevent_new_attempt($numprevattempts, $lastattempt) { - if ($numprevattempts >= $this->_quiz->attempts) { - return get_string('nomoreattempts', 'quiz'); - } - return false; - } - public function is_finished($numprevattempts, $lastattempt) { - return $numprevattempts >= $this->_quiz->attempts; - } -} - -/** - * A rule enforcing open and close dates. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class open_close_date_access_rule extends quiz_access_rule_base { - public function description() { - $result = array(); - if ($this->_timenow < $this->_quiz->timeopen) { - $result[] = get_string('quiznotavailable', 'quiz', userdate($this->_quiz->timeopen)); - } else if ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose) { - $result[] = get_string("quizclosed", "quiz", userdate($this->_quiz->timeclose)); - } else { - if ($this->_quiz->timeopen) { - $result[] = get_string('quizopenedon', 'quiz', userdate($this->_quiz->timeopen)); - } - if ($this->_quiz->timeclose) { - $result[] = get_string('quizcloseson', 'quiz', userdate($this->_quiz->timeclose)); - } - } - return $result; - } - public function prevent_access() { - if ($this->_timenow < $this->_quiz->timeopen || - ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose)) { - return get_string('notavailable', 'quiz'); - } - return false; - } - public function is_finished($numprevattempts, $lastattempt) { - return $this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose; - } - public function time_left($attempt, $timenow) { - // If this is a teacher preview after the close date, do not show - // the time. - if ($attempt->preview && $timenow > $this->_quiz->timeclose) { - return false; - } - - // Otherwise, return to the time left until the close date, providing - // that is less than QUIZ_SHOW_TIME_BEFORE_DEADLINE - if ($this->_quiz->timeclose) { - $timeleft = $this->_quiz->timeclose - $timenow; - if ($timeleft < QUIZ_SHOW_TIME_BEFORE_DEADLINE) { - return $timeleft; - } - } - return false; - } -} - -/** - * A rule imposing the delay between attempts settings. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class inter_attempt_delay_access_rule extends quiz_access_rule_base { - public function prevent_new_attempt($numprevattempts, $lastattempt) { - if ($this->_quiz->attempts > 0 && $numprevattempts >= $this->_quiz->attempts) { - // No more attempts allowed anyway. - return false; - } - if ($this->_quiz->timeclose != 0 && $this->_timenow > $this->_quiz->timeclose) { - // No more attempts allowed anyway. - return false; - } - $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); - if ($this->_timenow < $nextstarttime) { - if ($this->_quiz->timeclose == 0 || $nextstarttime <= $this->_quiz->timeclose) { - return get_string('youmustwait', 'quiz', userdate($nextstarttime)); - } else { - return get_string('youcannotwait', 'quiz'); - } - } - return false; - } - - /** - * Compute the next time a student would be allowed to start an attempt, - * according to this rule. - * @param int $numprevattempts number of previous attempts. - * @param object $lastattempt information about the previous attempt. - * @return number the time. - */ - protected function compute_next_start_time($numprevattempts, $lastattempt) { - if ($numprevattempts == 0) { - return 0; - } - - $lastattemptfinish = $lastattempt->timefinish; - if ($this->_quiz->timelimit > 0) { - $lastattemptfinish = min($lastattemptfinish, - $lastattempt->timestart + $this->_quiz->timelimit); - } - - if ($numprevattempts == 1 && $this->_quiz->delay1) { - return $lastattemptfinish + $this->_quiz->delay1; - } else if ($numprevattempts > 1 && $this->_quiz->delay2) { - return $lastattemptfinish + $this->_quiz->delay2; - } - return 0; - } - - public function is_finished($numprevattempts, $lastattempt) { - $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); - return $this->_timenow <= $nextstarttime && - $this->_quiz->timeclose != 0 && $nextstarttime >= $this->_quiz->timeclose; - } -} - -/** - * A rule implementing the ipaddress check against the ->submet setting. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class ipaddress_access_rule extends quiz_access_rule_base { - public function prevent_access() { - if (address_in_subnet(getremoteaddr(), $this->_quiz->subnet)) { - return false; - } else { - return get_string('subnetwrong', 'quiz'); - } - } -} - -/** - * A rule representing the password check. It does not actually implement the check, - * that has to be done directly in attempt.php, but this facilitates telling users about it. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class password_access_rule extends quiz_access_rule_base { - public function description() { - return get_string('requirepasswordmessage', 'quiz'); - } - /** - * Clear the flag in the session that says that the current user is allowed to do this quiz. - */ - public function clear_access_allowed() { - global $SESSION; - if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { - unset($SESSION->passwordcheckedquizzes[$this->_quiz->id]); - } - } - /** - * Actually ask the user for the password, if they have not already given it this session. - * This function only returns is access is OK. - * - * @param bool $canpreview used to enfore securewindow stuff. - * @param object $accessmanager the accessmanager calling us. - * @return mixed return null, unless $return is true, and a form needs to be displayed. - */ - public function do_password_check($canpreview, $accessmanager) { - global $CFG, $SESSION, $OUTPUT, $PAGE; - - // We have already checked the password for this quiz this session, so don't ask again. - if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { - return; - } - - // If the user cancelled the password form, send them back to the view page. - if (optional_param('cancelpassword', false, PARAM_BOOL)) { - $accessmanager->back_to_view_page($canpreview); - } - - // If they entered the right password, let them in. - $enteredpassword = optional_param('quizpassword', '', PARAM_RAW); - $validpassword = false; - if (strcmp($this->_quiz->password, $enteredpassword) === 0) { - $validpassword = true; - } else if (isset($this->_quiz->extrapasswords)) { - // group overrides may have additional passwords - foreach ($this->_quiz->extrapasswords as $password) { - if (strcmp($password, $enteredpassword) === 0) { - $validpassword = true; - break; - } - } - } - if ($validpassword) { - $SESSION->passwordcheckedquizzes[$this->_quiz->id] = true; - return; - } - - // User entered the wrong password, or has not entered one yet, so display the form. - $output = ''; - - // Start the page and print the quiz intro, if any. - if ($accessmanager->securewindow_required($canpreview)) { - $accessmanager->setup_secure_page($this->_quizobj->get_course()->shortname . ': ' . - format_string($this->_quizobj->get_quiz_name())); - } else if ($accessmanager->safebrowser_required($canpreview)) { - $PAGE->set_title($this->_quizobj->get_course()->shortname . ': ' . - format_string($this->_quizobj->get_quiz_name())); - $PAGE->set_cacheable(false); - } else { - $PAGE->set_title(format_string($this->_quizobj->get_quiz_name())); - } - - echo $OUTPUT->header(); - if (trim(strip_tags($this->_quiz->intro))) { - $output .= $OUTPUT->box(format_module_intro('quiz', $this->_quiz, - $this->_quizobj->get_cmid()), 'generalbox', 'intro'); - } - $output .= $OUTPUT->box_start('generalbox', 'passwordbox'); - - // If they have previously tried and failed to enter a password, tell them it was wrong. - if (!empty($enteredpassword)) { - $output .= '

    ' . get_string('passworderror', 'quiz') . '

    '; - } - - // Print the password entry form. - $output .= '

    ' . get_string('requirepasswordmessage', 'quiz') . "

    \n"; - $output .= '
    ' . "\n"; - $output .= "
    \n"; - $output .= '\n"; - $output .= '' . "\n"; - $output .= '' . "\n"; - $output .= '' . "\n"; - $output .= ''; - $output .= '' . "\n"; - $output .= "
    \n"; - $output .= "
    \n"; - - // Finish page. - $output .= $OUTPUT->box_end(); - - // return or display form. - echo $output; - echo $OUTPUT->footer(); - exit; - } -} - -/** - * A rule representing the time limit. It does not actually restrict access, but we use this - * class to encapsulate some of the relevant code. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class time_limit_access_rule extends quiz_access_rule_base { - public function description() { - return get_string('quiztimelimit', 'quiz', format_time($this->_quiz->timelimit)); - } - public function time_left($attempt, $timenow) { - return $attempt->timestart + $this->_quiz->timelimit - $timenow; - } -} - -/** - * A rule for ensuring that the quiz is opened in a popup, with some JavaScript - * to prevent copying and pasting, etc. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class securewindow_access_rule extends quiz_access_rule_base { - /** - * @var array options that should be used for opening the secure popup. - */ - public static $popupoptions = array( - 'left' => 0, - 'top' => 0, - 'fullscreen' => true, - 'scrollbars' => true, - 'resizeable' => false, - 'directories' => false, - 'toolbar' => false, - 'titlebar' => false, - 'location' => false, - 'status' => false, - 'menubar' => false, - ); - - /** - * Make a link to the review page for an attempt. - * - * @param string $linktext the desired link text. - * @param int $attemptid the attempt id. - * @return string HTML for the link. - */ - public function make_review_link($linktext, $attemptid) { - global $OUTPUT; - $url = $this->_quizobj->review_url($attemptid); - $button = new single_button($url, $linktext); - $button->add_action(new popup_action('click', $url, 'quizpopup', self::$popupoptions)); - return $OUTPUT->render($button); - } - - /** - * Do the printheader call, etc. required for a secure page, including the necessary JS. - * - * @param string $title HTML title tag content, passed to printheader. - * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader. - * $headtags has been deprectaed since Moodle 2.0 - */ - public function setup_secure_page($title, $headtags=null) { - global $PAGE; - $PAGE->set_popup_notification_allowed(false);//prevent message notifications - $PAGE->set_title($title); - $PAGE->set_cacheable(false); - $PAGE->set_pagelayout('popup'); - $PAGE->add_body_class('quiz-secure-window'); - $PAGE->requires->js_init_call('M.mod_quiz.secure_window.init', null, false, - quiz_get_js_module()); - } -} - - -/** - * A rule representing the safe browser check. - * - * @copyright 2009 Oliver Rahs - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class safebrowser_access_rule extends quiz_access_rule_base { - public function prevent_access() { - if (!$this->_quizobj->is_preview_user() && !quiz_check_safe_browser()) { - return get_string('safebrowsererror', 'quiz'); - } else { - return false; - } - } - - public function description() { - return get_string("safebrowsernotice", "quiz"); - } -} diff --git a/mod/quiz/db/subplugins.php b/mod/quiz/db/subplugins.php index 9b63ff1896f61..1a8724041b8a8 100644 --- a/mod/quiz/db/subplugins.php +++ b/mod/quiz/db/subplugins.php @@ -25,4 +25,7 @@ defined('MOODLE_INTERNAL') || die(); -$subplugins = array('quiz' => 'mod/quiz/report'); +$subplugins = array( + 'quiz' => 'mod/quiz/report', + 'quizaccess' => 'mod/quiz/accessrule', +); From e5844040a0a363197e9e310e77db68237ecf8169 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 3 Oct 2011 17:40:01 +0100 Subject: [PATCH 16/67] MDL-29627 quiz access: fix variable names that don't match the coding style. --- mod/quiz/accessrule/accessrulebase.php | 15 +++++---- .../accessrule/delaybetweenattempts/rule.php | 24 +++++++------- mod/quiz/accessrule/ipaddress/rule.php | 2 +- mod/quiz/accessrule/numattempts/rule.php | 6 ++-- mod/quiz/accessrule/openclosedate/rule.php | 28 ++++++++-------- mod/quiz/accessrule/password/rule.php | 32 +++++++++---------- .../lang/en/quizaccess_safebrowser.php | 4 +-- mod/quiz/accessrule/safebrowser/rule.php | 2 +- mod/quiz/accessrule/securewindow/rule.php | 2 +- mod/quiz/accessrule/timelimit/rule.php | 4 +-- 10 files changed, 60 insertions(+), 59 deletions(-) diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index 9ef9ef36e6ba4..bfe8e16d1204c 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -40,17 +40,18 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class quiz_access_rule_base { - protected $_quiz; - protected $_quizobj; - protected $_timenow; + protected $quiz; + protected $quizobj; + protected $timenow; + /** * Create an instance of this rule for a particular quiz. * @param object $quiz the quiz we will be controlling access to. */ public function __construct($quizobj, $timenow) { - $this->_quizobj = $quizobj; - $this->_quiz = $quizobj->get_quiz(); - $this->_timenow = $timenow; + $this->quizobj = $quizobj; + $this->quiz = $quizobj->get_quiz(); + $this->timenow = $timenow; } /** * Whether or not a user should be allowed to start a new attempt at this quiz now. @@ -98,7 +99,7 @@ public function is_finished($numprevattempts, $lastattempt) { * If, because of this rule, the user has to finish their attempt by a certain time, * you should override this method to return the amount of time left in seconds. * @param object $attempt the current attempt - * @param int $timenow the time now. We don't use $this->_timenow, so we can + * @param int $timenow the time now. We don't use $this->timenow, so we can * give the user a more accurate indication of how much time is left. * @return mixed false if there is no deadline, of the time left in seconds if there is one. */ diff --git a/mod/quiz/accessrule/delaybetweenattempts/rule.php b/mod/quiz/accessrule/delaybetweenattempts/rule.php index a441b85d52d00..95831e931fbe9 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/rule.php +++ b/mod/quiz/accessrule/delaybetweenattempts/rule.php @@ -37,17 +37,17 @@ */ class quizaccess_delaybetweenattempts extends quiz_access_rule_base { public function prevent_new_attempt($numprevattempts, $lastattempt) { - if ($this->_quiz->attempts > 0 && $numprevattempts >= $this->_quiz->attempts) { + if ($this->quiz->attempts > 0 && $numprevattempts >= $this->quiz->attempts) { // No more attempts allowed anyway. return false; } - if ($this->_quiz->timeclose != 0 && $this->_timenow > $this->_quiz->timeclose) { + if ($this->quiz->timeclose != 0 && $this->timenow > $this->quiz->timeclose) { // No more attempts allowed anyway. return false; } $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); - if ($this->_timenow < $nextstarttime) { - if ($this->_quiz->timeclose == 0 || $nextstarttime <= $this->_quiz->timeclose) { + if ($this->timenow < $nextstarttime) { + if ($this->quiz->timeclose == 0 || $nextstarttime <= $this->quiz->timeclose) { return get_string('youmustwait', 'quiz', userdate($nextstarttime)); } else { return get_string('youcannotwait', 'quiz'); @@ -69,22 +69,22 @@ protected function compute_next_start_time($numprevattempts, $lastattempt) { } $lastattemptfinish = $lastattempt->timefinish; - if ($this->_quiz->timelimit > 0) { + if ($this->quiz->timelimit > 0) { $lastattemptfinish = min($lastattemptfinish, - $lastattempt->timestart + $this->_quiz->timelimit); + $lastattempt->timestart + $this->quiz->timelimit); } - if ($numprevattempts == 1 && $this->_quiz->delay1) { - return $lastattemptfinish + $this->_quiz->delay1; - } else if ($numprevattempts > 1 && $this->_quiz->delay2) { - return $lastattemptfinish + $this->_quiz->delay2; + if ($numprevattempts == 1 && $this->quiz->delay1) { + return $lastattemptfinish + $this->quiz->delay1; + } else if ($numprevattempts > 1 && $this->quiz->delay2) { + return $lastattemptfinish + $this->quiz->delay2; } return 0; } public function is_finished($numprevattempts, $lastattempt) { $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); - return $this->_timenow <= $nextstarttime && - $this->_quiz->timeclose != 0 && $nextstarttime >= $this->_quiz->timeclose; + return $this->timenow <= $nextstarttime && + $this->quiz->timeclose != 0 && $nextstarttime >= $this->quiz->timeclose; } } diff --git a/mod/quiz/accessrule/ipaddress/rule.php b/mod/quiz/accessrule/ipaddress/rule.php index 785520ee436be..02d29be583d63 100644 --- a/mod/quiz/accessrule/ipaddress/rule.php +++ b/mod/quiz/accessrule/ipaddress/rule.php @@ -37,7 +37,7 @@ */ class quizaccess_ipaddress extends quiz_access_rule_base { public function prevent_access() { - if (address_in_subnet(getremoteaddr(), $this->_quiz->subnet)) { + if (address_in_subnet(getremoteaddr(), $this->quiz->subnet)) { return false; } else { return get_string('subnetwrong', 'quiz'); diff --git a/mod/quiz/accessrule/numattempts/rule.php b/mod/quiz/accessrule/numattempts/rule.php index 42be84d393f03..b9ef538a6bbc9 100644 --- a/mod/quiz/accessrule/numattempts/rule.php +++ b/mod/quiz/accessrule/numattempts/rule.php @@ -37,15 +37,15 @@ */ class quizaccess_numattempts extends quiz_access_rule_base { public function description() { - return get_string('attemptsallowedn', 'quiz', $this->_quiz->attempts); + return get_string('attemptsallowedn', 'quiz', $this->quiz->attempts); } public function prevent_new_attempt($numprevattempts, $lastattempt) { - if ($numprevattempts >= $this->_quiz->attempts) { + if ($numprevattempts >= $this->quiz->attempts) { return get_string('nomoreattempts', 'quiz'); } return false; } public function is_finished($numprevattempts, $lastattempt) { - return $numprevattempts >= $this->_quiz->attempts; + return $numprevattempts >= $this->quiz->attempts; } } diff --git a/mod/quiz/accessrule/openclosedate/rule.php b/mod/quiz/accessrule/openclosedate/rule.php index 63eb98899b298..a62c4a8829d5a 100644 --- a/mod/quiz/accessrule/openclosedate/rule.php +++ b/mod/quiz/accessrule/openclosedate/rule.php @@ -38,44 +38,44 @@ class quizaccess_openclosedate extends quiz_access_rule_base { public function description() { $result = array(); - if ($this->_timenow < $this->_quiz->timeopen) { - $result[] = get_string('quiznotavailable', 'quiz', userdate($this->_quiz->timeopen)); - } else if ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose) { - $result[] = get_string("quizclosed", "quiz", userdate($this->_quiz->timeclose)); + if ($this->timenow < $this->quiz->timeopen) { + $result[] = get_string('quiznotavailable', 'quiz', userdate($this->quiz->timeopen)); + } else if ($this->quiz->timeclose && $this->timenow > $this->quiz->timeclose) { + $result[] = get_string("quizclosed", "quiz", userdate($this->quiz->timeclose)); } else { - if ($this->_quiz->timeopen) { - $result[] = get_string('quizopenedon', 'quiz', userdate($this->_quiz->timeopen)); + if ($this->quiz->timeopen) { + $result[] = get_string('quizopenedon', 'quiz', userdate($this->quiz->timeopen)); } - if ($this->_quiz->timeclose) { - $result[] = get_string('quizcloseson', 'quiz', userdate($this->_quiz->timeclose)); + if ($this->quiz->timeclose) { + $result[] = get_string('quizcloseson', 'quiz', userdate($this->quiz->timeclose)); } } return $result; } public function prevent_access() { - if ($this->_timenow < $this->_quiz->timeopen || - ($this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose)) { + if ($this->timenow < $this->quiz->timeopen || + ($this->quiz->timeclose && $this->timenow > $this->quiz->timeclose)) { return get_string('notavailable', 'quiz'); } return false; } public function is_finished($numprevattempts, $lastattempt) { - return $this->_quiz->timeclose && $this->_timenow > $this->_quiz->timeclose; + return $this->quiz->timeclose && $this->timenow > $this->quiz->timeclose; } public function time_left($attempt, $timenow) { // If this is a teacher preview after the close date, do not show // the time. - if ($attempt->preview && $timenow > $this->_quiz->timeclose) { + if ($attempt->preview && $timenow > $this->quiz->timeclose) { return false; } // Otherwise, return to the time left until the close date, providing // that is less than QUIZ_SHOW_TIME_BEFORE_DEADLINE - if ($this->_quiz->timeclose) { - $timeleft = $this->_quiz->timeclose - $timenow; + if ($this->quiz->timeclose) { + $timeleft = $this->quiz->timeclose - $timenow; if ($timeleft < QUIZ_SHOW_TIME_BEFORE_DEADLINE) { return $timeleft; } diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php index c6960b434da59..f47e4c5f742b7 100644 --- a/mod/quiz/accessrule/password/rule.php +++ b/mod/quiz/accessrule/password/rule.php @@ -46,8 +46,8 @@ public function description() { */ public function clear_access_allowed() { global $SESSION; - if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { - unset($SESSION->passwordcheckedquizzes[$this->_quiz->id]); + if (!empty($SESSION->passwordcheckedquizzes[$this->quiz->id])) { + unset($SESSION->passwordcheckedquizzes[$this->quiz->id]); } } @@ -63,7 +63,7 @@ public function do_password_check($canpreview, $accessmanager) { global $CFG, $SESSION, $OUTPUT, $PAGE; // We have already checked the password for this quiz this session, so don't ask again. - if (!empty($SESSION->passwordcheckedquizzes[$this->_quiz->id])) { + if (!empty($SESSION->passwordcheckedquizzes[$this->quiz->id])) { return; } @@ -75,11 +75,11 @@ public function do_password_check($canpreview, $accessmanager) { // If they entered the right password, let them in. $enteredpassword = optional_param('quizpassword', '', PARAM_RAW); $validpassword = false; - if (strcmp($this->_quiz->password, $enteredpassword) === 0) { + if (strcmp($this->quiz->password, $enteredpassword) === 0) { $validpassword = true; - } else if (isset($this->_quiz->extrapasswords)) { + } else if (isset($this->quiz->extrapasswords)) { // group overrides may have additional passwords - foreach ($this->_quiz->extrapasswords as $password) { + foreach ($this->quiz->extrapasswords as $password) { if (strcmp($password, $enteredpassword) === 0) { $validpassword = true; break; @@ -87,7 +87,7 @@ public function do_password_check($canpreview, $accessmanager) { } } if ($validpassword) { - $SESSION->passwordcheckedquizzes[$this->_quiz->id] = true; + $SESSION->passwordcheckedquizzes[$this->quiz->id] = true; return; } @@ -96,20 +96,20 @@ public function do_password_check($canpreview, $accessmanager) { // Start the page and print the quiz intro, if any. if ($accessmanager->securewindow_required($canpreview)) { - $accessmanager->setup_secure_page($this->_quizobj->get_course()->shortname . ': ' . - format_string($this->_quizobj->get_quiz_name())); + $accessmanager->setup_secure_page($this->quizobj->get_course()->shortname . ': ' . + format_string($this->quizobj->get_quiz_name())); } else if ($accessmanager->safebrowser_required($canpreview)) { - $PAGE->set_title($this->_quizobj->get_course()->shortname . ': ' . - format_string($this->_quizobj->get_quiz_name())); + $PAGE->set_title($this->quizobj->get_course()->shortname . ': ' . + format_string($this->quizobj->get_quiz_name())); $PAGE->set_cacheable(false); } else { - $PAGE->set_title(format_string($this->_quizobj->get_quiz_name())); + $PAGE->set_title(format_string($this->quizobj->get_quiz_name())); } echo $OUTPUT->header(); - if (trim(strip_tags($this->_quiz->intro))) { - $output .= $OUTPUT->box(format_module_intro('quiz', $this->_quiz, - $this->_quizobj->get_cmid()), 'generalbox', 'intro'); + if (trim(strip_tags($this->quiz->intro))) { + $output .= $OUTPUT->box(format_module_intro('quiz', $this->quiz, + $this->quizobj->get_cmid()), 'generalbox', 'intro'); } $output .= $OUTPUT->box_start('generalbox', 'passwordbox'); @@ -126,7 +126,7 @@ public function do_password_check($canpreview, $accessmanager) { $output .= '\n"; $output .= '' . "\n"; $output .= '' . "\n"; + $this->quizobj->get_cmid() . '"/>' . "\n"; $output .= '' . "\n"; $output .= ''; $output .= '. + +/** + * Unit tests for the quizaccess_safebrowser plugin. + * + * @package quizaccess + * @subpackage safebrowser + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/safebrowser/rule.php'); + + +/** + * Unit tests for the quizaccess_safebrowser plugin. + * + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_safebrowser_test extends UnitTestCase { + public static $includecoverage = array('mod/quiz/accessrule/safebrowser/rule.php'); + + // Nothing very testable in this class, just test that it obeys the general access rule contact. + public function test_safebrowser_access_rule() { + $quiz = new stdClass(); + $quiz->popup = 1; + $quiz->questions = ''; + $cm = new stdClass(); + $cm->id = 0; + $quizobj = new quiz($quiz, $cm, null); + $rule = new quizaccess_safebrowser($quizobj, 0); + $attempt = new stdClass(); + + // This next test assumes the unit tests are not being run using Safe Exam Browser! + $this->assertEqual(get_string('safebrowsererror', 'quiz'), $rule->prevent_access()); + + $this->assertEqual(get_string('safebrowsernotice', 'quiz'), $rule->description()); + $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); + $this->assertFalse($rule->is_finished(0, $attempt)); + $this->assertFalse($rule->time_left($attempt, 1)); + } +} diff --git a/mod/quiz/accessrule/securewindow/simpletest/testrule.php b/mod/quiz/accessrule/securewindow/simpletest/testrule.php new file mode 100644 index 0000000000000..04e0801680574 --- /dev/null +++ b/mod/quiz/accessrule/securewindow/simpletest/testrule.php @@ -0,0 +1,58 @@ +. + +/** + * Unit tests for the quizaccess_securewindow plugin. + * + * @package quizaccess + * @subpackage securewindow + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/securewindow/rule.php'); + + +/** + * Unit tests for the quizaccess_securewindow plugin. + * + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_securewindow_test extends UnitTestCase { + public static $includecoverage = array('mod/quiz/accessrule/securewindow/rule.php'); + + // Nothing very testable in this class, just test that it obeys the general access rule contact. + public function test_securewindow_access_rule() { + $quiz = new stdClass(); + $quiz->popup = 1; + $quiz->questions = ''; + $cm = new stdClass(); + $cm->id = 0; + $quizobj = new quiz($quiz, $cm, null); + $rule = new quizaccess_securewindow($quizobj, 0); + $attempt = new stdClass(); + + $this->assertFalse($rule->prevent_access()); + $this->assertFalse($rule->description()); + $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); + $this->assertFalse($rule->is_finished(0, $attempt)); + $this->assertFalse($rule->time_left($attempt, 1)); + } +} diff --git a/mod/quiz/accessrule/timelimit/simpletest/testrule.php b/mod/quiz/accessrule/timelimit/simpletest/testrule.php new file mode 100644 index 0000000000000..eb1332a8e87d5 --- /dev/null +++ b/mod/quiz/accessrule/timelimit/simpletest/testrule.php @@ -0,0 +1,63 @@ +. + +/** + * Unit tests for the quizaccess_timelimit plugin. + * + * @package quizaccess + * @subpackage timelimit + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/accessrule/timelimit/rule.php'); + + +/** + * Unit tests for the quizaccess_timelimit plugin. + * + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quizaccess_timelimit_test extends UnitTestCase { + public static $includecoverage = array('mod/quiz/accessrule/timelimit/rule.php'); + + public function test_time_limit_access_rule() { + $quiz = new stdClass(); + $quiz->timelimit = 3600; + $quiz->questions = ''; + $cm = new stdClass(); + $cm->id = 0; + $quizobj = new quiz($quiz, $cm, null); + $rule = new quizaccess_timelimit($quizobj, 10000); + $attempt = new stdClass(); + + $this->assertEqual($rule->description(), + get_string('quiztimelimit', 'quiz', format_time(3600))); + + $attempt->timestart = 10000; + $this->assertEqual($rule->time_left($attempt, 10000), 3600); + $this->assertEqual($rule->time_left($attempt, 12000), 1600); + $this->assertEqual($rule->time_left($attempt, 14000), -400); + + $this->assertFalse($rule->prevent_access()); + $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); + $this->assertFalse($rule->is_finished(0, $attempt)); + } +} diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index d8c965c2808a0..a155ee74bc5ff 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -656,7 +656,7 @@ $string['reviewthisattempt'] = 'Review your responses to this attempt'; $string['rqp'] = 'Remote question'; $string['rqps'] = 'Remote questions'; -$string['safebrowsererror'] = 'This quiz has been set up so that it may only be attempted using the Safe Exam Browser. You cannot attempt it form this web browser.'; +$string['safebrowsererror'] = 'This quiz has been set up so that it may only be attempted using the Safe Exam Browser. You cannot attempt it from this web browser.'; $string['safebrowsernotice'] = 'This quiz has been configured so that students may only attempt it using the Safe Exam Browser.'; $string['sameasoverall'] = 'Same as for overall grades'; $string['save'] = 'Save'; diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index ae956d59f5000..e5fa7f6cf308b 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/mod/quiz/lib.php'); -require_once($CFG->dirroot . '/mod/quiz/accessrules.php'); +require_once($CFG->dirroot . '/mod/quiz/accessmanager.php'); require_once($CFG->dirroot . '/mod/quiz/renderer.php'); require_once($CFG->dirroot . '/mod/quiz/attemptlib.php'); require_once($CFG->dirroot . '/question/editlib.php'); @@ -1283,15 +1283,6 @@ function quiz_attempt_submitted_handler($event) { get_context_instance(CONTEXT_MODULE, $cm->id), $cm); } -/** - * Checks if browser is safe browser - * - * @return true, if browser is safe browser else false - */ -function quiz_check_safe_browser() { - return strpos($_SERVER['HTTP_USER_AGENT'], "SEB") !== false; -} - function quiz_get_js_module() { global $PAGE; diff --git a/mod/quiz/simpletest/testaccessmanager.php b/mod/quiz/simpletest/testaccessmanager.php new file mode 100644 index 0000000000000..bc9887d2ee821 --- /dev/null +++ b/mod/quiz/simpletest/testaccessmanager.php @@ -0,0 +1,70 @@ +. + +/** + * Unit tests for the access manager class. + * + * @package mod + * @subpackage quiz + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + + +/** + * Unit tests for the access manager class + * + * @copyright 2008 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class quiz_access_manager_test extends UnitTestCase { + public function test_cannot_review_message() { + $quiz = new stdClass(); + $quiz->reviewattempt = 0x10010; + $quiz->timeclose = 0; + $quiz->attempts = 0; + $quiz->questions = '1,2,0,3,4,0'; + + $cm = new stdClass(); + $cm->id = 123; + + $quizobj = new quiz($quiz, $cm, new stdClass(), false); + + $am = new quiz_access_manager($quizobj, time(), false); + + $this->assertEqual('', + $am->cannot_review_message(mod_quiz_display_options::DURING)); + $this->assertEqual('', + $am->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER)); + $this->assertEqual(get_string('noreview', 'quiz'), + $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); + $this->assertEqual(get_string('noreview', 'quiz'), + $am->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE)); + + $closetime = time() + 10000; + $quiz->timeclose = $closetime; + $quizobj = new quiz($quiz, $cm, new stdClass(), false); + $am = new quiz_access_manager($quizobj, time(), false); + + $this->assertEqual(get_string('noreviewuntil', 'quiz', userdate($closetime)), + $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); + } +} diff --git a/mod/quiz/simpletest/testaccessrules.php b/mod/quiz/simpletest/testaccessrules.php deleted file mode 100644 index 8048b37a4cb3f..0000000000000 --- a/mod/quiz/simpletest/testaccessrules.php +++ /dev/null @@ -1,614 +0,0 @@ -. - -/** - * Unit tests for (some of) mod/quiz/accessrules.php. - * - * @package mod - * @subpackage quiz - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/mod/quiz/locallib.php'); - - -/** - * Unit tests for (some of) mod/quiz/accessrules.php. - * - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class simple_rules_test extends UnitTestCase { - public static $includecoverage = array('mod/quiz/locallib.php'); - public function test_num_attempts_access_rule() { - $quiz = new stdClass(); - $quiz->attempts = 3; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $rule = new num_attempts_access_rule($quizobj, 0); - $attempt = new stdClass(); - - $this->assertEqual($rule->description(), get_string('attemptsallowedn', 'quiz', 3)); - - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('nomoreattempts', 'quiz')); - $this->assertEqual($rule->prevent_new_attempt(666, $attempt), - get_string('nomoreattempts', 'quiz')); - - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->is_finished(2, $attempt)); - $this->assertTrue($rule->is_finished(3, $attempt)); - $this->assertTrue($rule->is_finished(666, $attempt)); - - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->time_left($attempt, 1)); - } - - public function test_ipaddress_access_rule() { - $quiz = new stdClass(); - $attempt = new stdClass(); - $cm = new stdClass(); - $cm->id = 0; - - // Test the allowed case by getting the user's IP address. However, this - // does not always work, for example using the mac install package on my laptop. - $quiz->subnet = getremoteaddr(null); - if (!empty($quiz->subnet)) { - $quiz->questions = ''; - $quizobj = new quiz($quiz, $cm, null); - $rule = new ipaddress_access_rule($quizobj, 0); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 1)); - } - - $quiz->subnet = '0.0.0.0'; - $quiz->questions = ''; - $quizobj = new quiz($quiz, $cm, null); - $rule = new ipaddress_access_rule($quizobj, 0); - $this->assertTrue($rule->prevent_access()); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 1)); - } - - public function test_time_limit_access_rule() { - $quiz = new stdClass(); - $quiz->timelimit = 3600; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $rule = new time_limit_access_rule($quizobj, 10000); - $attempt = new stdClass(); - - $this->assertEqual($rule->description(), - get_string('quiztimelimit', 'quiz', format_time(3600))); - - $attempt->timestart = 10000; - $this->assertEqual($rule->time_left($attempt, 10000), 3600); - $this->assertEqual($rule->time_left($attempt, 12000), 1600); - $this->assertEqual($rule->time_left($attempt, 14000), -400); - - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - } -} - - -/** - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class open_close_date_access_rule_test extends UnitTestCase { - public function test_no_dates() { - $quiz = new stdClass(); - $quiz->timeopen = 0; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->preview = 0; - - $rule = new open_close_date_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 10000)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $rule = new open_close_date_access_rule($quizobj, 0); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - } - - public function test_start_date() { - $quiz = new stdClass(); - $quiz->timeopen = 10000; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->preview = 0; - - $rule = new open_close_date_access_rule($quizobj, 9999); - $this->assertEqual($rule->description(), - array(get_string('quiznotavailable', 'quiz', userdate(10000)))); - $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $rule = new open_close_date_access_rule($quizobj, 10000); - $this->assertEqual($rule->description(), - array(get_string('quizopenedon', 'quiz', userdate(10000)))); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - } - - public function test_close_date() { - $quiz = new stdClass(); - $quiz->timeopen = 0; - $quiz->timeclose = 20000; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->preview = 0; - - $rule = new open_close_date_access_rule($quizobj, 20000); - $this->assertEqual($rule->description(), - array(get_string('quizcloseson', 'quiz', userdate(20000)))); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 20000 - QUIZ_SHOW_TIME_BEFORE_DEADLINE)); - $this->assertEqual($rule->time_left($attempt, 19900), 100); - $this->assertEqual($rule->time_left($attempt, 20000), 0); - $this->assertEqual($rule->time_left($attempt, 20100), -100); - - $rule = new open_close_date_access_rule($quizobj, 20001); - $this->assertEqual($rule->description(), - array(get_string('quizclosed', 'quiz', userdate(20000)))); - $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertTrue($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 20000 - QUIZ_SHOW_TIME_BEFORE_DEADLINE)); - $this->assertEqual($rule->time_left($attempt, 19900), 100); - $this->assertEqual($rule->time_left($attempt, 20000), 0); - $this->assertEqual($rule->time_left($attempt, 20100), -100); - } - - public function test_both_dates() { - $quiz = new stdClass(); - $quiz->timeopen = 10000; - $quiz->timeclose = 20000; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->preview = 0; - - $rule = new open_close_date_access_rule($quizobj, 9999); - $this->assertEqual($rule->description(), - array(get_string('quiznotavailable', 'quiz', userdate(10000)))); - $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - - $rule = new open_close_date_access_rule($quizobj, 10000); - $this->assertEqual($rule->description(), - array(get_string('quizopenedon', 'quiz', userdate(10000)), - get_string('quizcloseson', 'quiz', userdate(20000)))); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - - $rule = new open_close_date_access_rule($quizobj, 20000); - $this->assertEqual($rule->description(), - array(get_string('quizopenedon', 'quiz', userdate(10000)), - get_string('quizcloseson', 'quiz', userdate(20000)))); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - - $rule = new open_close_date_access_rule($quizobj, 20001); - $this->assertEqual($rule->description(), - array(get_string('quizclosed', 'quiz', userdate(20000)))); - $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertTrue($rule->is_finished(0, $attempt)); - - $this->assertFalse($rule->time_left($attempt, 20000 - QUIZ_SHOW_TIME_BEFORE_DEADLINE)); - $this->assertEqual($rule->time_left($attempt, 19900), 100); - $this->assertEqual($rule->time_left($attempt, 20000), 0); - $this->assertEqual($rule->time_left($attempt, 20100), -100); - } -} - - -/** - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class inter_attempt_delay_access_rule_test extends UnitTestCase { - public function test_just_first_delay() { - $quiz = new stdClass(); - $quiz->attempts = 3; - $quiz->timelimit = 0; - $quiz->delay1 = 1000; - $quiz->delay2 = 0; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->timefinish = 10000; - - $rule = new inter_attempt_delay_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $attempt->timefinish = 9000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $attempt->timefinish = 9001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - } - - public function test_just_second_delay() { - $quiz = new stdClass(); - $quiz->attempts = 5; - $quiz->timelimit = 0; - $quiz->delay1 = 0; - $quiz->delay2 = 1000; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->timefinish = 10000; - - $rule = new inter_attempt_delay_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $attempt->timefinish = 9000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timefinish = 9001; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - } - - public function test_just_both_delays() { - $quiz = new stdClass(); - $quiz->attempts = 5; - $quiz->timelimit = 0; - $quiz->delay1 = 2000; - $quiz->delay2 = 1000; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->timefinish = 10000; - - $rule = new inter_attempt_delay_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(12000))); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $attempt->timefinish = 8000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timefinish = 8001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(4, $attempt)); - $attempt->timefinish = 9000; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timefinish = 9001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - } - - public function test_with_close_date() { - $quiz = new stdClass(); - $quiz->attempts = 5; - $quiz->timelimit = 0; - $quiz->delay1 = 2000; - $quiz->delay2 = 1000; - $quiz->timeclose = 15000; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->timefinish = 13000; - - $rule = new inter_attempt_delay_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $attempt->timefinish = 13000; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(15000))); - $attempt->timefinish = 13001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youcannotwait', 'quiz')); - $attempt->timefinish = 14000; - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(15000))); - $attempt->timefinish = 14001; - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youcannotwait', 'quiz')); - - $rule = new inter_attempt_delay_access_rule($quizobj, 15000); - $attempt->timefinish = 13000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $attempt->timefinish = 13001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youcannotwait', 'quiz')); - $attempt->timefinish = 14000; - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $attempt->timefinish = 14001; - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youcannotwait', 'quiz')); - - $rule = new inter_attempt_delay_access_rule($quizobj, 15001); - $attempt->timefinish = 13000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $attempt->timefinish = 13001; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $attempt->timefinish = 14000; - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $attempt->timefinish = 14001; - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - } - - public function test_time_limit_and_overdue() { - $quiz = new stdClass(); - $quiz->attempts = 5; - $quiz->timelimit = 100; - $quiz->delay1 = 2000; - $quiz->delay2 = 1000; - $quiz->timeclose = 0; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $attempt = new stdClass(); - $attempt->timestart = 9900; - $attempt->timefinish = 10100; - - $rule = new inter_attempt_delay_access_rule($quizobj, 10000); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 0)); - - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(12000))); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $attempt->timestart = 7950; - $attempt->timefinish = 8000; - $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timestart = 7950; - $attempt->timefinish = 8001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(4, $attempt)); - $attempt->timestart = 8950; - $attempt->timefinish = 9000; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timestart = 8950; - $attempt->timefinish = 9001; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $attempt->timestart = 8900; - $attempt->timefinish = 9100; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); - $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); - $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); - $attempt->timestart = 8901; - $attempt->timefinish = 9100; - $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); - $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); - } -} - - -/** - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class password_access_rule_test extends UnitTestCase { - public function test_password_access_rule() { - $quiz = new stdClass(); - $quiz->password = 'frog'; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $rule = new password_access_rule($quizobj, 0); - $attempt = new stdClass(); - - $this->assertFalse($rule->prevent_access()); - $this->assertEqual($rule->description(), get_string('requirepasswordmessage', 'quiz')); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 1)); - } -} - - -/** - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class securewindow_access_rule_test extends UnitTestCase { - // Nothing very testable in this class, just test that it obeys the general access rule contact. - - public function test_securewindow_access_rule() { - $quiz = new stdClass(); - $quiz->popup = 1; - $quiz->questions = ''; - $cm = new stdClass(); - $cm->id = 0; - $quizobj = new quiz($quiz, $cm, null); - $rule = new securewindow_access_rule($quizobj, 0); - $attempt = new stdClass(); - - $this->assertFalse($rule->prevent_access()); - $this->assertFalse($rule->description()); - $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); - $this->assertFalse($rule->is_finished(0, $attempt)); - $this->assertFalse($rule->time_left($attempt, 1)); - } -} - - -/** - * @copyright 2008 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class quiz_access_manager_test extends UnitTestCase { - public function test_cannot_review_message() { - $quiz = new stdClass(); - $quiz->reviewattempt = 0x10010; - $quiz->timeclose = 0; - $quiz->attempts = 0; - $quiz->questions = '1,2,0,3,4,0'; - - $cm = new stdClass(); - $cm->id = 123; - - $quizobj = new quiz($quiz, $cm, new stdClass(), false); - - $am = new quiz_access_manager($quizobj, time(), false); - - $this->assertEqual('', - $am->cannot_review_message(mod_quiz_display_options::DURING)); - $this->assertEqual('', - $am->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER)); - $this->assertEqual(get_string('noreview', 'quiz'), - $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); - $this->assertEqual(get_string('noreview', 'quiz'), - $am->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE)); - - $closetime = time() + 10000; - $quiz->timeclose = $closetime; - $quizobj = new quiz($quiz, $cm, new stdClass(), false); - $am = new quiz_access_manager($quizobj, time(), false); - - $this->assertEqual(get_string('noreviewuntil', 'quiz', userdate($closetime)), - $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); - } -} From b83c32d39fc2d2ac433fdbbe13bb2347c7d8d2b6 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 5 Oct 2011 11:47:37 +0100 Subject: [PATCH 18/67] MDL-29627 quiz access plugins can add fields to mod_form and save the data. --- mod/quiz/accessmanager.php | 25 +++++++++++++++++++++++++ mod/quiz/accessrule/accessrulebase.php | 22 ++++++++++++++++++++++ mod/quiz/lib.php | 3 +++ mod/quiz/mod_form.php | 3 +++ 4 files changed, 53 insertions(+) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index 2bd90e38e27f2..e45557d15f117 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -90,6 +90,31 @@ protected function create_standard_rules($canignoretimelimits) { } } + /** + * Add any form fields that the access rules require to the settings form. + * @param mod_quiz_mod_form $quizform the quiz settings form that is being built. + * @param MoodleQuickForm $mform the wrapped MoodleQuickForm. + */ + public static function add_settings_form_fields( + mod_quiz_mod_form $quizform, MoodleQuickForm $mform) { + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + foreach ($rules as $rule) { + $rule::add_settings_form_fields($quizform, $mform); + } + } + + /** + * Save any submitted settings when the quiz settings form is submitted. + * @param object $quiz the data from the quiz form, including $quiz->id + * which is the is of the quiz being saved. + */ + public static function save_settings($quiz) { + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + foreach ($rules as $rule) { + $rule::save_settings($quiz); + } + } + protected function accumulate_messages(&$messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index bfe8e16d1204c..116bbd7fcddd4 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -106,4 +106,26 @@ public function is_finished($numprevattempts, $lastattempt) { public function time_left($attempt, $timenow) { return false; } + + /** + * Add any fields that this rule requires to the quiz settings form. This + * method is called from {@link mod_quiz_mod_form::definition()}, while the + * security seciton is being built. + * @param mod_quiz_mod_form $quizform the quiz settings form that is being built. + * @param MoodleQuickForm $mform the wrapped MoodleQuickForm. + */ + public static function add_settings_form_fields( + mod_quiz_mod_form $quizform, MoodleQuickForm $mform) { + // By default do nothing. + } + + /** + * Save any submitted settings when the quiz settings form is submitted. This + * is called from {@link quiz_after_add_or_update()} in lib.php. + * @param object $quiz the data from the quiz form, including $quiz->id + * which is the is of the quiz being saved. + */ + public static function save_settings($quiz) { + // By default do nothing. + } } diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 18f0bcd6efaef..bbe3e38264f92 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -1038,6 +1038,9 @@ function quiz_after_add_or_update($quiz) { array('id' => $feedback->id)); } + // Store any settings belonging to the access rules. + quiz_access_manager::save_settings($quiz); + // Update the events relating to this quiz. quiz_update_events($quiz); diff --git a/mod/quiz/mod_form.php b/mod/quiz/mod_form.php index ee27b1684d1cd..38875e7e1a40c 100644 --- a/mod/quiz/mod_form.php +++ b/mod/quiz/mod_form.php @@ -293,6 +293,9 @@ protected function definition() { $mform->setAdvanced('popup', $quizconfig->popup_adv); $mform->setDefault('popup', $quizconfig->popup); + // Any other rule plugins. + quiz_access_manager::add_settings_form_fields($this, $mform); + //------------------------------------------------------------------------------- $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'quiz')); $mform->addHelpButton('overallfeedbackhdr', 'overallfeedback', 'quiz'); From c18ba64c3e8d2aec9bbf78e4be3f2c4f1f04f09d Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 5 Oct 2011 12:55:22 +0100 Subject: [PATCH 19/67] MDL-29627 load the existing settings when the quiz settings are re-edited. --- mod/quiz/accessmanager.php | 53 ++++++++++++++++++++++++++ mod/quiz/accessrule/accessrulebase.php | 35 +++++++++++++++++ mod/quiz/mod_form.php | 8 ++++ 3 files changed, 96 insertions(+) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index e45557d15f117..5217178a6c0f4 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -92,6 +92,10 @@ protected function create_standard_rules($canignoretimelimits) { /** * Add any form fields that the access rules require to the settings form. + * + * Note that the standard plugins do not use this mechanism, becuase all their + * settings are stored in the quiz table. + * * @param mod_quiz_mod_form $quizform the quiz settings form that is being built. * @param MoodleQuickForm $mform the wrapped MoodleQuickForm. */ @@ -105,6 +109,10 @@ public static function add_settings_form_fields( /** * Save any submitted settings when the quiz settings form is submitted. + * + * Note that the standard plugins do not use this mechanism, becuase all their + * settings are stored in the quiz table. + * * @param object $quiz the data from the quiz form, including $quiz->id * which is the is of the quiz being saved. */ @@ -115,6 +123,51 @@ public static function save_settings($quiz) { } } + /** + * Load any settings required by the access rules. We try to do this with + * a single DB query. + * + * Note that the standard plugins do not use this mechanism, becuase all their + * settings are stored in the quiz table. + * + * @param int $quizid the quiz id. + * @return array setting value name => value. The value names should all + * start with the name of the corresponding plugin to avoid collisions. + */ + public static function load_settings($quizid) { + global $DB; + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + + $allfields = ''; + $alljoins = '{quiz} quiz'; + $allparams = array('quizid' => $quizid); + foreach ($rules as $rule) { + list($fields, $joins, $params) = $rule::get_settings_sql($quizid); + if ($fields) { + if ($allfields) { + $allfields .= ', '; + } + $allfields .= $fields; + } + if ($joins) { + $alljoins .= ' ' . $joins; + } + if ($params) { + $allparams += $params; + } + } + $data = (array) $DB->get_record_sql(" + SELECT $allfields + FROM $alljoins + WHERE quiz.id = :quizid", $allparams); + + foreach ($rules as $rule) { + $data += $rule::get_extra_settings($quizid); + } + + return $data; + } + protected function accumulate_messages(&$messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index 116bbd7fcddd4..ca598a639b0a9 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -128,4 +128,39 @@ public static function add_settings_form_fields( public static function save_settings($quiz) { // By default do nothing. } + + /** + * Return the bits of SQL needed to load all the settings from all the access + * plugins in one DB query. The easiest way to understand what you need to do + * here is probalby to read the code of {@link quiz_access_manager::load_settings()}. + * + * If you have some settings that cannot be loaded in this way, then you can + * use the {@link get_extra_settings()} method instead, but that has + * performance implications. + * + * @param int $quizid the id of the quiz we are loading settings for. This + * can also be accessed as quiz.id in the SQL. (quiz is a table alisas for {quiz}.) + * @return array with three elements: + * 1. fields: any fields to add to the select list. These should be alised + * if neccessary so that the field name starts the name of the plugin. + * 2. joins: any joins (should probably be LEFT JOINS) with other tables that + * are needed. + * 3. params: array of placeholder values that are needed by the SQL. You must + * used named placeholders, and the placeholder names should start with the + * plugin name, to avoid collisions. + */ + public static function get_settings_sql($quizid) { + return array('', '', array()); + } + + /** + * You can use this method to load any extra settings your plugin has that + * cannot be loaded efficiently with get_settings_sql(). + * @param int $quizid the quiz id. + * @return array setting value name => value. The value names should all + * start with the name of your plugin to avoid collisions. + */ + public static function get_extra_settings($quizid) { + return array(); + } } diff --git a/mod/quiz/mod_form.php b/mod/quiz/mod_form.php index 38875e7e1a40c..8462bf00ed448 100644 --- a/mod/quiz/mod_form.php +++ b/mod/quiz/mod_form.php @@ -452,6 +452,14 @@ public function data_preprocessing(&$toform) { $toform['quizpassword'] = $toform['password']; unset($toform['password']); } + + // Load any settings belonging to the access rules. + if (!empty($toform['instance'])) { + $accesssettings = quiz_access_manager::load_settings($toform['instance']); + foreach ($accesssettings as $name => $value) { + $toform[$name] = $value; + } + } } public function validation($data, $files) { From dd70d561ef854bdf170e923a469366982edd5254 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 5 Oct 2011 15:54:57 +0100 Subject: [PATCH 20/67] MDL-29627 quiz load access settings when a quiz is being attempted. --- mod/quiz/accessmanager.php | 70 +++++++++++++++++++++++++++++--------- mod/quiz/attemptlib.php | 4 +-- mod/quiz/startattempt.php | 21 +++++------- mod/quiz/view.php | 10 ++---- 4 files changed, 67 insertions(+), 38 deletions(-) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index 5217178a6c0f4..2d2cd3cadcf68 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -124,23 +124,16 @@ public static function save_settings($quiz) { } /** - * Load any settings required by the access rules. We try to do this with - * a single DB query. - * - * Note that the standard plugins do not use this mechanism, becuase all their - * settings are stored in the quiz table. - * + * Build the SQL for loading all the access settings in one go. * @param int $quizid the quiz id. - * @return array setting value name => value. The value names should all - * start with the name of the corresponding plugin to avoid collisions. + * @param string $basefields initial part of the select list. + * @return array with two elements, the sql and the placeholder values. */ - public static function load_settings($quizid) { - global $DB; - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); - - $allfields = ''; + protected static function get_load_sql($quizid, $rules, $basefields) { + $allfields = $basefields; $alljoins = '{quiz} quiz'; $allparams = array('quizid' => $quizid); + foreach ($rules as $rule) { list($fields, $joins, $params) = $rule::get_settings_sql($quizid); if ($fields) { @@ -156,10 +149,27 @@ public static function load_settings($quizid) { $allparams += $params; } } - $data = (array) $DB->get_record_sql(" - SELECT $allfields - FROM $alljoins - WHERE quiz.id = :quizid", $allparams); + + return array("SELECT $allfields FROM $alljoins WHERE quiz.id = :quizid", $allparams); + } + + /** + * Load any settings required by the access rules. We try to do this with + * a single DB query. + * + * Note that the standard plugins do not use this mechanism, becuase all their + * settings are stored in the quiz table. + * + * @param int $quizid the quiz id. + * @return array setting value name => value. The value names should all + * start with the name of the corresponding plugin to avoid collisions. + */ + public static function load_settings($quizid) { + global $DB; + + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + list($sql, $params) = self::get_load_sql($quizid, $rules, ''); + $data = (array) $DB->get_record_sql($sql, $params); foreach ($rules as $rule) { $data += $rule::get_extra_settings($quizid); @@ -168,6 +178,32 @@ public static function load_settings($quizid) { return $data; } + /** + * Load the quiz settings and any settings required by the access rules. + * We try to do this with a single DB query. + * + * Note that the standard plugins do not use this mechanism, becuase all their + * settings are stored in the quiz table. + * + * @param int $quizid the quiz id. + * @return object mdl_quiz row with extra fields. + */ + public static function load_quiz_and_settings($quizid) { + global $DB; + + $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + list($sql, $params) = self::get_load_sql($quizid, $rules, 'quiz.*'); + $quiz = $DB->get_record_sql($sql, $params, MUST_EXIST); + + foreach ($rules as $rule) { + foreach ($rule::get_extra_settings($quizid) as $name => $value) { + $quiz->$name = $value; + } + } + + return $quiz; + } + protected function accumulate_messages(&$messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 98f4dafa3c294..af99b63a67135 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -103,7 +103,7 @@ public function __construct($quiz, $cm, $course, $getcontext = true) { public static function create($quizid, $userid) { global $DB; - $quiz = $DB->get_record('quiz', array('id' => $quizid), '*', MUST_EXIST); + $quiz = quiz_access_manager::load_quiz_and_settings($quizid); $course = $DB->get_record('course', array('id' => $quiz->course), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id, false, MUST_EXIST); @@ -383,7 +383,7 @@ protected static function create_helper($conditions) { global $DB; $attempt = $DB->get_record('quiz_attempts', $conditions, '*', MUST_EXIST); - $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz), '*', MUST_EXIST); + $quiz = quiz_access_manager::load_quiz_and_settings($attempt->quiz); $course = $DB->get_record('course', array('id' => $quiz->course), '*', MUST_EXIST); $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id, false, MUST_EXIST); diff --git a/mod/quiz/startattempt.php b/mod/quiz/startattempt.php index 4f85d373ca311..1323220df4a3d 100644 --- a/mod/quiz/startattempt.php +++ b/mod/quiz/startattempt.php @@ -41,11 +41,8 @@ if (!$course = $DB->get_record('course', array('id' => $cm->course))) { print_error("coursemisconf"); } -if (!$quiz = $DB->get_record('quiz', array('id' => $cm->instance))) { - print_error('invalidcoursemodule'); -} -$quizobj = quiz::create($quiz->id, $USER->id); +$quizobj = quiz::create($cm->instance, $USER->id); // This script should only ever be posted to, so set page URL to the view page. $PAGE->set_url($quizobj->view_url()); @@ -75,11 +72,11 @@ // To force the creation of a new preview, we set a finish time on the // current attempt (if any). It will then automatically be deleted below $DB->set_field('quiz_attempts', 'timefinish', time(), - array('quiz' => $quiz->id, 'userid' => $USER->id)); + array('quiz' => $quizobj->get_quizid(), 'userid' => $USER->id)); } // Look for an existing attempt. -$attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'all'); +$attempts = quiz_get_user_attempts($quizobj->get_quizid(), $USER->id, 'all'); $lastattempt = end($attempts); // If an in-progress attempt exists, check password then redirect to it. @@ -107,16 +104,16 @@ $accessmanager->do_password_check($quizobj->is_preview_user()); // Delete any previous preview attempts belonging to this user. -quiz_delete_previews($quiz, $USER->id); +quiz_delete_previews($quizobj->get_quiz(), $USER->id); $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); -$quba->set_preferred_behaviour($quiz->preferredbehaviour); +$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Create the new attempt and initialize the question sessions -$attempt = quiz_create_attempt($quiz, $attemptnumber, $lastattempt, time(), +$attempt = quiz_create_attempt($quizobj->get_quiz(), $attemptnumber, $lastattempt, time(), $quizobj->is_preview_user()); -if (!($quiz->attemptonlast && $lastattempt)) { +if (!($quizobj->get_quiz()->attemptonlast && $lastattempt)) { // Starting a normal, new, quiz attempt. // Fully load all the questions in this quiz. @@ -128,14 +125,14 @@ $questionsinuse = array_keys($quizobj->get_questions()); foreach ($quizobj->get_questions() as $i => $questiondata) { if ($questiondata->qtype != 'random') { - if (!$quiz->shuffleanswers) { + if (!$quizobj->get_quiz()->shuffleanswers) { $questiondata->options->shuffleanswers = false; } $question = question_bank::make_question($questiondata); } else { $question = question_bank::get_qtype('random')->choose_other_question( - $questiondata, $questionsinuse, $quiz->shuffleanswers); + $questiondata, $questionsinuse, $quizobj->get_quiz()->shuffleanswers); if (is_null($question)) { throw new moodle_exception('notenoughrandomquestions', 'quiz', $quizobj->view_url(), $questiondata); diff --git a/mod/quiz/view.php b/mod/quiz/view.php index 642e8bf5d8404..c38b8d5097c12 100644 --- a/mod/quiz/view.php +++ b/mod/quiz/view.php @@ -40,9 +40,6 @@ if (!$course = $DB->get_record('course', array('id' => $cm->course))) { print_error('coursemisconf'); } - if (!$quiz = $DB->get_record('quiz', array('id' => $cm->instance))) { - print_error('invalidcoursemodule'); - } } else { if (!$quiz = $DB->get_record('quiz', array('id' => $q))) { print_error('invalidquizid', 'quiz'); @@ -67,8 +64,10 @@ // Create an object to manage all the other (non-roles) access rules. $timenow = time(); -$accessmanager = new quiz_access_manager(quiz::create($quiz->id, $USER->id), $timenow, +$quizobj = quiz::create($cm->instance, $USER->id); +$accessmanager = new quiz_access_manager($quizobj, $timenow, has_capability('mod/quiz:ignoretimelimits', $context, null, false)); +$quiz = $quizobj->get_quiz(); // Log this request. add_to_log($course->id, 'quiz', 'view', 'view.php?id=' . $cm->id, $quiz->id, $cm->id); @@ -84,9 +83,6 @@ $USER->editing = $edit; } -// Update the quiz with overrides for the current user -$quiz = quiz_update_effective_access($quiz, $USER->id); - // Get this user's attempts. $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true); $lastfinishedattempt = end($attempts); From d755b0f5773fdf05b67e224a195356635903c1a6 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 5 Oct 2011 20:58:38 +0100 Subject: [PATCH 21/67] MDL-29627 quiz_access_manager move all output code to the renderer. This achieves a massive clean-up. It simplifies comples code in a number of places. It allows some methods and functions to be moved to a more appropriate home (for example cannot_review_message to the quiz class). It moves more logic out of the renderer. --- mod/quiz/accessmanager.php | 219 +++---------- mod/quiz/accessrule/accessrulebase.php | 30 ++ mod/quiz/accessrule/password/rule.php | 14 +- mod/quiz/accessrule/safebrowser/rule.php | 5 + mod/quiz/accessrule/securewindow/rule.php | 43 +-- mod/quiz/attempt.php | 16 +- mod/quiz/attemptlib.php | 64 ++++ mod/quiz/locallib.php | 18 +- mod/quiz/module.js | 17 + mod/quiz/renderer.php | 291 ++++++++++++------ mod/quiz/report/reportlib.php | 21 ++ mod/quiz/review.php | 21 +- mod/quiz/reviewquestion.php | 2 +- ...{testaccessmanager.php => testquizobj.php} | 19 +- mod/quiz/startattempt.php | 1 + mod/quiz/summary.php | 19 +- mod/quiz/view.php | 72 ++--- 17 files changed, 461 insertions(+), 411 deletions(-) rename mod/quiz/simpletest/{testaccessmanager.php => testquizobj.php} (72%) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index 2d2cd3cadcf68..2e0c84eef5cdc 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -285,17 +285,27 @@ public function is_finished($numprevattempts, $lastattempt) { } /** - * Do the printheader call, etc. required for a secure page, including the necessary JS. + * Sets up the attempt (review or summary) page with any properties required + * by the access rules. * - * @param string $title HTML title tag content, passed to printheader. - * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader. + * @param moodle_page $page the page object to initialise. */ - public function setup_secure_page($title, $headtags = null) { - $this->securewindowrule->setup_secure_page($title, $headtags); + public function setup_attempt_page($page) { + foreach ($this->rules as $rule) { + $rule->setup_attempt_page($page); + } } - public function show_attempt_timer_if_needed($attempt, $timenow) { - global $PAGE; + /** + * Will cause the attempt time to start counting down after the page has loaded, + * if that is necessary. + * + * @param object $attempt the data from the relevant quiz_attempts row. + * @param int $timenow the time to consider as 'now'. + * @param mod_quiz_renderer $output the quiz renderer. + */ + public function show_attempt_timer_if_needed($attempt, $timenow, $output) { + $timeleft = false; foreach ($this->rules as $rule) { $ruletimeleft = $rule->time_left($attempt, $timenow); @@ -303,134 +313,57 @@ public function show_attempt_timer_if_needed($attempt, $timenow) { $timeleft = $ruletimeleft; } } + if ($timeleft !== false) { // Make sure the timer starts just above zero. If $timeleft was <= 0, then // this will just have the effect of causing the quiz to be submitted immediately. $timerstartvalue = max($timeleft, 1); - $PAGE->requires->js_init_call('M.mod_quiz.timer.init', - array($timerstartvalue), false, quiz_get_js_module()); + $output->initialise_timer($timerstartvalue); } } /** - * @return bolean if this quiz should only be shown to students in a secure window. + * @return bolean if this quiz should only be shown to students in a popup window. */ - public function securewindow_required($canpreview) { - return !$canpreview && !is_null($this->securewindowrule); - } - - /** - * @return bolean if this quiz should only be shown to students with safe browser. - */ - public function safebrowser_required($canpreview) { - return !$canpreview && !is_null($this->safebrowserrule); + public function attempt_must_be_in_popup() { + foreach ($this->rules as $rule) { + if ($rule->attempt_must_be_in_popup()) { + return true; + } + } + return false; } /** - * Print a button to start a quiz attempt, with an appropriate javascript warning, - * depending on the access restrictions. The link will pop up a 'secure' window, if - * necessary. - * - * @param bool $canpreview whether this user can preview. This affects whether they must - * use a secure window. - * @param string $buttontext the label to put on the button. - * @param bool $unfinished whether the button is to continue an existing attempt, - * or start a new one. This affects whether a javascript alert is shown. + * @return array any options that are required for showing the attempt page + * in a popup window. */ - //TODO: Add this function to renderer - public function print_start_attempt_button($canpreview, $buttontext, $unfinished) { - global $OUTPUT; - - $url = $this->quizobj->start_attempt_url(); - $button = new single_button($url, $buttontext); - $button->class .= ' quizstartbuttondiv'; - - if (!$unfinished) { - $strconfirmstartattempt = $this->confirm_start_attempt_message(); - if ($strconfirmstartattempt) { - $button->add_action(new confirm_action($strconfirmstartattempt, null, - get_string('startattempt', 'quiz'))); - } - } - - $warning = ''; - - if ($this->securewindow_required($canpreview)) { - $button->class .= ' quizsecuremoderequired'; - - $button->add_action(new popup_action('click', $url, 'quizpopup', - securewindow_access_rule::$popupoptions)); - - $warning = html_writer::tag('noscript', - $OUTPUT->heading(get_string('noscript', 'quiz'))); + public function get_popup_options() { + $options = array(); + foreach ($this->rules as $rule) { + $options += $rule->get_popup_options(); } - - return $OUTPUT->render($button) . $warning; + return $options; } /** * Send the user back to the quiz view page. Normally this is just a redirect, but * If we were in a secure window, we close this window, and reload the view window we came from. * - * @param bool $canpreview This affects whether we have to worry about secure window stuff. - */ - public function back_to_view_page($canpreview, $message = '') { - global $CFG, $OUTPUT, $PAGE; - $url = $this->quizobj->view_url(); - if ($this->securewindow_required($canpreview)) { - $PAGE->set_pagelayout('popup'); - echo $OUTPUT->header(); - echo $OUTPUT->box_start(); - if ($message) { - echo '

    ' . $message . '

    ' . get_string('windowclosing', 'quiz') . '

    '; - $delay = 5; - } else { - echo '

    ' . get_string('pleaseclose', 'quiz') . '

    '; - $delay = 0; - } - $PAGE->requires->js_function_call('M.mod_quiz.secure_window.close', - array($url, $delay)); - echo $OUTPUT->box_end(); - echo $OUTPUT->footer(); - die(); - } else { - redirect($url, $message); - } - } - - /** - * Print a control to finish the review. Normally this is just a link, but if we are - * in a secure window, it needs to be a button that does M.mod_quiz.secure_window.close. + * This method does not return; * - * @param bool $canpreview This affects whether we have to worry about secure window stuff. + * @param mod_quiz_renderer $output the quiz renderer. + * @param string $message optional message to output while redirecting. */ - public function print_finish_review_link($canpreview, $return = false) { - global $CFG; - $output = ''; - $url = $this->quizobj->view_url(); - $output .= '
    '; - if ($this->securewindow_required($canpreview)) { - $url = addslashes_js(htmlspecialchars($url)); - $output .= '\n"; - } else { - $output .= '' . get_string('finishreview', 'quiz') . "\n"; - } - $output .= "
    \n"; - if ($return) { - return $output; + public function back_to_view_page($output, $message = '') { + if ($this->attempt_must_be_in_popup()) { + echo $output->close_attempt_popup($message, $this->quizobj->view_url()); + die(); } else { - echo $output; + redirect($this->quizobj->view_url(), $message); } } - /** - * @return bolean if this quiz is password protected. - */ - public function password_required() { - return !is_null($this->passwordrule); - } - /** * Clear the flag in the session that says that the current user is allowed to do this quiz. */ @@ -452,22 +385,6 @@ public function do_password_check($canpreview) { } } - /** - * @return string if the quiz policies merit it, return a warning string to be displayed - * in a javascript alert on the start attempt button. - */ - public function confirm_start_attempt_message() { - $quiz = $this->quizobj->get_quiz(); - if ($quiz->timelimit && $quiz->attempts) { - return get_string('confirmstartattempttimelimit', 'quiz', $quiz->attempts); - } else if ($quiz->timelimit) { - return get_string('confirmstarttimelimit', 'quiz'); - } else if ($quiz->attempts) { - return get_string('confirmstartattemptlimit', 'quiz', $quiz->attempts); - } - return ''; - } - /** * Make some text into a link to review the quiz, if that is appropriate. * @@ -476,12 +393,11 @@ public function confirm_start_attempt_message() { * @return string some HTML, the $linktext either unmodified or wrapped in a * link to the review page. */ - public function make_review_link($attempt, $canpreview, $reviewoptions) { - global $CFG; + public function make_review_link($attempt, $reviewoptions, $output) { // If review of responses is not allowed, or the attempt is still open, don't link. if (!$attempt->timefinish) { - return ''; + return $output->no_review_message(''); } $when = quiz_attempt_state($this->quizobj->get_quiz(), $attempt); @@ -489,52 +405,11 @@ public function make_review_link($attempt, $canpreview, $reviewoptions) { $this->quizobj->get_quiz(), $when); if (!$reviewoptions->attempt) { - $message = $this->cannot_review_message($when, true); - if ($message) { - return '' . $message . ''; - } else { - return ''; - } - } - - $linktext = get_string('review', 'quiz'); - - // It is OK to link, does it need to be in a secure window? - if ($this->securewindow_required($canpreview)) { - return $this->securewindowrule->make_review_link($linktext, $attempt->id); - } else { - return '' . $linktext . ''; - } - } + return $output->no_review_message($this->quizobj->cannot_review_message($when, true)); - /** - * If $reviewoptions->attempt is false, meaning that students can't review this - * attempt at the moment, return an appropriate string explaining why. - * - * @param int $when One of the mod_quiz_display_options::DURING, - * IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants. - * @param bool $short if true, return a shorter string. - * @return string an appropraite message. - */ - public function cannot_review_message($when, $short = false) { - $quiz = $this->quizobj->get_quiz(); - if ($short) { - $langstrsuffix = 'short'; - $dateformat = get_string('strftimedatetimeshort', 'langconfig'); - } else { - $langstrsuffix = ''; - $dateformat = ''; - } - if ($when == mod_quiz_display_options::DURING || - $when == mod_quiz_display_options::IMMEDIATELY_AFTER) { - return ''; - } else if ($when == mod_quiz_display_options::LATER_WHILE_OPEN && $quiz->timeclose && - $quiz->reviewattempt & mod_quiz_display_options::AFTER_CLOSE) { - return get_string('noreviewuntil' . $langstrsuffix, 'quiz', - userdate($quiz->timeclose, $dateformat)); } else { - return get_string('noreview' . $langstrsuffix, 'quiz'); + return $output->review_link($this->quizobj->review_url($attempt->id), + $this->attempt_must_be_in_popup(), $this->get_popup_options()); } } } diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index ca598a639b0a9..06e72002d01cf 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -40,8 +40,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class quiz_access_rule_base { + /** @var stdClass the quiz settings. */ protected $quiz; + /** @var quiz the quiz object. */ protected $quizobj; + /** @var int the time to use as 'now'. */ protected $timenow; /** @@ -107,6 +110,33 @@ public function time_left($attempt, $timenow) { return false; } + /** + * @return boolean whether this rule requires that the attemp (and review) + * pages must be displayed in a pop-up window. + */ + public function attempt_must_be_in_popup() { + return false; + } + + /** + * @return array any options that are required for showing the attempt page + * in a popup window. + */ + public function get_popup_options() { + return array(); + } + + /** + * Sets up the attempt (review or summary) page with any special extra + * properties required by this rule. securewindow rule is an example of where + * this is used. + * + * @param moodle_page $page the page object to initialise. + */ + public function setup_attempt_page($page) { + // Do nothing by default. + } + /** * Add any fields that this rule requires to the quiz settings form. This * method is called from {@link mod_quiz_mod_form::definition()}, while the diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php index f47e4c5f742b7..31c9828a20e34 100644 --- a/mod/quiz/accessrule/password/rule.php +++ b/mod/quiz/accessrule/password/rule.php @@ -69,7 +69,7 @@ public function do_password_check($canpreview, $accessmanager) { // If the user cancelled the password form, send them back to the view page. if (optional_param('cancelpassword', false, PARAM_BOOL)) { - $accessmanager->back_to_view_page($canpreview); + $accessmanager->back_to_view_page($output); } // If they entered the right password, let them in. @@ -95,16 +95,8 @@ public function do_password_check($canpreview, $accessmanager) { $output = ''; // Start the page and print the quiz intro, if any. - if ($accessmanager->securewindow_required($canpreview)) { - $accessmanager->setup_secure_page($this->quizobj->get_course()->shortname . ': ' . - format_string($this->quizobj->get_quiz_name())); - } else if ($accessmanager->safebrowser_required($canpreview)) { - $PAGE->set_title($this->quizobj->get_course()->shortname . ': ' . - format_string($this->quizobj->get_quiz_name())); - $PAGE->set_cacheable(false); - } else { - $PAGE->set_title(format_string($this->quizobj->get_quiz_name())); - } + $PAGE->set_title(format_string($this->quizobj->get_quiz_name())); + $accessmanager->setup_attempt_page($PAGE); echo $OUTPUT->header(); if (trim(strip_tags($this->quiz->intro))) { diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index 9d65f27f954de..4d0aa73a9ba9c 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -48,6 +48,11 @@ public function description() { return get_string("safebrowsernotice", "quiz"); } + public function setup_attempt_page($page) { + $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title); + $page->set_cacheable(false); + } + /** * Checks if browser is safe browser * diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index 258ce3dd684f2..00a15432eb930 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -38,7 +38,7 @@ */ class quizaccess_securewindow extends quiz_access_rule_base { /** @var array options that should be used for opening the secure popup. */ - public static $popupoptions = array( + protected static $popupoptions = array( 'left' => 0, 'top' => 0, 'fullscreen' => true, @@ -52,36 +52,21 @@ class quizaccess_securewindow extends quiz_access_rule_base { 'menubar' => false, ); - /** - * Make a link to the review page for an attempt. - * - * @param string $linktext the desired link text. - * @param int $attemptid the attempt id. - * @return string HTML for the link. - */ - public function make_review_link($linktext, $attemptid) { - global $OUTPUT; - $url = $this->quizobj->review_url($attemptid); - $button = new single_button($url, $linktext); - $button->add_action(new popup_action('click', $url, 'quizpopup', self::$popupoptions)); - return $OUTPUT->render($button); + public function attempt_must_be_in_popup() { + return true; } - /** - * Do the printheader call, etc. required for a secure page, including the necessary JS. - * - * @param string $title HTML title tag content, passed to printheader. - * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader. - * $headtags has been deprectaed since Moodle 2.0 - */ - public function setup_secure_page($title, $headtags=null) { - global $PAGE; - $PAGE->set_popup_notification_allowed(false);//prevent message notifications - $PAGE->set_title($title); - $PAGE->set_cacheable(false); - $PAGE->set_pagelayout('popup'); - $PAGE->add_body_class('quiz-secure-window'); - $PAGE->requires->js_init_call('M.mod_quiz.secure_window.init', + public function get_popup_options() { + return self::$popupoptions; + } + + public function setup_attempt_page($page) { + $page->set_popup_notification_allowed(false); // Prevent message notifications + $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title); + $page->set_cacheable(false); + $page->set_pagelayout('popup'); + $page->add_body_class('quiz-secure-window'); + $page->requires->js_init_call('M.mod_quiz.secure_window.init', null, false, quiz_get_js_module()); } } diff --git a/mod/quiz/attempt.php b/mod/quiz/attempt.php index 5afa75e472d4b..03ba4e3f1ab53 100644 --- a/mod/quiz/attempt.php +++ b/mod/quiz/attempt.php @@ -105,19 +105,9 @@ $title = get_string('attempt', 'quiz', $attemptobj->get_attempt_number()); $headtags = $attemptobj->get_html_head_contributions($page); +$PAGE->set_title(format_string($attemptobj->get_quiz_name())); $PAGE->set_heading($attemptobj->get_course()->fullname); -if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { - $accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' . - format_string($attemptobj->get_quiz_name())); - -} else if ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) { - $PAGE->set_title($attemptobj->get_course()->shortname . ': ' . - format_string($attemptobj->get_quiz_name())); - $PAGE->set_cacheable(false); - -} else { - $PAGE->set_title(format_string($attemptobj->get_quiz_name())); -} +$accessmanager->setup_attempt_page($PAGE); if ($attemptobj->is_last_page($page)) { $nextpage = -1; @@ -125,5 +115,5 @@ $nextpage = $page + 1; } -$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time()); +$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time(), $output); echo $output->attempt_page($attemptobj, $page, $accessmanager, $messages, $slots, $id, $nextpage); diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index af99b63a67135..57c21275ce57e 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -316,6 +316,58 @@ public function review_url($attemptid) { // Bits of content ===================================================================== + /** + * @param bool $unfinished whether there is currently an unfinished attempt active. + * @return string if the quiz policies merit it, return a warning string to + * be displayed in a javascript alert on the start attempt button. + */ + public function confirm_start_attempt_message($unfinished) { + if ($unfinished) { + return ''; + } + + if ($this->quiz->timelimit && $this->quiz->attempts) { + return get_string('confirmstartattempttimelimit', 'quiz', $this->quiz->attempts); + } else if ($this->quiz->timelimit) { + return get_string('confirmstarttimelimit', 'quiz'); + } else if ($this->quiz->attempts) { + return get_string('confirmstartattemptlimit', 'quiz', $this->quiz->attempts); + } + + return ''; + } + + /** + * If $reviewoptions->attempt is false, meaning that students can't review this + * attempt at the moment, return an appropriate string explaining why. + * + * @param int $when One of the mod_quiz_display_options::DURING, + * IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants. + * @param bool $short if true, return a shorter string. + * @return string an appropraite message. + */ + public function cannot_review_message($when, $short = false) { + + if ($short) { + $langstrsuffix = 'short'; + $dateformat = get_string('strftimedatetimeshort', 'langconfig'); + } else { + $langstrsuffix = ''; + $dateformat = ''; + } + + if ($when == mod_quiz_display_options::DURING || + $when == mod_quiz_display_options::IMMEDIATELY_AFTER) { + return ''; + } else if ($when == mod_quiz_display_options::LATER_WHILE_OPEN && $this->quiz->timeclose && + $this->quiz->reviewattempt & mod_quiz_display_options::AFTER_CLOSE) { + return get_string('noreviewuntil' . $langstrsuffix, 'quiz', + userdate($this->quiz->timeclose, $dateformat)); + } else { + return get_string('noreview' . $langstrsuffix, 'quiz'); + } + } + /** * @param string $title the name of this particular quiz page. * @return array the data that needs to be sent to print_header_simple as the $navigation @@ -897,6 +949,18 @@ public function review_url($slot = null, $page = -1, $showall = false, $thispage // Bits of content ===================================================================== + /** + * If $reviewoptions->attempt is false, meaning that students can't review this + * attempt at the moment, return an appropriate string explaining why. + * + * @param bool $short if true, return a shorter string. + * @return string an appropraite message. + */ + public function cannot_review_message($short = false) { + return $this->quizobj->cannot_review_message( + $this->get_attempt_state(), $short); + } + /** * Initialise the JS etc. required all the questions on a page.. * @param mixed $page a page number, or 'all'. diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index e5fa7f6cf308b..b3d96fe261deb 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -383,19 +383,6 @@ function quiz_has_feedback($quiz) { return $cache[$quiz->id]; } -function quiz_no_questions_message($quiz, $cm, $context) { - global $OUTPUT; - - $output = ''; - $output .= $OUTPUT->notification(get_string('noquestions', 'quiz')); - if (has_capability('mod/quiz:manage', $context)) { - $output .= $OUTPUT->single_button(new moodle_url('/mod/quiz/edit.php', - array('cmid' => $cm->id)), get_string('editquiz', 'quiz'), 'get'); - } - - return $output; -} - /** * Update the sumgrades field of the quiz. This needs to be called whenever * the grading structure of the quiz is changed. For example if a question is @@ -1293,9 +1280,10 @@ function quiz_get_js_module() { 'core_question_engine'), 'strings' => array( array('cancel', 'moodle'), - array('timesup', 'quiz'), - array('functiondisabledbysecuremode', 'quiz'), array('flagged', 'question'), + array('functiondisabledbysecuremode', 'quiz'), + array('startattempt', 'quiz'), + array('timesup', 'quiz'), ), ); } diff --git a/mod/quiz/module.js b/mod/quiz/module.js index 4db04d5b7354b..ec37234f43fef 100644 --- a/mod/quiz/module.js +++ b/mod/quiz/module.js @@ -224,6 +224,23 @@ M.mod_quiz.secure_window = { e.halt(); }, + /** + * Event handler for the quiz start attempt button. + */ + start_attempt_action: function(e, args) { + if (args.startattemptwarning == '') { + openpopup(e, args); + } else { + M.util.show_confirm_dialog(e, { + message: args.startattemptwarning, + callback: function() { + openpopup(e, args); + }, + continuelabel: M.util.get_string('startattempt', 'quiz') + }); + } + }, + init_close_button: function(Y, url) { Y.on('click', function(e) { M.mod_quiz.secure_window.close(url, 0) diff --git a/mod/quiz/renderer.php b/mod/quiz/renderer.php index cd8333cb3c3e7..0374d5833ba66 100644 --- a/mod/quiz/renderer.php +++ b/mod/quiz/renderer.php @@ -453,14 +453,50 @@ public function attempt_form($attemptobj, $page, $slots, $id, $nextpage) { return $output; } + /** + * Output the JavaScript required to initialise the countdown timer. + * @param int $timerstartvalue time remaining, in seconds. + */ + public function initialise_timer($timerstartvalue) { + $this->page->requires->js_init_call('M.mod_quiz.timer.init', + array($timerstartvalue), false, quiz_get_js_module()); + } + + /** + * Output a page with an optional message, and JavaScript code to close the + * current window and redirect the parent window to a new URL. + * @param moodle_url $url the URL to redirect the parent window to. + * @param string $message message to display before closing the window. (optional) + * @return string HTML to output. + */ + public function close_attempt_popup($url, $message = '') { + $output = ''; + $output .= $this->header(); + $output .= $this->box_start(); + + if ($message) { + $output .= html_writer('p', $message); + $output .= html_writer('p', get_string('windowclosing', 'quiz')); + $delay = 5; + } else { + $output .= html_writer('p', get_string('pleaseclose', 'quiz')); + $delay = 0; + } + $this->page->requires->js_function_call('M.mod_quiz.secure_window.close', + array($url, $delay)); + + $output .= $this->box_end(); + $output .= $this->footer(); + return $output; + } + /** * Print each message in an array, surrounded by <p>, </p> tags. * * @param array $messages the array of message strings. * @param bool $return if true, return a string, instead of outputting. * - * @return mixed, if $return is true, return the string that would have been output, otherwise - * return null. + * @return string HTML to output. */ public function access_messages($messages) { $output = ''; @@ -588,15 +624,104 @@ public function summary_page_controls($attemptobj) { * @param array $infomessages further information about why the student cannot * attempt this quiz now, if appicable this quiz */ - public function view_page($course, $quiz, $cm, $context, $infomessages, $viewobj, - $buttontext, $preventmessages) { + public function view_page($course, $quiz, $cm, $context, $viewobj) { $output = ''; - $output .= $this->view_information($course, $quiz, $cm, $context, $infomessages); + $output .= $this->view_information($course, $quiz, $cm, $context, $viewobj->infomessages); $output .= $this->view_table($quiz, $context, $viewobj); $output .= $this->view_best_score($viewobj); $output .= $this->view_result_info($quiz, $context, $cm, $viewobj); - $output .= $this->view_attempt_button($course, $quiz, $cm, $context, $viewobj, - $buttontext, $preventmessages); + $output .= $this->box($this->view_page_buttons($viewobj), 'quizattempt'); + return $output; + } + + /** + * Work out, and render, whatever buttons, and surrounding info, should appear + * at the end of the review page. + * @param mod_quiz_view_object $viewobj the information required to display + * the view page. + * @return string HTML to output. + */ + public function view_page_buttons(mod_quiz_view_object $viewobj) { + $output = ''; + + if (!$viewobj->quizhasquestions) { + $output .= $this->no_questions_message($viewobj->canedit, $viewobj->editurl); + } + + $output .= $this->access_messages($viewobj->preventmessages); + + if ($viewobj->buttontext) { + $output .= $this->start_attempt_button($viewobj->buttontext, + $viewobj->startattempturl, $viewobj->startattemptwarning, + $viewobj->popuprequired, $viewobj->popupoptions); + + } else if ($viewobj->buttontext === '') { + // We should show a 'back to the course' button. + $output .= $this->single_button($viewobj->backtocourseurl, + get_string('backtocourse', 'quiz'), 'get', + array('class' => 'continuebutton')); + } + + return $output; + } + + /** + * Generates the view attempt button + * + * @param int $course The course ID + * @param array $quiz Array containging quiz date + * @param int $cm The Course Module ID + * @param int $context The page Context ID + * @param mod_quiz_view_object $viewobj + * @param string $buttontext + */ + public function start_attempt_button($buttontext, moodle_url $url, + $startattemptwarning, $popuprequired, $popupoptions) { + + $button = new single_button($url, $buttontext); + $button->class .= ' quizstartbuttondiv'; + + $warning = ''; + if ($popuprequired) { + $this->page->requires->js_module(quiz_get_js_module()); + $this->page->requires->js('/mod/quiz/module.js'); + $popupaction = new popup_action('click', $url, 'quizpopup', $popupoptions); + + $button->class .= ' quizsecuremoderequired'; + $button->add_action(new component_action('click', + 'M.mod_quiz.secure_window.start_attempt_action', array( + 'url' => $url->out(false), + 'windowname' => 'quizpopup', + 'popupoptions' => $popupaction->get_js_options(), + 'fullscreen' => true, + 'startattemptwarning' => $startattemptwarning, + ))); + + $warning = html_writer::tag('noscript', $this->heading(get_string('noscript', 'quiz'))); + + } else if ($startattemptwarning) { + $button->add_action(new confirm_action($startattemptwarning, null, + get_string('startattempt', 'quiz'))); + } + + return $this->render($button) . $warning; + } + + /** + * Generate a message saying that this quiz has no questions, with a button to + * go to the edit page, if the user has the right capability. + * @param object $quiz the quiz settings. + * @param object $cm the course_module object. + * @param object $context the quiz context. + * @return string HTML to output. + */ + function no_questions_message($canedit, $editurl) { + $output = ''; + $output .= $this->notification(get_string('noquestions', 'quiz')); + if ($canedit) { + $output .= $this->single_button($editurl, get_string('editquiz', 'quiz'), 'get'); + } + return $output; } @@ -797,7 +922,7 @@ public function view_table($quiz, $context, $viewobj) { if ($viewobj->canreviewmine) { $row[] = $viewobj->accessmanager->make_review_link($attempt, - $viewobj->canpreview, $attemptoptions); + $attemptoptions, $this); } if ($viewobj->feedbackcolumn && $attempt->timefinish > 0) { @@ -893,42 +1018,35 @@ public function view_result_info($quiz, $context, $cm, $viewobj) { } /** - * Generates the view attempt button + * Output either a link to the review page for an attempt, or a button to + * open the review in a popup window. * - * @param int $course The course ID - * @param array $quiz Array containging quiz date - * @param int $cm The Course Module ID - * @param int $context The page Context ID - * @param mod_quiz_view_object $viewobj - * @param string $buttontext + * @param moodle_url $url of the target page. + * @param bool $reviewinpopup whether a pop-up is required. + * @param array $popupoptions options to pass to the popup_action constructor. + * @return string HTML to output. */ - public function view_attempt_button($course, $quiz, $cm, $context, $viewobj, - $buttontext, $preventmessages) { - $output = ''; - // Determine if we should be showing a start/continue attempt button, - // or a button to go back to the course page. - $output .= $this->box_start('quizattempt'); + public function review_link($url, $reviewinpopup, $popupoptions) { + if ($reviewinpopup) { + $button = new single_button($url, get_string('review', 'quiz')); + $button->add_action(new popup_action('click', $url, 'quizpopup', $popupoptions)); + return $this->render($button); - // Now actually print the appropriate button. - if (!quiz_clean_layout($quiz->questions, true)) { - $output .= quiz_no_questions_message($quiz, $cm, $context); - } - - if ($preventmessages) { - $output .= $this->access_messages($preventmessages); - } - - if ($buttontext) { - $output .= $viewobj->accessmanager->print_start_attempt_button($viewobj->canpreview, - $buttontext, $viewobj->unfinished); - } else if ($buttontext === '') { - $output .= $this->single_button(new moodle_url('/course/view.php', - array('id' => $course->id)), get_string('backtocourse', 'quiz'), 'get', - array('class' => 'continuebutton')); + } else { + return html_writer::link($url, get_string('review', 'quiz'), + array('title' => get_string('reviewthisattempt', 'quiz'))); } - $output .= $this->box_end(); + } - return $output; + /** + * Displayed where there might normally be a review link, to explain why the + * review is not available at this time. + * @param string $message optional message explaining why the review is not possible. + * @return string HTML to output. + */ + public function no_review_message($message) { + return html_writer::nonempty_tag('span', $message, + array('class' => 'noreviewmessage')); } /** @@ -969,76 +1087,59 @@ class mod_quiz_links_to_other_attempts implements renderable { } class mod_quiz_view_object { - /** - * @var array $attempt contains all the user's attempts at this quiz. - */ + /** @var array $infomessages of messages with information to display about the quiz. */ + public $infomessages; + /** @var array $attempt contains all the user's attempts at this quiz. */ public $attempts; - /** - * @var object $accessmanager contains various access rules. - */ + /** @var quiz_access_manager $accessmanager contains various access rules. */ public $accessmanager; - /** - * @var int $canattempt determins capability for attempting a quiz. - */ - public $canattempt; - /** - * @var int $canpreview determins capability for previewing a quiz. - */ - public $canpreview; - /** - * @var int $canreviewmine determins capability for reviwing own quiz. - */ + /** @var bool $canreviewmine whether the current user has the capability to review their own attempts. */ public $canreviewmine; - /** - * @var int $attemptcolumn contains the number of attempts done. - */ + /** @var bool $canedit whether the current user has the capability to edit the quiz. */ + public $canedit; + /** @var moodle_url $editurl the URL for editing this quiz. */ + public $editurl; + /** @var int $attemptcolumn contains the number of attempts done. */ public $attemptcolumn; - /** - * @var int $gradecolumn contains the grades of any attempts. - */ + /** @var int $gradecolumn contains the grades of any attempts. */ public $gradecolumn; - /** - * @var int $markcolumn contains the marks of any attempt. - */ + /** @var int $markcolumn contains the marks of any attempt. */ public $markcolumn; - /** - * @var int $overallstats contains all marks for any attempt. - */ + /** @var int $overallstats contains all marks for any attempt. */ public $overallstats; - /** - * @var string $feedbackcolumn contains any feedback for and attempt. - */ + /** @var string $feedbackcolumn contains any feedback for and attempt. */ public $feedbackcolumn; - /** - * @var string $timenow contains a timestamp in string format. - */ + /** @var string $timenow contains a timestamp in string format. */ public $timenow; - /** - * @var int $numattempts contains the total number of attempts. - */ + /** @var int $numattempts contains the total number of attempts. */ public $numattempts; - /** - * @var int $mygrade contains the current users final grade for a quiz. - */ + /** @var float $mygrade contains the user's final grade for a quiz. */ public $mygrade; - /** - * @var int $moreattempts total attempts left. - */ + /** @var bool $moreattempts whether this user is allowed more attempts. */ public $moreattempts; - /** - * @var int $mygradeoverridden contains an overriden grade. - */ + /** @var int $mygradeoverridden contains an overriden grade. */ public $mygradeoverridden; - /** - * @var string $gradebookfeedback contains any feedback for a gradebook. - */ + /** @var string $gradebookfeedback contains any feedback for a gradebook. */ public $gradebookfeedback; - /** - * @var int $unfinished contains 1 if an attempt is unfinished. - */ + /** @var bool $unfinished contains 1 if an attempt is unfinished. */ public $unfinished; - /** - * @var int $lastfinishedattempt contains a pointer to the last attempt in the attempts array. - */ + /** @var object $lastfinishedattempt the last attempt from the attempts array. */ public $lastfinishedattempt; + /** @var array $preventmessages of messages telling the user why they can't attempt the quiz now. */ + public $preventmessages; + /** @var string $buttontext caption for the start attempt button. If this is null, show no + * button, or if it is '' show a back to the course button. */ + public $buttontext; + /** @var string $startattemptwarning alert to show the user before starting an attempt. */ + public $startattemptwarning; + /** @var moodle_url $startattempturl URL to start an attempt. */ + public $startattempturl; + /** @var moodle_url $startattempturl URL for any Back to the course button. */ + public $backtocourseurl; + /** @var bool whether the attempt must take place in a popup window. */ + public $popuprequired; + /** @var array options to use for the popup window, if required. */ + public $popupoptions; + /** @var bool $quizhasquestions whether the quiz has any questions. */ + public $quizhasquestions; } diff --git a/mod/quiz/report/reportlib.php b/mod/quiz/report/reportlib.php index 14a4be88bb06a..53fa2b6763dad 100644 --- a/mod/quiz/report/reportlib.php +++ b/mod/quiz/report/reportlib.php @@ -369,3 +369,24 @@ function quiz_report_download_filename($report, $courseshortname, $quizname) { function quiz_report_default_report($context) { return reset(quiz_report_list($context)); } + +/** + * Generate a message saying that this quiz has no questions, with a button to + * go to the edit page, if the user has the right capability. + * @param object $quiz the quiz settings. + * @param object $cm the course_module object. + * @param object $context the quiz context. + * @return string HTML to output. + */ +function quiz_no_questions_message($quiz, $cm, $context) { + global $OUTPUT; + + $output = ''; + $output .= $OUTPUT->notification(get_string('noquestions', 'quiz')); + if (has_capability('mod/quiz:manage', $context)) { + $output .= $OUTPUT->single_button(new moodle_url('/mod/quiz/edit.php', + array('cmid' => $cm->id)), get_string('editquiz', 'quiz'), 'get'); + } + + return $output; +} diff --git a/mod/quiz/review.php b/mod/quiz/review.php index 46f0470b2d88c..584e12c8a49f3 100644 --- a/mod/quiz/review.php +++ b/mod/quiz/review.php @@ -58,9 +58,10 @@ if ($attemptobj->is_own_attempt()) { if (!$attemptobj->is_finished()) { redirect($attemptobj->attempt_url(null, $page)); + } else if (!$options->attempt) { - $accessmanager->back_to_view_page($attemptobj->is_preview_user(), - $accessmanager->cannot_review_message($attemptobj->get_attempt_state())); + $accessmanager->back_to_view_page($PAGE->get_renderer('mod_quiz'), + $attemptobj->cannot_review_message()); } } else if (!$attemptobj->is_review_allowed()) { @@ -100,19 +101,9 @@ // Set up the page header $headtags = $attemptobj->get_html_head_contributions($page, $showall); -if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { - $accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '. - format_string($attemptobj->get_quiz_name()), $headtags); -} else if ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) { - $PAGE->set_title($attemptobj->get_course()->shortname . ': '. - format_string($attemptobj->get_quiz_name())); - $PAGE->set_heading($attemptobj->get_course()->fullname); - $PAGE->set_cacheable(false); -} else { - $PAGE->navbar->add($strreviewtitle); - $PAGE->set_title(format_string($attemptobj->get_quiz_name())); - $PAGE->set_heading($attemptobj->get_course()->fullname); -} +$PAGE->set_title(format_string($attemptobj->get_quiz_name())); +$PAGE->set_heading($attemptobj->get_course()->fullname); +$accessmanager->setup_attempt_page($PAGE); // Summary table start ============================================================================ diff --git a/mod/quiz/reviewquestion.php b/mod/quiz/reviewquestion.php index f0ef55e9e93dd..0e3e68df2f3be 100644 --- a/mod/quiz/reviewquestion.php +++ b/mod/quiz/reviewquestion.php @@ -59,7 +59,7 @@ die(); } else if (!$options->attempt) { echo $output->review_question_not_allowed( - $accessmanager->cannot_review_message($attemptobj->get_review_options())); + $attemptobj->cannot_review_message()); die(); } diff --git a/mod/quiz/simpletest/testaccessmanager.php b/mod/quiz/simpletest/testquizobj.php similarity index 72% rename from mod/quiz/simpletest/testaccessmanager.php rename to mod/quiz/simpletest/testquizobj.php index bc9887d2ee821..625e8d5187276 100644 --- a/mod/quiz/simpletest/testaccessmanager.php +++ b/mod/quiz/simpletest/testquizobj.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * Unit tests for the access manager class. + * Unit tests for the quiz class. * * @package mod * @subpackage quiz @@ -30,12 +30,12 @@ /** - * Unit tests for the access manager class + * Unit tests for the quiz class * * @copyright 2008 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class quiz_access_manager_test extends UnitTestCase { +class quiz_class_test extends UnitTestCase { public function test_cannot_review_message() { $quiz = new stdClass(); $quiz->reviewattempt = 0x10010; @@ -48,23 +48,20 @@ public function test_cannot_review_message() { $quizobj = new quiz($quiz, $cm, new stdClass(), false); - $am = new quiz_access_manager($quizobj, time(), false); - $this->assertEqual('', - $am->cannot_review_message(mod_quiz_display_options::DURING)); + $quizobj->cannot_review_message(mod_quiz_display_options::DURING)); $this->assertEqual('', - $am->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER)); + $quizobj->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER)); $this->assertEqual(get_string('noreview', 'quiz'), - $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); + $quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); $this->assertEqual(get_string('noreview', 'quiz'), - $am->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE)); + $quizobj->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE)); $closetime = time() + 10000; $quiz->timeclose = $closetime; $quizobj = new quiz($quiz, $cm, new stdClass(), false); - $am = new quiz_access_manager($quizobj, time(), false); $this->assertEqual(get_string('noreviewuntil', 'quiz', userdate($closetime)), - $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); + $quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN)); } } diff --git a/mod/quiz/startattempt.php b/mod/quiz/startattempt.php index 1323220df4a3d..4cf5a926ac26c 100644 --- a/mod/quiz/startattempt.php +++ b/mod/quiz/startattempt.php @@ -101,6 +101,7 @@ print_error('attempterror', 'quiz', $quizobj->view_url(), $output->print_messages($messages)); } + $accessmanager->do_password_check($quizobj->is_preview_user()); // Delete any previous preview attempts belonging to this user. diff --git a/mod/quiz/summary.php b/mod/quiz/summary.php index 1cada4656bac0..7b0be2b466e7b 100644 --- a/mod/quiz/summary.php +++ b/mod/quiz/summary.php @@ -77,21 +77,12 @@ $PAGE->blocks->show_only_fake_blocks(); } -if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) { - $accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' . - format_string($attemptobj->get_quiz_name()), ''); -} else if ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) { - $PAGE->set_title($attemptobj->get_course()->shortname . ': ' . - format_string($attemptobj->get_quiz_name())); - $PAGE->set_heading($attemptobj->get_course()->fullname); - $PAGE->set_cacheable(false); -} else { - $PAGE->navbar->add(get_string('summaryofattempt', 'quiz')); - $PAGE->set_title(format_string($attemptobj->get_quiz_name())); - $PAGE->set_heading($attemptobj->get_course()->fullname); -} +$PAGE->navbar->add(get_string('summaryofattempt', 'quiz')); +$PAGE->set_title(format_string($attemptobj->get_quiz_name())); +$PAGE->set_heading($attemptobj->get_course()->fullname); +$accessmanager->setup_attempt_page($PAGE); // Print heading. -$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time()); +$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time(), $output); echo $output->summary_page($attemptobj, $displayoptions); diff --git a/mod/quiz/view.php b/mod/quiz/view.php index c38b8d5097c12..68c34ac7fd3e6 100644 --- a/mod/quiz/view.php +++ b/mod/quiz/view.php @@ -125,8 +125,6 @@ $viewobj = new mod_quiz_view_object(); $viewobj->attempts = $attempts; $viewobj->accessmanager = $accessmanager; -$viewobj->canattempt = $canattempt; -$viewobj->canpreview = $canpreview; $viewobj->canreviewmine = $canreviewmine; // Print table with existing attempts @@ -146,63 +144,68 @@ $viewobj->attemptcolumn = 1; } -$moreattempts = $unfinished || !$accessmanager->is_finished($numattempts, $lastfinishedattempt); - $viewobj->timenow = $timenow; $viewobj->numattempts = $numattempts; $viewobj->mygrade = $mygrade; -$viewobj->moreattempts = $moreattempts; +$viewobj->moreattempts = $unfinished || + !$accessmanager->is_finished($numattempts, $lastfinishedattempt); $viewobj->mygradeoverridden = $mygradeoverridden; $viewobj->gradebookfeedback = $gradebookfeedback; -$viewobj->unfinished = $unfinished; $viewobj->lastfinishedattempt = $lastfinishedattempt; +$viewobj->canedit = has_capability('mod/quiz:manage', $context); +$viewobj->editurl = new moodle_url('/mod/quiz/edit.php', array('cmid' => $cm->id)); +$viewobj->backtocourseurl = new moodle_url('/course/view.php', array('id' => $course->id)); +$viewobj->startattempturl = $quizobj->start_attempt_url(); +$viewobj->startattemptwarning = $quizobj->confirm_start_attempt_message($unfinished); +$viewobj->popuprequired = $accessmanager->attempt_must_be_in_popup(); +$viewobj->popupoptions = $accessmanager->get_popup_options(); // Display information about this quiz. -$infomessages = $viewobj->accessmanager->describe_rules(); +$viewobj->infomessages = $viewobj->accessmanager->describe_rules(); if ($quiz->attempts != 1) { - $infomessages[] = get_string('gradingmethod', 'quiz', + $viewobj->infomessages[] = get_string('gradingmethod', 'quiz', quiz_get_grading_option_name($quiz->grademethod)); } -// This will be set something if as start/continue attempt button should appear. -$buttontext = ''; -$preventmessages = array(); -if (!quiz_clean_layout($quiz->questions, true)) { - $buttontext = ''; +// Determine wheter a start attempt button should be displayed. +$viewobj->quizhasquestions = (bool) quiz_clean_layout($quiz->questions, true); +$viewobj->preventmessages = array(); +if (!$viewobj->quizhasquestions) { + $viewobj->buttontext = ''; } else { - if ($viewobj->unfinished) { - if ($viewobj->canattempt) { - $buttontext = get_string('continueattemptquiz', 'quiz'); - } else if ($viewobj->canpreview) { - $buttontext = get_string('continuepreview', 'quiz'); + if ($unfinished) { + if ($canattempt) { + $viewobj->buttontext = get_string('continueattemptquiz', 'quiz'); + } else if ($canpreview) { + $viewobj->buttontext = get_string('continuepreview', 'quiz'); } } else { - if ($viewobj->canattempt) { - $preventmessages = $viewobj->accessmanager->prevent_new_attempt($viewobj->numattempts, - $viewobj->lastfinishedattempt); - if ($preventmessages) { - $buttontext = ''; + if ($canattempt) { + $viewobj->preventmessages = $viewobj->accessmanager->prevent_new_attempt( + $viewobj->numattempts, $viewobj->lastfinishedattempt); + if ($viewobj->preventmessages) { + $viewobj->buttontext = ''; } else if ($viewobj->numattempts == 0) { - $buttontext = get_string('attemptquiznow', 'quiz'); + $viewobj->buttontext = get_string('attemptquiznow', 'quiz'); } else { - $buttontext = get_string('reattemptquiz', 'quiz'); + $viewobj->buttontext = get_string('reattemptquiz', 'quiz'); } - } else if ($viewobj->canpreview) { - $buttontext = get_string('previewquiznow', 'quiz'); + } else if ($canpreview) { + $viewobj->buttontext = get_string('previewquiznow', 'quiz'); } } // If, so far, we think a button should be printed, so check if they will be // allowed to access it. - if ($buttontext) { + if ($viewobj->buttontext) { if (!$viewobj->moreattempts) { - $buttontext = ''; - } else if ($viewobj->canattempt - && $preventmessages = $viewobj->accessmanager->prevent_access()) { - $buttontext = ''; + $viewobj->buttontext = ''; + } else if ($canattempt + && $viewobj->preventmessages = $viewobj->accessmanager->prevent_access()) { + $viewobj->buttontext = ''; } } } @@ -212,13 +215,12 @@ // Guests can't do a quiz, so offer them a choice of logging in or going back. if (isguestuser()) { echo $output->view_page_guest($course, $quiz, $cm, $context, $infomessages, $viewobj); -} else if (!isguestuser() && !($viewobj->canattempt || $viewobj->canpreview +} else if (!isguestuser() && !($canattempt || $canpreview || $viewobj->canreviewmine)) { // If they are not enrolled in this course in a good enough role, tell them to enrol. echo $output->view_page_notenrolled($course, $quiz, $cm, $context, $infomessages, $viewobj); } else { - echo $output->view_page($course, $quiz, $cm, $context, $infomessages, $viewobj, - $buttontext, $preventmessages); + echo $output->view_page($course, $quiz, $cm, $context, $viewobj); } echo $OUTPUT->footer(); From 1d9e1a3c069da4788c6529a07474153fec3a028e Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 6 Oct 2011 18:58:33 +0100 Subject: [PATCH 22/67] MDL-29627 refactor the way the rules are constructed from the quiz. --- mod/quiz/accessmanager.php | 98 ++++++++++++------- mod/quiz/accessrule/accessrulebase.php | 41 +++++++- .../accessrule/delaybetweenattempts/rule.php | 9 ++ mod/quiz/accessrule/ipaddress/rule.php | 11 ++- mod/quiz/accessrule/numattempts/rule.php | 12 +++ mod/quiz/accessrule/openclosedate/rule.php | 6 ++ mod/quiz/accessrule/password/rule.php | 9 ++ mod/quiz/accessrule/safebrowser/rule.php | 28 +++++- mod/quiz/accessrule/securewindow/rule.php | 24 ++++- mod/quiz/accessrule/timelimit/rule.php | 10 ++ mod/quiz/mod_form.php | 9 +- 11 files changed, 210 insertions(+), 47 deletions(-) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index 2e0c84eef5cdc..edd724cd124a4 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -53,41 +53,44 @@ class quiz_access_manager { public function __construct($quizobj, $timenow, $canignoretimelimits) { $this->quizobj = $quizobj; $this->timenow = $timenow; - $this->create_standard_rules($canignoretimelimits); + $this->rules = $this->make_rules($quizobj, $timenow, $canignoretimelimits); } - protected function create_standard_rules($canignoretimelimits) { - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + /** + * Make all the rules relevant to a particular quiz. + * @param quiz $quizobj information about the quiz in question. + * @param int $timenow the time that should be considered as 'now'. + * @param bool $canignoretimelimits whether the current user is exempt from + * time limits by the mod/quiz:ignoretimelimits capability. + * @return array of {@link quiz_access_rule_base}s. + */ + protected function make_rules($quizobj, $timenow, $canignoretimelimits) { - $quiz = $this->quizobj->get_quiz(); - if ($quiz->attempts > 0) { - $this->rules[] = new quizaccess_numattempts($this->quizobj, $this->timenow); - } - $this->rules[] = new quizaccess_openclosedate($this->quizobj, $this->timenow); - if (!empty($quiz->timelimit) && !$canignoretimelimits) { - $this->rules[] = new quizaccess_timelimit($this->quizobj, $this->timenow); - } - if (!empty($quiz->delay1) || !empty($quiz->delay2)) { - $this->rules[] = new quizaccess_delaybetweenattempts($this->quizobj, $this->timenow); - } - if (!empty($quiz->subnet)) { - $this->rules[] = new quizaccess_ipaddress($this->quizobj, $this->timenow); + $rules = array(); + foreach (self::get_rule_classes() as $ruleclass) { + $rule = $ruleclass::make($quizobj, $timenow, $canignoretimelimits); + if ($rule) { + $rules[$ruleclass] = $rule; + } } - if (!empty($quiz->password)) { - $this->passwordrule = new quizaccess_password($this->quizobj, $this->timenow); - $this->rules[] = $this->passwordrule; + + $superceededrules = array(); + foreach ($rules as $rule) { + $superceededrules += $rule->get_superceded_rules(); } - if (!empty($quiz->popup)) { - if ($quiz->popup == 1) { - $this->securewindowrule = new quizaccess_securewindow( - $this->quizobj, $this->timenow); - $this->rules[] = $this->securewindowrule; - } else if ($quiz->popup == 2) { - $this->safebrowserrule = new quizaccess_safebrowser( - $this->quizobj, $this->timenow); - $this->rules[] = $this->safebrowserrule; - } + + foreach ($superceededrules as $superceededrule) { + unset($rules['quizaccess_' . $superceededrule]); } + + return $rules; + } + + /** + * @return array of all the installed rule class names. + */ + protected static function get_rule_classes() { + return get_plugin_list_with_class('quizaccess', '', 'rule.php'); } /** @@ -101,12 +104,25 @@ protected function create_standard_rules($canignoretimelimits) { */ public static function add_settings_form_fields( mod_quiz_mod_form $quizform, MoodleQuickForm $mform) { - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); - foreach ($rules as $rule) { + + foreach (self::get_rule_classes() as $rule) { $rule::add_settings_form_fields($quizform, $mform); } } + /** + * The the options for the Browser security settings menu. + * + * @return array key => lang string. + */ + public static function get_browser_security_choices() { + $options = array(0 => get_string('none', 'quiz')); + foreach (self::get_rule_classes() as $rule) { + $options += $rule::get_browser_security_choices(); + } + return $options; + } + /** * Save any submitted settings when the quiz settings form is submitted. * @@ -117,8 +133,8 @@ public static function add_settings_form_fields( * which is the is of the quiz being saved. */ public static function save_settings($quiz) { - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); - foreach ($rules as $rule) { + + foreach (self::get_rule_classes() as $rule) { $rule::save_settings($quiz); } } @@ -167,7 +183,7 @@ protected static function get_load_sql($quizid, $rules, $basefields) { public static function load_settings($quizid) { global $DB; - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + $rules = self::get_rule_classes(); list($sql, $params) = self::get_load_sql($quizid, $rules, ''); $data = (array) $DB->get_record_sql($sql, $params); @@ -191,7 +207,7 @@ public static function load_settings($quizid) { public static function load_quiz_and_settings($quizid) { global $DB; - $rules = get_plugin_list_with_class('quizaccess', '', 'rule.php'); + $rules = self::get_rule_classes(); list($sql, $params) = self::get_load_sql($quizid, $rules, 'quiz.*'); $quiz = $DB->get_record_sql($sql, $params, MUST_EXIST); @@ -204,6 +220,18 @@ public static function load_quiz_and_settings($quizid) { return $quiz; } + /** + * @return array the class names of all the active rules. Mainly useful for + * debugging. + */ + public function get_active_rule_names() { + $classnames = array(); + foreach ($this->rules as $rule) { + $classnames[] = get_class($rule); + } + return $classnames; + } + protected function accumulate_messages(&$messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index 06e72002d01cf..2915ebed53671 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -49,13 +49,28 @@ abstract class quiz_access_rule_base { /** * Create an instance of this rule for a particular quiz. - * @param object $quiz the quiz we will be controlling access to. + * @param quiz $quizobj information about the quiz in question. + * @param int $timenow the time that should be considered as 'now'. */ public function __construct($quizobj, $timenow) { $this->quizobj = $quizobj; $this->quiz = $quizobj->get_quiz(); $this->timenow = $timenow; } + + /** + * Return an appropriately configured instance of this rule, if it is applicable + * to the given quiz, otherwise return null. + * @param quiz $quizobj information about the quiz in question. + * @param int $timenow the time that should be considered as 'now'. + * @param bool $canignoretimelimits whether the current user is exempt from + * time limits by the mod/quiz:ignoretimelimits capability. + * @return quiz_access_rule_base|null the rule, if applicable, else null. + */ + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + return null; + } + /** * Whether or not a user should be allowed to start a new attempt at this quiz now. * @param int $numattempts the number of previous attempts this user has made. @@ -66,6 +81,7 @@ public function __construct($quizobj, $timenow) { public function prevent_new_attempt($numprevattempts, $lastattempt) { return false; } + /** * Whether or not a user should be allowed to start a new attempt at this quiz now. * @return string false if access should be allowed, a message explaining the @@ -74,6 +90,7 @@ public function prevent_new_attempt($numprevattempts, $lastattempt) { public function prevent_access() { return false; } + /** * Information, such as might be shown on the quiz view page, relating to this restriction. * There is no obligation to return anything. If it is not appropriate to tell students @@ -84,6 +101,7 @@ public function prevent_access() { public function description() { return ''; } + /** * If this rule can determine that this user will never be allowed another attempt at * this quiz, then return true. This is used so we can know whether to display a @@ -137,6 +155,19 @@ public function setup_attempt_page($page) { // Do nothing by default. } + /** + * It is possible for one rule to override other rules. + * + * The aim is that third-party rules should be able to replace sandard rules + * if they want. See, for example MDL-13592. + * + * @return array plugin names of other rules that this one replaces. + * For example array('ipaddress', 'password'). + */ + public function get_superceded_rules() { + return array(); + } + /** * Add any fields that this rule requires to the quiz settings form. This * method is called from {@link mod_quiz_mod_form::definition()}, while the @@ -149,6 +180,14 @@ public static function add_settings_form_fields( // By default do nothing. } + /** + * @return array key => lang string any choices to add to the quiz Browser + * security settings menu. + */ + public static function get_browser_security_choices() { + return array(); + } + /** * Save any submitted settings when the quiz settings form is submitted. This * is called from {@link quiz_after_add_or_update()} in lib.php. diff --git a/mod/quiz/accessrule/delaybetweenattempts/rule.php b/mod/quiz/accessrule/delaybetweenattempts/rule.php index 95831e931fbe9..3c459aed527ea 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/rule.php +++ b/mod/quiz/accessrule/delaybetweenattempts/rule.php @@ -36,6 +36,15 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_delaybetweenattempts extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + if (empty($quizobj->get_quiz()->delay1) && empty($quizobj->get_quiz()->delay2)) { + return null; + } + + $this->rules[] = new self($quizobj, $timenow); + } + public function prevent_new_attempt($numprevattempts, $lastattempt) { if ($this->quiz->attempts > 0 && $numprevattempts >= $this->quiz->attempts) { // No more attempts allowed anyway. diff --git a/mod/quiz/accessrule/ipaddress/rule.php b/mod/quiz/accessrule/ipaddress/rule.php index 02d29be583d63..0b88416a517b2 100644 --- a/mod/quiz/accessrule/ipaddress/rule.php +++ b/mod/quiz/accessrule/ipaddress/rule.php @@ -30,12 +30,21 @@ /** - * A rule implementing the ipaddress check against the ->submet setting. + * A rule implementing the ipaddress check against the ->subnet setting. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_ipaddress extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + if (empty($quizobj->get_quiz()->subnet)) { + return null; + } + + return new self($quizobj, $timenow); + } + public function prevent_access() { if (address_in_subnet(getremoteaddr(), $this->quiz->subnet)) { return false; diff --git a/mod/quiz/accessrule/numattempts/rule.php b/mod/quiz/accessrule/numattempts/rule.php index b9ef538a6bbc9..c167e55d684b5 100644 --- a/mod/quiz/accessrule/numattempts/rule.php +++ b/mod/quiz/accessrule/numattempts/rule.php @@ -36,15 +36,27 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_numattempts extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + + if ($quizobj->get_num_attempts_allowed() == 0) { + return null; + } + + return new self($quizobj, $timenow); + } + public function description() { return get_string('attemptsallowedn', 'quiz', $this->quiz->attempts); } + public function prevent_new_attempt($numprevattempts, $lastattempt) { if ($numprevattempts >= $this->quiz->attempts) { return get_string('nomoreattempts', 'quiz'); } return false; } + public function is_finished($numprevattempts, $lastattempt) { return $numprevattempts >= $this->quiz->attempts; } diff --git a/mod/quiz/accessrule/openclosedate/rule.php b/mod/quiz/accessrule/openclosedate/rule.php index a62c4a8829d5a..36b98ede6ce65 100644 --- a/mod/quiz/accessrule/openclosedate/rule.php +++ b/mod/quiz/accessrule/openclosedate/rule.php @@ -36,6 +36,12 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_openclosedate extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + // This rule is always used, even if the quiz has no open or close date. + return new self($quizobj, $timenow); + } + public function description() { $result = array(); if ($this->timenow < $this->quiz->timeopen) { diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php index 31c9828a20e34..ffc6968cf46f1 100644 --- a/mod/quiz/accessrule/password/rule.php +++ b/mod/quiz/accessrule/password/rule.php @@ -37,6 +37,15 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_password extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + if (empty($quizobj->get_quiz()->password)) { + return null; + } + + return new self($quizobj, $timenow); + } + public function description() { return get_string('requirepasswordmessage', 'quiz'); } diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index 4d0aa73a9ba9c..2b3a3373ba41c 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -36,6 +36,16 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_safebrowser extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + + if ($quizobj->get_quiz()->popup != 2) { + return null; + } + + return new self($quizobj, $timenow); + } + public function prevent_access() { if (!$this->check_safe_browser()) { return get_string('safebrowsererror', 'quiz'); @@ -45,7 +55,7 @@ public function prevent_access() { } public function description() { - return get_string("safebrowsernotice", "quiz"); + return get_string('safebrowsernotice', 'quiz'); } public function setup_attempt_page($page) { @@ -59,6 +69,20 @@ public function setup_attempt_page($page) { * @return true, if browser is safe browser else false */ function check_safe_browser() { - return strpos($_SERVER['HTTP_USER_AGENT'], "SEB") !== false; + return strpos($_SERVER['HTTP_USER_AGENT'], 'SEB') !== false; + } + + /** + * @return array key => lang string any choices to add to the quiz Browser + * security settings menu. + */ + public static function get_browser_security_choices() { + global $CFG; + + if (empty($CFG->enablesafebrowserintegration)) { + return array(); + } + + return array(2 => get_string('requiresafeexambrowser', 'quiz')); } } diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index 00a15432eb930..6b766425ef99c 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -52,8 +52,17 @@ class quizaccess_securewindow extends quiz_access_rule_base { 'menubar' => false, ); + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + + if ($quizobj->get_quiz()->popup != 1) { + return null; + } + + return new self($quizobj, $timenow); + } + public function attempt_must_be_in_popup() { - return true; + return !$this->quizobj->is_preview_user(); } public function get_popup_options() { @@ -65,8 +74,21 @@ public function setup_attempt_page($page) { $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title); $page->set_cacheable(false); $page->set_pagelayout('popup'); + + if ($this->quizobj->is_preview_user()) { + return; + } + $page->add_body_class('quiz-secure-window'); $page->requires->js_init_call('M.mod_quiz.secure_window.init', null, false, quiz_get_js_module()); } + + /** + * @return array key => lang string any choices to add to the quiz Browser + * security settings menu. + */ + public static function get_browser_security_choices() { + return array(1 => get_string('popupwithjavascriptsupport', 'quiz')); + } } diff --git a/mod/quiz/accessrule/timelimit/rule.php b/mod/quiz/accessrule/timelimit/rule.php index 0515dd78ba62a..948b01e6d268e 100644 --- a/mod/quiz/accessrule/timelimit/rule.php +++ b/mod/quiz/accessrule/timelimit/rule.php @@ -37,6 +37,16 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quizaccess_timelimit extends quiz_access_rule_base { + + public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { + + if (empty($quizobj->get_quiz()->timelimit) || $canignoretimelimits) { + return null; + } + + return new self($quizobj, $timenow); + } + public function description() { return get_string('quiztimelimit', 'quiz', format_time($this->quiz->timelimit)); } diff --git a/mod/quiz/mod_form.php b/mod/quiz/mod_form.php index 8462bf00ed448..9130cad295c27 100644 --- a/mod/quiz/mod_form.php +++ b/mod/quiz/mod_form.php @@ -282,13 +282,8 @@ protected function definition() { $mform->disabledIf('delay2', 'attempts', 'eq', 2); // 'Secure' window. - $options = array( - 0 => get_string('none', 'quiz'), - 1 => get_string('popupwithjavascriptsupport', 'quiz')); - if (!empty($CFG->enablesafebrowserintegration)) { - $options[2] = get_string('requiresafeexambrowser', 'quiz'); - } - $mform->addElement('select', 'popup', get_string('browsersecurity', 'quiz'), $options); + $mform->addElement('select', 'popup', get_string('browsersecurity', 'quiz'), + quiz_access_manager::get_browser_security_choices()); $mform->addHelpButton('popup', 'browsersecurity', 'quiz'); $mform->setAdvanced('popup', $quizconfig->popup_adv); $mform->setDefault('popup', $quizconfig->popup); From 0eafc988527c27fa5c9af83e4e9c36cb8f95b3f2 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 6 Oct 2011 19:12:05 +0100 Subject: [PATCH 23/67] MDL-29627 fix code-checker issues throughout the quiz code. --- mod/quiz/accessrule/accessrulebase.php | 12 +++++------ .../accessrule/delaybetweenattempts/rule.php | 12 +++++------ mod/quiz/accessrule/numattempts/rule.php | 10 ++++----- mod/quiz/accessrule/safebrowser/rule.php | 12 +++++------ mod/quiz/accessrule/securewindow/rule.php | 12 +++++------ mod/quiz/accessrule/timelimit/rule.php | 12 +++++------ mod/quiz/attemptlib.php | 2 +- mod/quiz/backup/moodle1/lib.php | 21 ++++++++++++------- mod/quiz/lib.php | 5 +++-- mod/quiz/renderer.php | 8 ++++--- mod/quiz/report/overview/db/install.xml | 2 +- mod/quiz/report/overview/report.php | 6 ++++-- mod/quiz/report/responses/report.php | 9 +++++--- mod/quiz/report/statistics/db/upgrade.php | 3 ++- mod/quiz/report/statistics/report.php | 3 ++- 15 files changed, 72 insertions(+), 57 deletions(-) diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index 2915ebed53671..afd29a57d6abc 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -145,12 +145,12 @@ public function get_popup_options() { } /** - * Sets up the attempt (review or summary) page with any special extra - * properties required by this rule. securewindow rule is an example of where - * this is used. - * - * @param moodle_page $page the page object to initialise. - */ + * Sets up the attempt (review or summary) page with any special extra + * properties required by this rule. securewindow rule is an example of where + * this is used. + * + * @param moodle_page $page the page object to initialise. + */ public function setup_attempt_page($page) { // Do nothing by default. } diff --git a/mod/quiz/accessrule/delaybetweenattempts/rule.php b/mod/quiz/accessrule/delaybetweenattempts/rule.php index 3c459aed527ea..addd269cf4f24 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/rule.php +++ b/mod/quiz/accessrule/delaybetweenattempts/rule.php @@ -30,11 +30,11 @@ /** -* A rule imposing the delay between attempts settings. -* -* @copyright 2009 Tim Hunt -* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later -*/ + * A rule imposing the delay between attempts settings. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class quizaccess_delaybetweenattempts extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { @@ -42,7 +42,7 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { return null; } - $this->rules[] = new self($quizobj, $timenow); + return new self($quizobj, $timenow); } public function prevent_new_attempt($numprevattempts, $lastattempt) { diff --git a/mod/quiz/accessrule/numattempts/rule.php b/mod/quiz/accessrule/numattempts/rule.php index c167e55d684b5..fad046a2e1070 100644 --- a/mod/quiz/accessrule/numattempts/rule.php +++ b/mod/quiz/accessrule/numattempts/rule.php @@ -30,11 +30,11 @@ /** -* A rule controlling the number of attempts allowed. -* -* @copyright 2009 Tim Hunt -* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later -*/ + * A rule controlling the number of attempts allowed. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class quizaccess_numattempts extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index 2b3a3373ba41c..4d198bf45196e 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -30,11 +30,11 @@ /** -* A rule representing the safe browser check. -* -* @copyright 2009 Oliver Rahs -* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later -*/ + * A rule representing the safe browser check. + * + * @copyright 2009 Oliver Rahs + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class quizaccess_safebrowser extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { @@ -68,7 +68,7 @@ public function setup_attempt_page($page) { * * @return true, if browser is safe browser else false */ - function check_safe_browser() { + public function check_safe_browser() { return strpos($_SERVER['HTTP_USER_AGENT'], 'SEB') !== false; } diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index 6b766425ef99c..ca05017a17aaf 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -30,12 +30,12 @@ /** -* A rule for ensuring that the quiz is opened in a popup, with some JavaScript -* to prevent copying and pasting, etc. -* -* @copyright 2009 Tim Hunt -* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later -*/ + * A rule for ensuring that the quiz is opened in a popup, with some JavaScript + * to prevent copying and pasting, etc. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class quizaccess_securewindow extends quiz_access_rule_base { /** @var array options that should be used for opening the secure popup. */ protected static $popupoptions = array( diff --git a/mod/quiz/accessrule/timelimit/rule.php b/mod/quiz/accessrule/timelimit/rule.php index 948b01e6d268e..7d274f9931710 100644 --- a/mod/quiz/accessrule/timelimit/rule.php +++ b/mod/quiz/accessrule/timelimit/rule.php @@ -30,12 +30,12 @@ /** -* A rule representing the time limit. It does not actually restrict access, but we use this -* class to encapsulate some of the relevant code. -* -* @copyright 2009 Tim Hunt -* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later -*/ + * A rule representing the time limit. It does not actually restrict access, but we use this + * class to encapsulate some of the relevant code. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class quizaccess_timelimit extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 57c21275ce57e..64af40e33591b 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -1311,7 +1311,7 @@ public function get_question_buttons() { } protected function get_state_string(question_attempt $qa, $showcorrectness) { - if ($qa->get_question()->length > 0) { + if ($qa->get_question()->length > 0) { return $qa->get_state_string($showcorrectness); } diff --git a/mod/quiz/backup/moodle1/lib.php b/mod/quiz/backup/moodle1/lib.php index dec659ef1d927..4f43ef225a4ac 100644 --- a/mod/quiz/backup/moodle1/lib.php +++ b/mod/quiz/backup/moodle1/lib.php @@ -1,5 +1,4 @@ array( 'feedbacktextformat' => 0 @@ -112,12 +115,14 @@ public function process_quiz($data) { // convert course files embedded into the intro $this->fileman->filearea = 'intro'; $this->fileman->itemid = 0; - $data['intro'] = moodle1_converter::migrate_referenced_files($data['intro'], $this->fileman); + $data['intro'] = moodle1_converter::migrate_referenced_files( + $data['intro'], $this->fileman); // start writing quiz.xml $this->open_xml_writer("activities/quiz_{$this->moduleid}/quiz.xml"); - $this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $this->moduleid, - 'modulename' => 'quiz', 'contextid' => $contextid)); + $this->xmlwriter->begin_tag('activity', array('id' => $instanceid, + 'moduleid' => $this->moduleid, 'modulename' => 'quiz', + 'contextid' => $contextid)); $this->xmlwriter->begin_tag('quiz', array('id' => $instanceid)); foreach ($data as $field => $value) { diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index bbe3e38264f92..6a937ef474514 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -369,8 +369,9 @@ function quiz_user_outline($course, $user, $mod, $quiz) { $result->info = get_string('grade') . ': ' . $grade->str_long_grade; //datesubmitted == time created. dategraded == time modified or time overridden - //if grade was last modified by the user themselves use date graded. Otherwise use date submitted - //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704 + //if grade was last modified by the user themselves use date graded. Otherwise use + // date submitted + // TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704 if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) { $result->time = $grade->dategraded; } else { diff --git a/mod/quiz/renderer.php b/mod/quiz/renderer.php index 0374d5833ba66..750713081d6a5 100644 --- a/mod/quiz/renderer.php +++ b/mod/quiz/renderer.php @@ -715,7 +715,7 @@ public function start_attempt_button($buttontext, moodle_url $url, * @param object $context the quiz context. * @return string HTML to output. */ - function no_questions_message($canedit, $editurl) { + public function no_questions_message($canedit, $editurl) { $output = ''; $output .= $this->notification(get_string('noquestions', 'quiz')); if ($canedit) { @@ -1093,7 +1093,8 @@ class mod_quiz_view_object { public $attempts; /** @var quiz_access_manager $accessmanager contains various access rules. */ public $accessmanager; - /** @var bool $canreviewmine whether the current user has the capability to review their own attempts. */ + /** @var bool $canreviewmine whether the current user has the capability to + * review their own attempts. */ public $canreviewmine; /** @var bool $canedit whether the current user has the capability to edit the quiz. */ public $canedit; @@ -1125,7 +1126,8 @@ class mod_quiz_view_object { public $unfinished; /** @var object $lastfinishedattempt the last attempt from the attempts array. */ public $lastfinishedattempt; - /** @var array $preventmessages of messages telling the user why they can't attempt the quiz now. */ + /** @var array $preventmessages of messages telling the user why they can't + * attempt the quiz now. */ public $preventmessages; /** @var string $buttontext caption for the start attempt button. If this is null, show no * button, or if it is '' show a back to the course button. */ diff --git a/mod/quiz/report/overview/db/install.xml b/mod/quiz/report/overview/db/install.xml index fdfbcf4af451b..b077072a807fe 100644 --- a/mod/quiz/report/overview/db/install.xml +++ b/mod/quiz/report/overview/db/install.xml @@ -19,4 +19,4 @@
    - \ No newline at end of file + diff --git a/mod/quiz/report/overview/report.php b/mod/quiz/report/overview/report.php index 3c16ee40e97cf..9b518a4ddd336 100644 --- a/mod/quiz/report/overview/report.php +++ b/mod/quiz/report/overview/report.php @@ -117,10 +117,12 @@ public function display($quiz, $cm, $course) { } $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); + $courseshortname = format_string($course->shortname, true, + array('context' => $coursecontext)); $displaycoursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id); - $displaycourseshortname = format_string($COURSE->shortname, true, array('context' => $displaycoursecontext)); + $displaycourseshortname = format_string($COURSE->shortname, true, + array('context' => $displaycoursecontext)); // Load the required questions. $questions = quiz_report_get_significant_questions($quiz); diff --git a/mod/quiz/report/responses/report.php b/mod/quiz/report/responses/report.php index a0bb7f8d2550e..aad714022e7d7 100644 --- a/mod/quiz/report/responses/report.php +++ b/mod/quiz/report/responses/report.php @@ -124,7 +124,8 @@ public function display($quiz, $cm, $course) { $allowed = array(); } - if ($attemptids = optional_param_array('attemptid', array(), PARAM_INT) && confirm_sesskey()) { + $attemptids = optional_param_array('attemptid', array(), PARAM_INT); + if ($attemptids && confirm_sesskey()) { require_capability('mod/quiz:deleteattempts', $this->context); $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed); redirect($reporturl->out(false, $displayoptions)); @@ -134,10 +135,12 @@ public function display($quiz, $cm, $course) { $questions = quiz_report_get_significant_questions($quiz); $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); + $courseshortname = format_string($course->shortname, true, + array('context' => $coursecontext)); $displaycoursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id); - $displaycourseshortname = format_string($COURSE->shortname, true, array('context' => $displaycoursecontext)); + $displaycourseshortname = format_string($COURSE->shortname, true, + array('context' => $displaycoursecontext)); $table = new quiz_report_responses_table($quiz, $this->context, $qmsubselect, $groupstudents, $students, $questions, $candelete, $reporturl, $displayoptions); diff --git a/mod/quiz/report/statistics/db/upgrade.php b/mod/quiz/report/statistics/db/upgrade.php index 4b254ddaab777..a25da3ca48600 100644 --- a/mod/quiz/report/statistics/db/upgrade.php +++ b/mod/quiz/report/statistics/db/upgrade.php @@ -385,7 +385,8 @@ function xmldb_quiz_statistics_upgrade($oldversion) { // Changing sign of field maxmark on table quiz_question_statistics to signed $table = new xmldb_table('quiz_question_statistics'); - $field = new xmldb_field('maxmark', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'subquestions'); + $field = new xmldb_field('maxmark', XMLDB_TYPE_NUMBER, '12, 7', null, + null, null, null, 'subquestions'); // Launch change of sign for field maxmark $dbman->change_field_unsigned($table, $field); diff --git a/mod/quiz/report/statistics/report.php b/mod/quiz/report/statistics/report.php index 6b692804f371b..4d976005b61fe 100644 --- a/mod/quiz/report/statistics/report.php +++ b/mod/quiz/report/statistics/report.php @@ -116,7 +116,8 @@ public function display($quiz, $cm, $course) { $report = get_string('questionstatsfilename', 'quiz_statistics'); } $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); - $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext)); + $courseshortname = format_string($course->shortname, true, + array('context' => $coursecontext)); $filename = quiz_report_download_filename($report, $courseshortname, $quiz->name); $this->table->is_downloading($download, $filename, get_string('quizstructureanalysis', 'quiz_statistics')); From 4344c5d5d9c4765106e44bffcf4482697ececc7f Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Thu, 6 Oct 2011 20:15:01 +0100 Subject: [PATCH 24/67] MDL-29627 quiz old popup int column => new browsersecurity column. The new column store more meaningful string constants. --- mod/quiz/accessmanager.php | 2 +- mod/quiz/accessrule/safebrowser/rule.php | 4 +- .../safebrowser/simpletest/testrule.php | 2 +- mod/quiz/accessrule/securewindow/rule.php | 4 +- .../securewindow/simpletest/testrule.php | 2 +- .../backup/moodle2/backup_quiz_stepslib.php | 2 +- .../backup/moodle2/restore_quiz_stepslib.php | 15 ++++ mod/quiz/db/install.xml | 6 +- mod/quiz/db/upgrade.php | 70 +++++++++++++++++++ mod/quiz/lib.php | 10 +++ mod/quiz/locallib.php | 10 --- mod/quiz/mod_form.php | 8 +-- mod/quiz/settings.php | 29 +++----- mod/quiz/settingslib.php | 50 ++++++++++++- mod/quiz/version.php | 2 +- 15 files changed, 170 insertions(+), 46 deletions(-) diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index edd724cd124a4..5296f5c073481 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -116,7 +116,7 @@ public static function add_settings_form_fields( * @return array key => lang string. */ public static function get_browser_security_choices() { - $options = array(0 => get_string('none', 'quiz')); + $options = array('-' => get_string('none', 'quiz')); foreach (self::get_rule_classes() as $rule) { $options += $rule::get_browser_security_choices(); } diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index 4d198bf45196e..e0d11e2274ccf 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -39,7 +39,7 @@ class quizaccess_safebrowser extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { - if ($quizobj->get_quiz()->popup != 2) { + if ($quizobj->get_quiz()->browsersecurity !== 'safebrowser') { return null; } @@ -83,6 +83,6 @@ public static function get_browser_security_choices() { return array(); } - return array(2 => get_string('requiresafeexambrowser', 'quiz')); + return array('safebrowser' => get_string('requiresafeexambrowser', 'quiz')); } } diff --git a/mod/quiz/accessrule/safebrowser/simpletest/testrule.php b/mod/quiz/accessrule/safebrowser/simpletest/testrule.php index 57dd749a47c06..5baab3015f385 100644 --- a/mod/quiz/accessrule/safebrowser/simpletest/testrule.php +++ b/mod/quiz/accessrule/safebrowser/simpletest/testrule.php @@ -41,7 +41,7 @@ class quizaccess_safebrowser_test extends UnitTestCase { // Nothing very testable in this class, just test that it obeys the general access rule contact. public function test_safebrowser_access_rule() { $quiz = new stdClass(); - $quiz->popup = 1; + $quiz->browsersecurity = 'safebrowser'; $quiz->questions = ''; $cm = new stdClass(); $cm->id = 0; diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index ca05017a17aaf..7e471eff3a862 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -54,7 +54,7 @@ class quizaccess_securewindow extends quiz_access_rule_base { public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { - if ($quizobj->get_quiz()->popup != 1) { + if ($quizobj->get_quiz()->browsersecurity !== 'securewindow') { return null; } @@ -89,6 +89,6 @@ public function setup_attempt_page($page) { * security settings menu. */ public static function get_browser_security_choices() { - return array(1 => get_string('popupwithjavascriptsupport', 'quiz')); + return array('securewindow' => get_string('popupwithjavascriptsupport', 'quiz')); } } diff --git a/mod/quiz/accessrule/securewindow/simpletest/testrule.php b/mod/quiz/accessrule/securewindow/simpletest/testrule.php index 04e0801680574..dc509a020ad60 100644 --- a/mod/quiz/accessrule/securewindow/simpletest/testrule.php +++ b/mod/quiz/accessrule/securewindow/simpletest/testrule.php @@ -41,7 +41,7 @@ class quizaccess_securewindow_test extends UnitTestCase { // Nothing very testable in this class, just test that it obeys the general access rule contact. public function test_securewindow_access_rule() { $quiz = new stdClass(); - $quiz->popup = 1; + $quiz->browsersecurity = 'securewindow'; $quiz->questions = ''; $cm = new stdClass(); $cm->id = 0; diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index f047f4a9a18be..1e512974e8a15 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -49,7 +49,7 @@ protected function define_structure() { 'questionsperpage', 'shufflequestions', 'shuffleanswers', 'questions', 'sumgrades', 'grade', 'timecreated', 'timemodified', 'timelimit', 'password', 'subnet', - 'popup', 'delay1', 'delay2', 'showuserpicture', + 'browsersecurity', 'delay1', 'delay2', 'showuserpicture', 'showblocks')); $qinstances = new backup_nested_element('question_instances'); diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index a80f87ce383be..90756fe1674fe 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -195,6 +195,21 @@ protected function process_quiz($data) { mod_quiz_display_options::AFTER_CLOSE : 0); } + // The old popup column from from <= 2.1 need to be mapped to + // the new browsersecurity. MDL-29627 + if (!isset($data->browsersecurity)) { + if (empty($data->popup)) { + $data->browsersecurity = '-'; + } else if ($data->popup == 1) { + $data->browsersecurity = 'securewindow'; + } else if ($data->popup == 2) { + $data->browsersecurity = 'safebrowser'; + } else { + $data->preferredbehaviour = '-'; + } + unset($data->popup); + } + // insert the quiz record $newitemid = $DB->insert_record('quiz', $data); // immediately after inserting "activity" record, call this diff --git a/mod/quiz/db/install.xml b/mod/quiz/db/install.xml index f9f0af99d1609..8ed07e5125565 100644 --- a/mod/quiz/db/install.xml +++ b/mod/quiz/db/install.xml @@ -36,9 +36,9 @@ - - - + + + diff --git a/mod/quiz/db/upgrade.php b/mod/quiz/db/upgrade.php index 6319c9fec7621..8a963a4be9909 100644 --- a/mod/quiz/db/upgrade.php +++ b/mod/quiz/db/upgrade.php @@ -1121,6 +1121,76 @@ function xmldb_quiz_upgrade($oldversion) { // Moodle v2.1.0 release upgrade line // Put any upgrade step following this + if ($oldversion < 2011100600) { + + // Define field browsersecurity to be added to quiz + $table = new xmldb_table('quiz'); + $field = new xmldb_field('browsersecurity', XMLDB_TYPE_CHAR, '32', null, + XMLDB_NOTNULL, null, '[unknownvalue]', 'subnet'); + + // Conditionally launch add field browsersecurity + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // quiz savepoint reached + upgrade_mod_savepoint(true, 2011100600, 'quiz'); + } + + if ($oldversion < 2011100601) { + $DB->set_field('quiz', 'browsersecurity', '-', array('popup' => 0)); + $DB->set_field('quiz', 'browsersecurity', 'securewindow', array('popup' => 1)); + $DB->set_field('quiz', 'browsersecurity', 'safebrowser', array('popup' => 2)); + + upgrade_mod_savepoint(true, 2011100601, 'quiz'); + } + + if ($oldversion < 2011100602) { + + // Changing the default of field browsersecurity on table quiz to drop it + $table = new xmldb_table('quiz'); + $field = new xmldb_field('browsersecurity', XMLDB_TYPE_CHAR, '32', null, + XMLDB_NOTNULL, null, null, 'subnet'); + + // Launch change of default for field browsersecurity + $dbman->change_field_default($table, $field); + + // quiz savepoint reached + upgrade_mod_savepoint(true, 2011100602, 'quiz'); + } + + if ($oldversion < 2011100603) { + + // Define field popup to be dropped from quiz + $table = new xmldb_table('quiz'); + $field = new xmldb_field('popup'); + + // Conditionally launch drop field popup + if ($dbman->field_exists($table, $field)) { + $dbman->drop_field($table, $field); + } + + // quiz savepoint reached + upgrade_mod_savepoint(true, 2011100603, 'quiz'); + } + + if ($oldversion < 2011100604) { + switch (get_config('quiz', 'popup')) { + case 1: + set_config('browsersecurity', 'securewindow', 'quiz'); + case 2: + set_config('browsersecurity', 'safebrowser', 'quiz'); + default: + set_config('browsersecurity', '-', 'quiz'); + } + unset_config('quiz', 'popup'); + + set_config('browsersecurity_adv', get_config('quiz', 'popup_adv'), 'quiz'); + unset_config('quiz', 'popup_adv'); + + upgrade_mod_savepoint(true, 2011100604, 'quiz'); + } + return true; } diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 6a937ef474514..1a45b157e2720 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -42,6 +42,16 @@ define('QUIZ_MAX_Q_DECIMAL_OPTION', 7); /**#@-*/ +/**#@+ + * Options determining how the grades from individual attempts are combined to give + * the overall grade for a user + */ +define('QUIZ_GRADEHIGHEST', '1'); +define('QUIZ_GRADEAVERAGE', '2'); +define('QUIZ_ATTEMPTFIRST', '3'); +define('QUIZ_ATTEMPTLAST', '4'); +/**#@-*/ + /** * If start and end date for the quiz are more than this many seconds apart * they will be represented by two separate events in the calendar diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index b3d96fe261deb..416588a504e9d 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -41,16 +41,6 @@ require_once($CFG->libdir . '/filelib.php'); -/**#@+ - * Options determining how the grades from individual attempts are combined to give - * the overall grade for a user - */ -define('QUIZ_GRADEHIGHEST', '1'); -define('QUIZ_GRADEAVERAGE', '2'); -define('QUIZ_ATTEMPTFIRST', '3'); -define('QUIZ_ATTEMPTLAST', '4'); -/**#@-*/ - /** * We show the countdown timer if there is less than this amount of time left before the * the quiz close date. (1 hour) diff --git a/mod/quiz/mod_form.php b/mod/quiz/mod_form.php index 9130cad295c27..92178f6080fdd 100644 --- a/mod/quiz/mod_form.php +++ b/mod/quiz/mod_form.php @@ -282,11 +282,11 @@ protected function definition() { $mform->disabledIf('delay2', 'attempts', 'eq', 2); // 'Secure' window. - $mform->addElement('select', 'popup', get_string('browsersecurity', 'quiz'), + $mform->addElement('select', 'browsersecurity', get_string('browsersecurity', 'quiz'), quiz_access_manager::get_browser_security_choices()); - $mform->addHelpButton('popup', 'browsersecurity', 'quiz'); - $mform->setAdvanced('popup', $quizconfig->popup_adv); - $mform->setDefault('popup', $quizconfig->popup); + $mform->addHelpButton('browsersecurity', 'browsersecurity', 'quiz'); + $mform->setAdvanced('browsersecurity', $quizconfig->browsersecurity_adv); + $mform->setDefault('browsersecurity', $quizconfig->browsersecurity); // Any other rule plugins. quiz_access_manager::add_settings_form_fields($this, $mform); diff --git a/mod/quiz/settings.php b/mod/quiz/settings.php index 8bbb14b666d68..85561a7f276fb 100644 --- a/mod/quiz/settings.php +++ b/mod/quiz/settings.php @@ -27,26 +27,17 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/mod/quiz/lib.php'); -require_once($CFG->dirroot . '/mod/quiz/locallib.php'); require_once($CFG->dirroot . '/mod/quiz/settingslib.php'); // First get a list of quiz reports with there own settings pages. If there none, // we use a simpler overall menu structure. +$reports = get_plugin_list_with_file('quiz', 'settings.php', false); $reportsbyname = array(); -if ($reports = get_plugin_list('quiz')) { - foreach ($reports as $report => $reportdir) { - if (file_exists("$reportdir/settings.php")) { - $strreportname = get_string($report . 'report', 'quiz_'.$report); - // Deal with reports which are lacking the language string - if ($strreportname[0] == '[') { - $textlib = textlib_get_instance(); - $strreportname = $textlib->strtotitle($report . ' report'); - } - $reportsbyname[$strreportname] = $report; - } - } - ksort($reportsbyname); +foreach ($reports as $report => $reportdir) { + $strreportname = get_string($report . 'report', 'quiz_'.$report); + $reportsbyname[$strreportname] = $report; } +ksort($reportsbyname); // Create the quiz settings page. if (empty($reportsbyname)) { @@ -74,9 +65,9 @@ array('value' => 0, 'fix' => false), $options)); // Grading method. -$quizsettings->add(new admin_setting_configselect_with_advanced('quiz/grademethod', +$quizsettings->add(new mod_quiz_admin_setting_grademethod('quiz/grademethod', get_string('grademethod', 'quiz'), get_string('configgrademethod', 'quiz'), - array('value' => QUIZ_GRADEHIGHEST, 'fix' => false), quiz_get_grading_options())); + array('value' => QUIZ_GRADEHIGHEST, 'fix' => false), null)); // Maximum grade $quizsettings->add(new admin_setting_configtext('quiz/maximumgrade', @@ -178,9 +169,9 @@ array('value' => 0, 'fix' => true), PARAM_INTEGER)); // 'Secure' window. -$quizsettings->add(new admin_setting_configcheckbox_with_advanced('quiz/popup', +$quizsettings->add(new mod_quiz_admin_setting_browsersecurity('quiz/browsersecurity', get_string('showinsecurepopup', 'quiz'), get_string('configpopup', 'quiz'), - array('value' => 0, 'adv' => true))); + array('value' => '-', 'adv' => true), null)); // Now, depending on whether any reports have their own settings page, add // the quiz setting page to the appropriate place in the tree. @@ -198,7 +189,7 @@ $settings = new admin_settingpage('modsettingsquizcat'.$reportname, $strreportname, 'moodle/site:config', !$module->visible); if ($ADMIN->fulltree) { - include($CFG->dirroot."/mod/quiz/report/$reportname/settings.php"); + include($CFG->dirroot . "/mod/quiz/report/$reportname/settings.php"); } $ADMIN->add('modsettingsquizcat', $settings); } diff --git a/mod/quiz/settingslib.php b/mod/quiz/settingslib.php index 36247b7d6f3e7..d8c2a02c416dd 100644 --- a/mod/quiz/settingslib.php +++ b/mod/quiz/settingslib.php @@ -29,7 +29,7 @@ /** - * Quiz specific admin settings class. + * Admin settings class for the quiz review opitions. * * @copyright 2008 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -147,3 +147,51 @@ public function output_html($data, $query = '') { $this->description, true, '', get_string('everythingon', 'quiz'), $query); } } + + +/** + * Admin settings class for the quiz grading method. + * + * Just so we can lazy-load the choices. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_quiz_admin_setting_grademethod extends admin_setting_configselect_with_advanced { + public function load_choices() { + global $CFG; + + if (is_array($this->choices)) { + return true; + } + + require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + $this->choices = quiz_get_grading_options(); + + return true; + } +} + + +/** + * Admin settings class for the quiz browser security option. + * + * Just so we can lazy-load the choices. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_quiz_admin_setting_browsersecurity extends admin_setting_configselect_with_advanced { + public function load_choices() { + global $CFG; + + if (is_array($this->choices)) { + return true; + } + + require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + $this->choices = quiz_access_manager::get_browser_security_choices(); + + return true; + } +} diff --git a/mod/quiz/version.php b/mod/quiz/version.php index 1381c23225b34..c9ebe0847cf5b 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -25,6 +25,6 @@ defined('MOODLE_INTERNAL') || die(); -$module->version = 2011070100; +$module->version = 2011100604; $module->requires = 2011060313; $module->cron = 0; From 987c2d491ec45c806354963af1819eb85df21a29 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 7 Oct 2011 17:43:15 +0100 Subject: [PATCH 25/67] MDL-29627 quiz access refactor password check into a generic check page. --- mod/quiz/accessmanager.php | 95 ++++++++++----- mod/quiz/accessmanager_form.php | 72 ++++++++++++ mod/quiz/accessrule/accessrulebase.php | 62 +++++++++- .../password/lang/en/quizaccess_password.php | 2 +- mod/quiz/accessrule/password/rule.php | 111 ++++++------------ mod/quiz/attempt.php | 4 +- mod/quiz/attemptlib.php | 4 +- mod/quiz/locallib.php | 1 + mod/quiz/renderer.php | 46 ++++++-- mod/quiz/startattempt.php | 59 +++++++--- mod/quiz/summary.php | 4 +- 11 files changed, 322 insertions(+), 138 deletions(-) create mode 100644 mod/quiz/accessmanager_form.php diff --git a/mod/quiz/accessmanager.php b/mod/quiz/accessmanager.php index 5296f5c073481..22ed02aa5bddb 100644 --- a/mod/quiz/accessmanager.php +++ b/mod/quiz/accessmanager.php @@ -35,11 +35,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class quiz_access_manager { + /** @var quiz the quiz settings object. */ protected $quizobj; + /** @var int the time to be considered as 'now'. */ protected $timenow; - protected $passwordrule = null; - protected $securewindowrule = null; - protected $safebrowserrule = null; + /** @var array of quiz_access_rule_base. */ protected $rules = array(); /** @@ -232,7 +232,13 @@ public function get_active_rule_names() { return $classnames; } - protected function accumulate_messages(&$messages, $new) { + /** + * Accumulates an array of messages. + * @param array $messages the current list of messages. + * @param string|array $new the new messages or messages. + * @return array the updated array of messages. + */ + protected function accumulate_messages($messages, $new) { if (is_array($new)) { $messages = array_merge($messages, $new); } else if (is_string($new) && $new) { @@ -258,8 +264,8 @@ public function describe_rules() { } /** - * Is it OK to let the current user start a new attempt now? If there are - * any restrictions in force now, return an array of reasons why access + * Whether or not a user should be allowed to start a new attempt at this quiz now. + * If there are any restrictions in force now, return an array of reasons why access * should be blocked. If access is OK, return false. * * @param int $numattempts the number of previous attempts this user has made. @@ -278,9 +284,9 @@ public function prevent_new_attempt($numprevattempts, $lastattempt) { } /** - * Is it OK to let the current user start a new attempt now? If there are - * any restrictions in force now, return an array of reasons why access - * should be blocked. If access is OK, return false. + * Whether the user should be blocked from starting a new attempt or continuing + * an attempt now. If there are any restrictions in force now, return an array + * of reasons why access should be blocked. If access is OK, return false. * * @return mixed An array of reason why access is not allowed, or an empty array * (== false) if access should be allowed. @@ -293,6 +299,56 @@ public function prevent_access() { return $reasons; } + /** + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + * @return bool whether a check is required before the user starts/continues + * their attempt. + */ + public function is_preflight_check_required($attemptid) { + foreach ($this->rules as $rule) { + if ($rule->is_preflight_check_required($attemptid)) { + return true; + } + } + return false; + } + + /** + * Build the form required to do the pre-flight checks. + * @param moodle_url $url the form action URL. + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + * @return mod_quiz_preflight_check_form the form. + */ + public function get_preflight_check_form(moodle_url $url, $attemptid) { + return new mod_quiz_preflight_check_form($url->out_omit_querystring(), + array('rules' => $this->rules, 'quizobj' => $this->quizobj, + 'attemptid' => $attemptid, 'hidden' => $url->params())); + } + + /** + * The pre-flight check has passed. This is a chance to record that fact in + * some way. + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + */ + public function notify_preflight_check_passed($attemptid) { + foreach ($this->rules as $rule) { + $rule->notify_preflight_check_passed($attemptid); + } + } + + /** + * Inform the rules that the current attempt is finished. This is use, for example + * by the password rule, to clear the flag in the session. + */ + public function current_attempt_finished() { + foreach ($this->rules as $rule) { + $rule->current_attempt_finished(); + } + } + /** * Do any of the rules mean that this student will no be allowed any further attempts at this * quiz. Used, for example, to change the label by the grade displayed on the view page from @@ -392,27 +448,6 @@ public function back_to_view_page($output, $message = '') { } } - /** - * Clear the flag in the session that says that the current user is allowed to do this quiz. - */ - public function clear_password_access() { - if (!is_null($this->passwordrule)) { - $this->passwordrule->clear_access_allowed(); - } - } - - /** - * Actually ask the user for the password, if they have not already given it this session. - * This function only returns is access is OK. - * - * @param bool $canpreview used to enfore securewindow stuff. - */ - public function do_password_check($canpreview) { - if (!is_null($this->passwordrule)) { - $this->passwordrule->do_password_check($canpreview, $this); - } - } - /** * Make some text into a link to review the quiz, if that is appropriate. * diff --git a/mod/quiz/accessmanager_form.php b/mod/quiz/accessmanager_form.php new file mode 100644 index 0000000000000..0018791da5d5b --- /dev/null +++ b/mod/quiz/accessmanager_form.php @@ -0,0 +1,72 @@ +. + +/** + * Defines the form that limits student's access to attempt a quiz. + * + * @package mod + * @subpackage quiz + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once ($CFG->libdir.'/formslib.php'); + + +/** + * A form that limits student's access to attempt a quiz. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_quiz_preflight_check_form extends moodleform { + + protected function definition() { + $mform = $this->_form; + + foreach ($this->_customdata['hidden'] as $name => $value) { + if ($name === 'sesskey') { + continue; + } + $mform->addElement('hidden', $name, $value); + } + + foreach ($this->_customdata['rules'] as $rule) { + if ($rule->is_preflight_check_required($this->_customdata['attemptid'])) { + $rule->add_preflight_check_form_fields($this, $mform, + $this->_customdata['attemptid']); + } + } + + $this->add_action_buttons(true, get_string('continue')); + } + + public function validation($data, $files) { + $errors = parent::validation($data, $files); + + foreach ($this->_customdata['rules'] as $rule) { + if ($rule->is_preflight_check_required($this->_customdata['attemptid'])) { + $errors = $rule->validate_preflight_check($data, $files, $errors, + $this->_customdata['attemptid']); + } + } + + return $errors; + } +} diff --git a/mod/quiz/accessrule/accessrulebase.php b/mod/quiz/accessrule/accessrulebase.php index afd29a57d6abc..a30a048a85cb9 100644 --- a/mod/quiz/accessrule/accessrulebase.php +++ b/mod/quiz/accessrule/accessrulebase.php @@ -83,7 +83,8 @@ public function prevent_new_attempt($numprevattempts, $lastattempt) { } /** - * Whether or not a user should be allowed to start a new attempt at this quiz now. + * Whether the user should be blocked from starting a new attempt or continuing + * an attempt now. * @return string false if access should be allowed, a message explaining the * reason if access should be prevented. */ @@ -91,6 +92,65 @@ public function prevent_access() { return false; } + /** + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + * @return bool whether a check is required before the user starts/continues + * their attempt. + */ + public function is_preflight_check_required($attemptid) { + return false; + } + + /** + * Add any field you want to pre-flight check form. You should only do + * something here if {@link is_preflight_check_required()} returned true. + * + * @param mod_quiz_preflight_check_form $quizform the form being built. + * @param MoodleQuickForm $mform The wrapped MoodleQuickForm. + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + */ + public function add_preflight_check_form_fields(mod_quiz_preflight_check_form $quizform, + MoodleQuickForm $mform, $attemptid) { + // Do nothing by default. + } + + /** + * Validate the pre-flight check form submission. You should only do + * something here if {@link is_preflight_check_required()} returned true. + * + * If the form validates, the user will be allowed to continue. + * + * @param array $data the submitted form data. + * @param array $files any files in the submission. + * @param array $errors the list of validation errors that is being built up. + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + * @return array the update $errors array; + */ + public function validate_preflight_check($data, $files, $errors, $attemptid) { + return $errors; + } + + /** + * The pre-flight check has passed. This is a chance to record that fact in + * some way. + * @param int|null $attemptid the id of the current attempt, if there is one, + * otherwise null. + */ + public function notify_preflight_check_passed($attemptid) { + // Do nothing by default. + } + + /** + * This is called when the current attempt at the quiz is finished. This is + * used, for example by the password rule, to clear the flag in the session. + */ + public function current_attempt_finished() { + // Do nothing by default. + } + /** * Information, such as might be shown on the quiz view page, relating to this restriction. * There is no obligation to return anything. If it is not appropriate to tell students diff --git a/mod/quiz/accessrule/password/lang/en/quizaccess_password.php b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php index 415770321de68..132e83315498e 100644 --- a/mod/quiz/accessrule/password/lang/en/quizaccess_password.php +++ b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php @@ -23,8 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - defined('MOODLE_INTERNAL') || die(); $string['pluginname'] = 'Password quiz access rule'; +$string['quizpassword'] = 'Quiz password'; \ No newline at end of file diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php index ffc6968cf46f1..571414451216b 100644 --- a/mod/quiz/accessrule/password/rule.php +++ b/mod/quiz/accessrule/password/rule.php @@ -30,8 +30,7 @@ /** - * A rule representing the password check. It does not actually implement the check, - * that has to be done directly in attempt.php, but this facilitates telling users about it. + * A rule implementing the password check. * * @copyright 2009 Tim Hunt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -50,97 +49,53 @@ public function description() { return get_string('requirepasswordmessage', 'quiz'); } - /** - * Clear the flag in the session that says that the current user is allowed to do this quiz. - */ - public function clear_access_allowed() { + public function is_preflight_check_required($attemptid) { global $SESSION; - if (!empty($SESSION->passwordcheckedquizzes[$this->quiz->id])) { - unset($SESSION->passwordcheckedquizzes[$this->quiz->id]); - } + return empty($SESSION->passwordcheckedquizzes[$this->quiz->id]); } - /** - * Actually ask the user for the password, if they have not already given it this session. - * This function only returns is access is OK. - * - * @param bool $canpreview used to enfore securewindow stuff. - * @param object $accessmanager the accessmanager calling us. - * @return mixed return null, unless $return is true, and a form needs to be displayed. - */ - public function do_password_check($canpreview, $accessmanager) { - global $CFG, $SESSION, $OUTPUT, $PAGE; - - // We have already checked the password for this quiz this session, so don't ask again. - if (!empty($SESSION->passwordcheckedquizzes[$this->quiz->id])) { - return; - } + public function add_preflight_check_form_fields(mod_quiz_preflight_check_form $quizform, + MoodleQuickForm $mform, $attemptid) { - // If the user cancelled the password form, send them back to the view page. - if (optional_param('cancelpassword', false, PARAM_BOOL)) { - $accessmanager->back_to_view_page($output); - } + $mform->addElement('header', 'passwordheader', get_string('password')); + $mform->addElement('static', 'passwordmessage', '', + get_string('requirepasswordmessage', 'quiz')); + + // don't use the 'proper' field name of 'password' since that get's + // Firefox's password auto-complete over-excited. + $mform->addElement('password', 'quizpassword', get_string('quizpassword', 'quizaccess_password')); + } - // If they entered the right password, let them in. - $enteredpassword = optional_param('quizpassword', '', PARAM_RAW); - $validpassword = false; + public function validate_preflight_check($data, $files, $errors, $attemptid) { + + $enteredpassword = $data['quizpassword']; if (strcmp($this->quiz->password, $enteredpassword) === 0) { - $validpassword = true; + return $errors; // Password is OK. + } else if (isset($this->quiz->extrapasswords)) { - // group overrides may have additional passwords + // Group overrides may have additional passwords foreach ($this->quiz->extrapasswords as $password) { if (strcmp($password, $enteredpassword) === 0) { - $validpassword = true; - break; + return $errors; // Password is OK. } } } - if ($validpassword) { - $SESSION->passwordcheckedquizzes[$this->quiz->id] = true; - return; - } - - // User entered the wrong password, or has not entered one yet, so display the form. - $output = ''; - // Start the page and print the quiz intro, if any. - $PAGE->set_title(format_string($this->quizobj->get_quiz_name())); - $accessmanager->setup_attempt_page($PAGE); + $errors['quizpassword'] = get_string('passworderror', 'quiz'); + return $errors; + } - echo $OUTPUT->header(); - if (trim(strip_tags($this->quiz->intro))) { - $output .= $OUTPUT->box(format_module_intro('quiz', $this->quiz, - $this->quizobj->get_cmid()), 'generalbox', 'intro'); - } - $output .= $OUTPUT->box_start('generalbox', 'passwordbox'); + public function notify_preflight_check_passed($attemptid) { + global $SESSION; + $SESSION->passwordcheckedquizzes[$this->quiz->id] = true; + } - // If they have previously tried and failed to enter a password, tell them it was wrong. - if (!empty($enteredpassword)) { - $output .= '

    ' . get_string('passworderror', 'quiz') . '

    '; + public function current_attempt_finished() { + global $SESSION; + // Clear the flag in the session that says that the user has already + // entered the password for this quiz. + if (!empty($SESSION->passwordcheckedquizzes[$this->quiz->id])) { + unset($SESSION->passwordcheckedquizzes[$this->quiz->id]); } - - // Print the password entry form. - $output .= '

    ' . get_string('requirepasswordmessage', 'quiz') . "

    \n"; - $output .= '
    ' . "\n"; - $output .= "
    \n"; - $output .= '\n"; - $output .= '' . "\n"; - $output .= '' . "\n"; - $output .= '' . "\n"; - $output .= ''; - $output .= '' . "\n"; - $output .= "
    \n"; - $output .= "
    \n"; - - // Finish page. - $output .= $OUTPUT->box_end(); - - // return or display form. - echo $output; - echo $OUTPUT->footer(); - exit; } } diff --git a/mod/quiz/attempt.php b/mod/quiz/attempt.php index 03ba4e3f1ab53..5c5395d807022 100644 --- a/mod/quiz/attempt.php +++ b/mod/quiz/attempt.php @@ -80,7 +80,9 @@ print_error('attempterror', 'quiz', $attemptobj->view_url(), $output->access_messages($messages)); } -$accessmanager->do_password_check($attemptobj->is_preview_user()); +if ($accessmanager->is_preflight_check_required($attemptobj->get_attemptid())) { + redirect($attemptobj->start_attempt_url(null, $page)); +} add_to_log($attemptobj->get_courseid(), 'quiz', 'continue attempt', 'review.php?attempt=' . $attemptobj->get_attemptid(), diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 64af40e33591b..22dba91fac0a3 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -1162,8 +1162,8 @@ public function finish_attempt($timestamp) { $eventdata->courseid = $this->get_courseid(); events_trigger('quiz_attempt_submitted', $eventdata); - // Clear the password check flag in the session. - $this->get_access_manager($timestamp)->clear_password_access(); + // Tell any access rules that care that the attempt is over. + $this->get_access_manager($timestamp)->current_attempt_finished(); } } diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 416588a504e9d..98ddd21d2aa9f 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -34,6 +34,7 @@ require_once($CFG->dirroot . '/mod/quiz/lib.php'); require_once($CFG->dirroot . '/mod/quiz/accessmanager.php'); +require_once($CFG->dirroot . '/mod/quiz/accessmanager_form.php'); require_once($CFG->dirroot . '/mod/quiz/renderer.php'); require_once($CFG->dirroot . '/mod/quiz/attemptlib.php'); require_once($CFG->dirroot . '/question/editlib.php'); diff --git a/mod/quiz/renderer.php b/mod/quiz/renderer.php index 750713081d6a5..a43a3004e4dd8 100644 --- a/mod/quiz/renderer.php +++ b/mod/quiz/renderer.php @@ -362,6 +362,17 @@ protected function render_mod_quiz_links_to_other_attempts( return implode(', ', $attemptlinks); } + public function start_attempt_page(quiz $quizobj, mod_quiz_preflight_check_form $mform) { + $output = ''; + $output .= $this->header(); + $output .= $this->quiz_intro($quizobj->get_quiz(), $quizobj->get_cm()); + ob_start(); + $mform->display(); + $output .= ob_get_clean(); + $output .= $this->footer(); + return $output; + } + /** * Attempt Page * @@ -626,7 +637,7 @@ public function summary_page_controls($attemptobj) { */ public function view_page($course, $quiz, $cm, $context, $viewobj) { $output = ''; - $output .= $this->view_information($course, $quiz, $cm, $context, $viewobj->infomessages); + $output .= $this->view_information($quiz, $cm, $context, $viewobj->infomessages); $output .= $this->view_table($quiz, $context, $viewobj); $output .= $this->view_best_score($viewobj); $output .= $this->view_result_info($quiz, $context, $cm, $viewobj); @@ -736,7 +747,7 @@ public function no_questions_message($canedit, $editurl) { */ public function view_page_guest($course, $quiz, $cm, $context, $messages) { $output = ''; - $output .= $this->view_information($course, $quiz, $cm, $context, $messages); + $output .= $this->view_information($quiz, $cm, $context, $messages); $guestno = html_writer::tag('p', get_string('guestsno', 'quiz')); $liketologin = html_writer::tag('p', get_string('liketologin')); $output .= $this->confirm($guestno."\n\n".$liketologin."\n", get_login_url(), @@ -756,7 +767,7 @@ public function view_page_guest($course, $quiz, $cm, $context, $messages) { public function view_page_notenrolled($course, $quiz, $cm, $context, $messages) { global $CFG; $output = ''; - $output .= $this->view_information($course, $quiz, $cm, $context, $messages); + $output .= $this->view_information($quiz, $cm, $context, $messages); $youneedtoenrol = html_writer::tag('p', get_string('youneedtoenrol', 'quiz')); $button = html_writer::tag('p', $this->continue_button($CFG->wwwroot . '/course/view.php?id=' . $course->id)); @@ -767,14 +778,15 @@ public function view_page_notenrolled($course, $quiz, $cm, $context, $messages) /** * Output the page information * - * @param int $course The course ID - * @param array $quiz Array contingin quiz data - * @param int $cm Course Module ID - * @param int $context The page contect ID - * @param array $messages Array containing any messages + * @param object $quiz the quiz settings. + * @param object $cm the course_module object. + * @param object $context the quiz context. + * @param array $messages any access messages that should be described. + * @return string HTML to output. */ - public function view_information($course, $quiz, $cm, $context, $messages) { + public function view_information($quiz, $cm, $context, $messages) { global $CFG; + $output = ''; // Print quiz name and description $output .= $this->heading(format_string($quiz->name)); @@ -796,6 +808,22 @@ public function view_information($course, $quiz, $cm, $context, $messages) { return $output; } + /** + * Output the quiz intro. + * @param object $quiz the quiz settings. + * @param object $cm the course_module object. + * @return string HTML to output. + */ + public function quiz_intro($quiz, $cm) { + if (trim(strip_tags($quiz->intro))) { + $output .= $this->box(format_module_intro('quiz', $quiz, $cm->id), + 'generalbox', 'intro'); + + } else { + return ''; + } + } + /** * Generates the table heading. */ diff --git a/mod/quiz/startattempt.php b/mod/quiz/startattempt.php index 4cf5a926ac26c..c660df32e93dd 100644 --- a/mod/quiz/startattempt.php +++ b/mod/quiz/startattempt.php @@ -49,7 +49,6 @@ // Check login and sesskey. require_login($quizobj->get_courseid(), false, $quizobj->get_cm()); require_sesskey(); -$PAGE->set_pagelayout('base'); // if no questions have been set up yet redirect to edit.php if (!$quizobj->has_questions() && $quizobj->has_capability('mod/quiz:manage')) { @@ -59,7 +58,7 @@ // Create an object to manage all the other (non-roles) access rules. $accessmanager = $quizobj->get_access_manager(time()); if ($quizobj->is_preview_user() && $forcenew) { - $accessmanager->clear_password_access(); + $accessmanager->current_attempt_finished(); } // Check capabilities. @@ -76,33 +75,63 @@ } // Look for an existing attempt. -$attempts = quiz_get_user_attempts($quizobj->get_quizid(), $USER->id, 'all'); +$attempts = quiz_get_user_attempts($quizobj->get_quizid(), $USER->id, 'all', true); $lastattempt = end($attempts); // If an in-progress attempt exists, check password then redirect to it. if ($lastattempt && !$lastattempt->timefinish) { - $accessmanager->do_password_check($quizobj->is_preview_user()); - redirect($quizobj->attempt_url($lastattempt->id, $page)); -} + $currentattemptid = $lastattempt->id; + $messages = $accessmanager->prevent_access(); -// Get number for the next or unfinished attempt -if ($lastattempt && !$lastattempt->preview && !$quizobj->is_preview_user()) { - $attemptnumber = $lastattempt->attempt + 1; } else { - $lastattempt = false; - $attemptnumber = 1; + // Get number for the next or unfinished attempt + if ($lastattempt && !$lastattempt->preview && !$quizobj->is_preview_user()) { + $attemptnumber = $lastattempt->attempt + 1; + } else { + $lastattempt = false; + $attemptnumber = 1; + } + $currentattemptid = null; + + $messages = $accessmanager->prevent_access() + + $accessmanager->prevent_new_attempt(count($attempts), $lastattempt); } // Check access. -$messages = $accessmanager->prevent_access() + - $accessmanager->prevent_new_attempt(count($attempts), $lastattempt); +$output = $PAGE->get_renderer('mod_quiz'); if (!$quizobj->is_preview_user() && $messages) { - $output = $PAGE->get_renderer('mod_quiz'); print_error('attempterror', 'quiz', $quizobj->view_url(), $output->print_messages($messages)); } -$accessmanager->do_password_check($quizobj->is_preview_user()); +if ($accessmanager->is_preflight_check_required($currentattemptid)) { + // Need to do some checks before allowing the user to continue. + $mform = $accessmanager->get_preflight_check_form( + $quizobj->start_attempt_url($page), $currentattemptid); + + if ($mform->is_cancelled()) { + $accessmanager->back_to_view_page($output); + + } else if (!$mform->get_data()) { + + // Form not submitted successfully, re-display it and stop. + $PAGE->set_url($quizobj->start_attempt_url($page)); + $PAGE->set_title(format_string($quizobj->get_quiz_name())); + $accessmanager->setup_attempt_page($PAGE); + if (empty($quizobj->get_quiz()->showblocks)) { + $PAGE->blocks->show_only_fake_blocks(); + } + + echo $output->start_attempt_page($quizobj, $mform); + die(); + } + + // Pre-flight check passed. + $accessmanager->notify_preflight_check_passed($currentattemptid); +} +if ($currentattemptid) { + redirect($quizobj->attempt_url($currentattemptid, $page)); +} // Delete any previous preview attempts belonging to this user. quiz_delete_previews($quizobj->get_quiz(), $USER->id); diff --git a/mod/quiz/summary.php b/mod/quiz/summary.php index 7b0be2b466e7b..d75bd0e35c3a5 100644 --- a/mod/quiz/summary.php +++ b/mod/quiz/summary.php @@ -63,7 +63,9 @@ print_error('attempterror', 'quiz', $attemptobj->view_url(), $output->print_messages($messages)); } -$accessmanager->do_password_check($attemptobj->is_preview_user()); +if ($accessmanager->is_preflight_check_required($attemptobj->get_attemptid())) { + redirect($attemptobj->start_attempt_url(null, $page)); +} $displayoptions = $attemptobj->get_display_options(false); From ffe162d44d28ee96eb544cb5bf9bdbb0123091c5 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 7 Oct 2011 18:50:38 +0100 Subject: [PATCH 26/67] MDL-29627 quiz access move strings only used in one subplugin into that plugin. AMOS BEGIN MOV [youcannotwait,mod_quiz],[youcannotwait,quizaccess_delaybetweenattempts] MOV [youmustwait,mod_quiz],[youmustwait,quizaccess_delaybetweenattempts] MOV [subnetwrong,mod_quiz],[subnetwrong,quizaccess_ipaddress] MOV [attemptsallowedn,mod_quiz],[attemptsallowedn,quizaccess_numattempts] MOV [notavailable,mod_quiz],[notavailable,quizaccess_openclosedate] MOV [quiznotavailable,mod_quiz],[quiznotavailable,quizaccess_openclosedate] MOV [passworderror,mod_quiz],[passworderror,quizaccess_password] MOV [requirepasswordmessage,mod_quiz],[requirepasswordmessage,quizaccess_password] MOV [requiresafeexambrowser,mod_quiz],[requiresafeexambrowser,quizaccess_safebrowser] MOV [safebrowsererror,mod_quiz],[safebrowsererror,quizaccess_safebrowser] MOV [safebrowsernotice,mod_quiz],[safebrowsernotice,quizaccess_safebrowser] MOV [popupwithjavascriptsupport,mod_quiz],[popupwithjavascriptsupport,quizaccess_securewindow] MOV [quiztimelimit,mod_quiz],[quiztimelimit,quizaccess_timelimit] AMOS END --- mod/quiz/accessmanager_form.php | 2 +- .../en/quizaccess_delaybetweenattempts.php | 2 + .../accessrule/delaybetweenattempts/rule.php | 5 +- .../simpletest/testrule.php | 64 +++++++++---------- .../lang/en/quizaccess_ipaddress.php | 1 + mod/quiz/accessrule/ipaddress/rule.php | 2 +- .../lang/en/quizaccess_numattempts.php | 1 + mod/quiz/accessrule/numattempts/rule.php | 2 +- .../numattempts/simpletest/testrule.php | 3 +- .../lang/en/quizaccess_openclosedate.php | 2 + mod/quiz/accessrule/openclosedate/rule.php | 10 ++- .../openclosedate/simpletest/testrule.php | 12 ++-- .../password/lang/en/quizaccess_password.php | 4 +- mod/quiz/accessrule/password/rule.php | 9 +-- .../password/simpletest/testrule.php | 3 +- .../lang/en/quizaccess_safebrowser.php | 3 + mod/quiz/accessrule/safebrowser/rule.php | 7 +- .../safebrowser/simpletest/testrule.php | 6 +- .../lang/en/quizaccess_securewindow.php | 1 + mod/quiz/accessrule/securewindow/rule.php | 3 +- .../lang/en/quizaccess_timelimit.php | 1 + mod/quiz/accessrule/timelimit/rule.php | 3 +- .../timelimit/simpletest/testrule.php | 2 +- mod/quiz/lang/en/quiz.php | 13 ---- 24 files changed, 87 insertions(+), 74 deletions(-) diff --git a/mod/quiz/accessmanager_form.php b/mod/quiz/accessmanager_form.php index 0018791da5d5b..291068e947cc4 100644 --- a/mod/quiz/accessmanager_form.php +++ b/mod/quiz/accessmanager_form.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); -require_once ($CFG->libdir.'/formslib.php'); +require_once($CFG->libdir.'/formslib.php'); /** diff --git a/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php b/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php index ab749318b7995..8b9fdcdce98ec 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php +++ b/mod/quiz/accessrule/delaybetweenattempts/lang/en/quizaccess_delaybetweenattempts.php @@ -28,3 +28,5 @@ $string['pluginname'] = 'Delay between attempts quiz access rule'; +$string['youcannotwait'] = 'This quiz closes before you will be allowed to start another attempt.'; +$string['youmustwait'] = 'You must wait before you may re-attempt this quiz. You will be allowed to start another attempt after {$a}.'; diff --git a/mod/quiz/accessrule/delaybetweenattempts/rule.php b/mod/quiz/accessrule/delaybetweenattempts/rule.php index addd269cf4f24..947b91f2b69b1 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/rule.php +++ b/mod/quiz/accessrule/delaybetweenattempts/rule.php @@ -57,9 +57,10 @@ public function prevent_new_attempt($numprevattempts, $lastattempt) { $nextstarttime = $this->compute_next_start_time($numprevattempts, $lastattempt); if ($this->timenow < $nextstarttime) { if ($this->quiz->timeclose == 0 || $nextstarttime <= $this->quiz->timeclose) { - return get_string('youmustwait', 'quiz', userdate($nextstarttime)); + return get_string('youmustwait', 'quizaccess_delaybetweenattempts', + userdate($nextstarttime)); } else { - return get_string('youcannotwait', 'quiz'); + return get_string('youcannotwait', 'quizaccess_delaybetweenattempts'); } } return false; diff --git a/mod/quiz/accessrule/delaybetweenattempts/simpletest/testrule.php b/mod/quiz/accessrule/delaybetweenattempts/simpletest/testrule.php index dae92e8d47670..f7d7ec35fa188 100644 --- a/mod/quiz/accessrule/delaybetweenattempts/simpletest/testrule.php +++ b/mod/quiz/accessrule/delaybetweenattempts/simpletest/testrule.php @@ -59,14 +59,14 @@ public function test_just_first_delay() { $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $attempt->timefinish = 9000; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $attempt->timefinish = 9001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); } @@ -94,9 +94,9 @@ public function test_just_second_delay() { $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $attempt->timefinish = 9000; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); @@ -104,9 +104,9 @@ public function test_just_second_delay() { $attempt->timefinish = 9001; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); } public function test_just_both_delays() { @@ -132,32 +132,32 @@ public function test_just_both_delays() { $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(12000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(12000))); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $attempt->timefinish = 8000; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); $attempt->timefinish = 8001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(4, $attempt)); $attempt->timefinish = 9000; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); $attempt->timefinish = 9001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11001))); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); } public function test_with_close_date() { @@ -182,28 +182,28 @@ public function test_with_close_date() { $attempt->timefinish = 13000; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(15000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(15000))); $attempt->timefinish = 13001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youcannotwait', 'quiz')); + get_string('youcannotwait', 'quizaccess_delaybetweenattempts')); $attempt->timefinish = 14000; $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(15000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(15000))); $attempt->timefinish = 14001; $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youcannotwait', 'quiz')); + get_string('youcannotwait', 'quizaccess_delaybetweenattempts')); $rule = new quizaccess_delaybetweenattempts($quizobj, 15000); $attempt->timefinish = 13000; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); $attempt->timefinish = 13001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youcannotwait', 'quiz')); + get_string('youcannotwait', 'quizaccess_delaybetweenattempts')); $attempt->timefinish = 14000; $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $attempt->timefinish = 14001; $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youcannotwait', 'quiz')); + get_string('youcannotwait', 'quizaccess_delaybetweenattempts')); $rule = new quizaccess_delaybetweenattempts($quizobj, 15001); $attempt->timefinish = 13000; @@ -240,11 +240,11 @@ public function test_time_limit_and_overdue() { $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->prevent_new_attempt(5, $attempt)); $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(12000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(12000))); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertEqual($rule->prevent_new_attempt(3, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $attempt->timestart = 7950; $attempt->timefinish = 8000; $this->assertFalse($rule->prevent_new_attempt(1, $attempt)); @@ -253,36 +253,36 @@ public function test_time_limit_and_overdue() { $attempt->timestart = 7950; $attempt->timefinish = 8001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(4, $attempt)); $attempt->timestart = 8950; $attempt->timefinish = 9000; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); $attempt->timestart = 8950; $attempt->timefinish = 9001; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11001))); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $attempt->timestart = 8900; $attempt->timefinish = 9100; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11000))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11000))); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); $this->assertFalse($rule->prevent_new_attempt(3, $attempt)); $attempt->timestart = 8901; $attempt->timefinish = 9100; $this->assertEqual($rule->prevent_new_attempt(1, $attempt), - get_string('youmustwait', 'quiz', userdate(11001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(11001))); $this->assertEqual($rule->prevent_new_attempt(2, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); $this->assertEqual($rule->prevent_new_attempt(4, $attempt), - get_string('youmustwait', 'quiz', userdate(10001))); + get_string('youmustwait', 'quizaccess_delaybetweenattempts', userdate(10001))); } } diff --git a/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php b/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php index f56a768b89733..984b26632db06 100644 --- a/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php +++ b/mod/quiz/accessrule/ipaddress/lang/en/quizaccess_ipaddress.php @@ -28,3 +28,4 @@ $string['pluginname'] = 'IP address quiz access rule'; +$string['subnetwrong'] = 'This quiz is only accessible from certain locations, and this computer is not on the allowed list.'; diff --git a/mod/quiz/accessrule/ipaddress/rule.php b/mod/quiz/accessrule/ipaddress/rule.php index 0b88416a517b2..fa666a1e2e823 100644 --- a/mod/quiz/accessrule/ipaddress/rule.php +++ b/mod/quiz/accessrule/ipaddress/rule.php @@ -49,7 +49,7 @@ public function prevent_access() { if (address_in_subnet(getremoteaddr(), $this->quiz->subnet)) { return false; } else { - return get_string('subnetwrong', 'quiz'); + return get_string('subnetwrong', 'quizaccess_ipaddress'); } } } diff --git a/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php b/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php index 075af113b1698..b778ba03e7da6 100644 --- a/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php +++ b/mod/quiz/accessrule/numattempts/lang/en/quizaccess_numattempts.php @@ -27,4 +27,5 @@ defined('MOODLE_INTERNAL') || die(); +$string['attemptsallowedn'] = 'Attempts allowed: {$a}'; $string['pluginname'] = 'Number of attempts quiz access rule'; diff --git a/mod/quiz/accessrule/numattempts/rule.php b/mod/quiz/accessrule/numattempts/rule.php index fad046a2e1070..e69faf74bfb1c 100644 --- a/mod/quiz/accessrule/numattempts/rule.php +++ b/mod/quiz/accessrule/numattempts/rule.php @@ -47,7 +47,7 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { } public function description() { - return get_string('attemptsallowedn', 'quiz', $this->quiz->attempts); + return get_string('attemptsallowedn', 'quizaccess_numattempts', $this->quiz->attempts); } public function prevent_new_attempt($numprevattempts, $lastattempt) { diff --git a/mod/quiz/accessrule/numattempts/simpletest/testrule.php b/mod/quiz/accessrule/numattempts/simpletest/testrule.php index 9297731352ee2..27d66571418a2 100644 --- a/mod/quiz/accessrule/numattempts/simpletest/testrule.php +++ b/mod/quiz/accessrule/numattempts/simpletest/testrule.php @@ -48,7 +48,8 @@ public function test_num_attempts_access_rule() { $rule = new quizaccess_numattempts($quizobj, 0); $attempt = new stdClass(); - $this->assertEqual($rule->description(), get_string('attemptsallowedn', 'quiz', 3)); + $this->assertEqual($rule->description(), + get_string('attemptsallowedn', 'quizaccess_numattempts', 3)); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->prevent_new_attempt(2, $attempt)); diff --git a/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php b/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php index 4e2a9137fac00..a7bdcc322462b 100644 --- a/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php +++ b/mod/quiz/accessrule/openclosedate/lang/en/quizaccess_openclosedate.php @@ -27,4 +27,6 @@ defined('MOODLE_INTERNAL') || die(); +$string['notavailable'] = 'This quiz is not currently available'; $string['pluginname'] = 'Open and close date access rule'; +$string['quiznotavailable'] = 'The quiz will not be available until {$a}'; diff --git a/mod/quiz/accessrule/openclosedate/rule.php b/mod/quiz/accessrule/openclosedate/rule.php index 36b98ede6ce65..19519018bf825 100644 --- a/mod/quiz/accessrule/openclosedate/rule.php +++ b/mod/quiz/accessrule/openclosedate/rule.php @@ -45,9 +45,12 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { public function description() { $result = array(); if ($this->timenow < $this->quiz->timeopen) { - $result[] = get_string('quiznotavailable', 'quiz', userdate($this->quiz->timeopen)); + $result[] = get_string('quiznotavailable', 'quizaccess_openclosedate', + userdate($this->quiz->timeopen)); + } else if ($this->quiz->timeclose && $this->timenow > $this->quiz->timeclose) { - $result[] = get_string("quizclosed", "quiz", userdate($this->quiz->timeclose)); + $result[] = get_string('quizclosed', 'quiz', userdate($this->quiz->timeclose)); + } else { if ($this->quiz->timeopen) { $result[] = get_string('quizopenedon', 'quiz', userdate($this->quiz->timeopen)); @@ -56,13 +59,14 @@ public function description() { $result[] = get_string('quizcloseson', 'quiz', userdate($this->quiz->timeclose)); } } + return $result; } public function prevent_access() { if ($this->timenow < $this->quiz->timeopen || ($this->quiz->timeclose && $this->timenow > $this->quiz->timeclose)) { - return get_string('notavailable', 'quiz'); + return get_string('notavailable', 'quizaccess_openclosedate'); } return false; } diff --git a/mod/quiz/accessrule/openclosedate/simpletest/testrule.php b/mod/quiz/accessrule/openclosedate/simpletest/testrule.php index d5a14505ab072..6ccc150351535 100644 --- a/mod/quiz/accessrule/openclosedate/simpletest/testrule.php +++ b/mod/quiz/accessrule/openclosedate/simpletest/testrule.php @@ -78,9 +78,9 @@ public function test_start_date() { $rule = new quizaccess_openclosedate($quizobj, 9999); $this->assertEqual($rule->description(), - array(get_string('quiznotavailable', 'quiz', userdate(10000)))); + array(get_string('quiznotavailable', 'quizaccess_openclosedate', userdate(10000)))); $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); + get_string('notavailable', 'quizaccess_openclosedate')); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->is_finished(0, $attempt)); $this->assertFalse($rule->time_left($attempt, 0)); @@ -120,7 +120,7 @@ public function test_close_date() { $this->assertEqual($rule->description(), array(get_string('quizclosed', 'quiz', userdate(20000)))); $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); + get_string('notavailable', 'quizaccess_openclosedate')); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertTrue($rule->is_finished(0, $attempt)); $this->assertFalse($rule->time_left($attempt, 20000 - QUIZ_SHOW_TIME_BEFORE_DEADLINE)); @@ -142,9 +142,9 @@ public function test_both_dates() { $rule = new quizaccess_openclosedate($quizobj, 9999); $this->assertEqual($rule->description(), - array(get_string('quiznotavailable', 'quiz', userdate(10000)))); + array(get_string('quiznotavailable', 'quizaccess_openclosedate', userdate(10000)))); $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); + get_string('notavailable', 'quizaccess_openclosedate')); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->is_finished(0, $attempt)); @@ -168,7 +168,7 @@ public function test_both_dates() { $this->assertEqual($rule->description(), array(get_string('quizclosed', 'quiz', userdate(20000)))); $this->assertEqual($rule->prevent_access(), - get_string('notavailable', 'quiz')); + get_string('notavailable', 'quizaccess_openclosedate')); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertTrue($rule->is_finished(0, $attempt)); diff --git a/mod/quiz/accessrule/password/lang/en/quizaccess_password.php b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php index 132e83315498e..1672114438130 100644 --- a/mod/quiz/accessrule/password/lang/en/quizaccess_password.php +++ b/mod/quiz/accessrule/password/lang/en/quizaccess_password.php @@ -26,5 +26,7 @@ defined('MOODLE_INTERNAL') || die(); +$string['passworderror'] = 'The password entered was incorrect'; $string['pluginname'] = 'Password quiz access rule'; -$string['quizpassword'] = 'Quiz password'; \ No newline at end of file +$string['quizpassword'] = 'Quiz password'; +$string['requirepasswordmessage'] = 'To attempt this quiz you need to know the quiz password'; diff --git a/mod/quiz/accessrule/password/rule.php b/mod/quiz/accessrule/password/rule.php index 571414451216b..369012d536d99 100644 --- a/mod/quiz/accessrule/password/rule.php +++ b/mod/quiz/accessrule/password/rule.php @@ -46,7 +46,7 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { } public function description() { - return get_string('requirepasswordmessage', 'quiz'); + return get_string('requirepasswordmessage', 'quizaccess_password'); } public function is_preflight_check_required($attemptid) { @@ -59,11 +59,12 @@ public function add_preflight_check_form_fields(mod_quiz_preflight_check_form $q $mform->addElement('header', 'passwordheader', get_string('password')); $mform->addElement('static', 'passwordmessage', '', - get_string('requirepasswordmessage', 'quiz')); + get_string('requirepasswordmessage', 'quizaccess_password')); // don't use the 'proper' field name of 'password' since that get's // Firefox's password auto-complete over-excited. - $mform->addElement('password', 'quizpassword', get_string('quizpassword', 'quizaccess_password')); + $mform->addElement('password', 'quizpassword', + get_string('quizpassword', 'quizaccess_password')); } public function validate_preflight_check($data, $files, $errors, $attemptid) { @@ -81,7 +82,7 @@ public function validate_preflight_check($data, $files, $errors, $attemptid) { } } - $errors['quizpassword'] = get_string('passworderror', 'quiz'); + $errors['quizpassword'] = get_string('passworderror', 'quizaccess_password'); return $errors; } diff --git a/mod/quiz/accessrule/password/simpletest/testrule.php b/mod/quiz/accessrule/password/simpletest/testrule.php index 6fb0e12a59b92..b3d1e56de2dd2 100644 --- a/mod/quiz/accessrule/password/simpletest/testrule.php +++ b/mod/quiz/accessrule/password/simpletest/testrule.php @@ -49,7 +49,8 @@ public function test_password_access_rule() { $attempt = new stdClass(); $this->assertFalse($rule->prevent_access()); - $this->assertEqual($rule->description(), get_string('requirepasswordmessage', 'quiz')); + $this->assertEqual($rule->description(), + get_string('requirepasswordmessage', 'quizaccess_password')); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->is_finished(0, $attempt)); $this->assertFalse($rule->time_left($attempt, 1)); diff --git a/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php b/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php index 0b91c9747d695..0847d5fd274c5 100644 --- a/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php +++ b/mod/quiz/accessrule/safebrowser/lang/en/quizaccess_safebrowser.php @@ -28,3 +28,6 @@ $string['pluginname'] = 'Safe Exam Browser quiz access rule'; +$string['requiresafeexambrowser'] = 'Require the use of Safe Exam Browser'; +$string['safebrowsererror'] = 'This quiz has been set up so that it may only be attempted using the Safe Exam Browser. You cannot attempt it from this web browser.'; +$string['safebrowsernotice'] = 'This quiz has been configured so that students may only attempt it using the Safe Exam Browser.'; diff --git a/mod/quiz/accessrule/safebrowser/rule.php b/mod/quiz/accessrule/safebrowser/rule.php index e0d11e2274ccf..e73c5b83412b4 100644 --- a/mod/quiz/accessrule/safebrowser/rule.php +++ b/mod/quiz/accessrule/safebrowser/rule.php @@ -48,14 +48,14 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { public function prevent_access() { if (!$this->check_safe_browser()) { - return get_string('safebrowsererror', 'quiz'); + return get_string('safebrowsererror', 'quizaccess_safebrowser'); } else { return false; } } public function description() { - return get_string('safebrowsernotice', 'quiz'); + return get_string('safebrowsernotice', 'quizaccess_safebrowser'); } public function setup_attempt_page($page) { @@ -83,6 +83,7 @@ public static function get_browser_security_choices() { return array(); } - return array('safebrowser' => get_string('requiresafeexambrowser', 'quiz')); + return array('safebrowser' => + get_string('requiresafeexambrowser', 'quizaccess_safebrowser')); } } diff --git a/mod/quiz/accessrule/safebrowser/simpletest/testrule.php b/mod/quiz/accessrule/safebrowser/simpletest/testrule.php index 5baab3015f385..a7266f4ff0637 100644 --- a/mod/quiz/accessrule/safebrowser/simpletest/testrule.php +++ b/mod/quiz/accessrule/safebrowser/simpletest/testrule.php @@ -50,9 +50,11 @@ public function test_safebrowser_access_rule() { $attempt = new stdClass(); // This next test assumes the unit tests are not being run using Safe Exam Browser! - $this->assertEqual(get_string('safebrowsererror', 'quiz'), $rule->prevent_access()); + $this->assertEqual(get_string('safebrowsererror', 'quizaccess_safebrowser'), + $rule->prevent_access()); - $this->assertEqual(get_string('safebrowsernotice', 'quiz'), $rule->description()); + $this->assertEqual(get_string('safebrowsernotice', 'quizaccess_safebrowser'), + $rule->description()); $this->assertFalse($rule->prevent_new_attempt(0, $attempt)); $this->assertFalse($rule->is_finished(0, $attempt)); $this->assertFalse($rule->time_left($attempt, 1)); diff --git a/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php b/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php index 9fa8292acc7dd..74897c91953c2 100644 --- a/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php +++ b/mod/quiz/accessrule/securewindow/lang/en/quizaccess_securewindow.php @@ -28,3 +28,4 @@ $string['pluginname'] = 'JavaScript security quiz access rule'; +$string['popupwithjavascriptsupport'] = 'Full screen pop-up with some JavaScript security'; diff --git a/mod/quiz/accessrule/securewindow/rule.php b/mod/quiz/accessrule/securewindow/rule.php index 7e471eff3a862..fb5ae669ebc75 100644 --- a/mod/quiz/accessrule/securewindow/rule.php +++ b/mod/quiz/accessrule/securewindow/rule.php @@ -89,6 +89,7 @@ public function setup_attempt_page($page) { * security settings menu. */ public static function get_browser_security_choices() { - return array('securewindow' => get_string('popupwithjavascriptsupport', 'quiz')); + return array('securewindow' => + get_string('popupwithjavascriptsupport', 'quizaccess_securewindow')); } } diff --git a/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php b/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php index 1d030905cde1d..f0bee8bff4c9c 100644 --- a/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php +++ b/mod/quiz/accessrule/timelimit/lang/en/quizaccess_timelimit.php @@ -28,3 +28,4 @@ $string['pluginname'] = 'Time limit quiz access rule'; +$string['quiztimelimit'] = 'Time limit: {$a}'; diff --git a/mod/quiz/accessrule/timelimit/rule.php b/mod/quiz/accessrule/timelimit/rule.php index 7d274f9931710..eff0dbbaf082d 100644 --- a/mod/quiz/accessrule/timelimit/rule.php +++ b/mod/quiz/accessrule/timelimit/rule.php @@ -48,7 +48,8 @@ public static function make(quiz $quizobj, $timenow, $canignoretimelimits) { } public function description() { - return get_string('quiztimelimit', 'quiz', format_time($this->quiz->timelimit)); + return get_string('quiztimelimit', 'quizaccess_timelimit', + format_time($this->quiz->timelimit)); } public function time_left($attempt, $timenow) { diff --git a/mod/quiz/accessrule/timelimit/simpletest/testrule.php b/mod/quiz/accessrule/timelimit/simpletest/testrule.php index eb1332a8e87d5..f9c2d23e1e266 100644 --- a/mod/quiz/accessrule/timelimit/simpletest/testrule.php +++ b/mod/quiz/accessrule/timelimit/simpletest/testrule.php @@ -49,7 +49,7 @@ public function test_time_limit_access_rule() { $attempt = new stdClass(); $this->assertEqual($rule->description(), - get_string('quiztimelimit', 'quiz', format_time(3600))); + get_string('quiztimelimit', 'quizaccess_timelimit', format_time(3600))); $attempt->timestart = 10000; $this->assertEqual($rule->time_left($attempt, 10000), 3600); diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index a155ee74bc5ff..119a3926e8fb8 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -87,7 +87,6 @@ $string['attemptquiznow'] = 'Attempt quiz now'; $string['attempts'] = 'Attempts'; $string['attemptsallowed'] = 'Attempts allowed'; -$string['attemptsallowedn'] = 'Attempts allowed: {$a}'; $string['attemptsdeleted'] = 'Quiz attempts deleted'; $string['attemptselection'] = 'Select which attempts to analyze per user:'; $string['attemptsexist'] = 'You can no longer add or remove questions.'; @@ -447,7 +446,6 @@ $string['noreviewuntil'] = 'You are not allowed to review this quiz until {$a}'; $string['noreviewuntilshort'] = 'Available {$a}'; $string['noscript'] = 'JavaScript must be enabled to continue!'; -$string['notavailable'] = 'This quiz is not currently available'; $string['notavailabletostudents'] = 'Note: This quiz is not currently available to your students'; $string['notenoughrandomquestions'] = 'There are not enough questions in category {$a->category} to create the question {$a->name} ({$a->id}).'; $string['notenoughsubquestions'] = 'Not enough sub-questions have been defined!
    Do you want to go back and fix this question?'; @@ -495,7 +493,6 @@ $string['parentcategory'] = 'Parent category'; $string['parsingquestions'] = 'Parsing questions from import file.'; $string['partiallycorrect'] = 'Partially correct'; -$string['passworderror'] = 'The password entered was incorrect'; $string['penalty'] = 'Penalty'; $string['penaltyscheme'] = 'Apply penalties'; $string['penaltyscheme_help'] = 'If enabled, a penalty is subtracted from the final mark for a question for a wrong response. The amount of penalty is specified in the question settings. This setting only applies if adaptive mode is enabled.'; @@ -506,7 +503,6 @@ $string['popup'] = 'Show quiz in a "secure" window'; $string['popupblockerwarning'] = 'This section of the test is in secure mode, this means that you need to take the quiz in a secure window. Please turn off your popup blocker. Thank you.'; $string['popupnotice'] = 'Students will see this quiz in a secure window'; -$string['popupwithjavascriptsupport'] = 'Full screen pop-up with some JavaScript security'; $string['preprocesserror'] = 'Error occurred during pre-processing!'; $string['preview'] = 'Preview'; $string['previewquestion'] = 'Preview question'; @@ -561,7 +557,6 @@ $string['quiz:manage'] = 'Manage quizzes'; $string['quiz:manageoverrides'] = 'Manage quiz overrides'; $string['quiznavigation'] = 'Quiz navigation'; -$string['quiznotavailable'] = 'The quiz will not be available until {$a}'; $string['quizopen'] = 'Open the quiz'; $string['quizopenclose'] = 'Open and close dates'; $string['quizopenclose_help'] = 'Students can only start their attempt(s) after the open time and they must complete their attempts before the close time.'; @@ -575,7 +570,6 @@ $string['quiz:regrade'] = 'Regrade quiz attempts'; $string['quiz:reviewmyattempts'] = 'Review your own attempts'; $string['quizsettings'] = 'Quiz settings'; -$string['quiztimelimit'] = 'Time limit: {$a}'; $string['quiz:view'] = 'View quiz information'; $string['quiz:viewreports'] = 'View quiz reports'; $string['quiztimer'] = 'Quiz Timer'; @@ -625,8 +619,6 @@ $string['reportsimplestat'] = 'Simple statistics'; $string['requirepassword'] = 'Require password'; $string['requirepassword_help'] = 'If a password is specified, a student must enter it in order to attempt the quiz.'; -$string['requirepasswordmessage'] = 'To attempt this quiz you need to know the quiz password'; -$string['requiresafeexambrowser'] = 'Require the use of Safe Exam Browser'; $string['requiresubnet'] = 'Require network address'; $string['requiresubnet_help'] = 'Quiz access may be restricted to particular subnets on the LAN or Internet by specifying a comma-separated list of partial or full IP address numbers. This can be useful for an invigilated (proctored) quiz, to ensure that only people in a certain location can access the quiz.'; $string['response'] = 'Response'; @@ -656,8 +648,6 @@ $string['reviewthisattempt'] = 'Review your responses to this attempt'; $string['rqp'] = 'Remote question'; $string['rqps'] = 'Remote questions'; -$string['safebrowsererror'] = 'This quiz has been set up so that it may only be attempted using the Safe Exam Browser. You cannot attempt it from this web browser.'; -$string['safebrowsernotice'] = 'This quiz has been configured so that students may only attempt it using the Safe Exam Browser.'; $string['sameasoverall'] = 'Same as for overall grades'; $string['save'] = 'Save'; $string['saveandedit'] = 'Save changes and edit questions'; @@ -728,7 +718,6 @@ $string['submitallandfinish'] = 'Submit all and finish'; $string['subneterror'] = 'Sorry, this quiz has been locked so that it is only accessible from certain locations. Currently your computer is not one of those allowed to use this quiz.'; $string['subnetnotice'] = 'This quiz has been locked so that it is only accessible from certain locations. Your computer is not on an allowed subnet. As teacher you are allowed to preview anyway.'; -$string['subnetwrong'] = 'This quiz is only accessible from certain locations, and this computer is not on the allowed list.'; $string['subplugintype_quiz'] = 'Report'; $string['subplugintype_quiz_plural'] = 'Reports'; $string['substitutedby'] = 'will be substituted by'; @@ -784,7 +773,5 @@ $string['withsummary'] = 'with summary statistics'; $string['wronguse'] = 'You can not use this page like that'; $string['xhtml'] = 'XHTML'; -$string['youcannotwait'] = 'This quiz closes before you will be allowed to start another attempt.'; -$string['youmustwait'] = 'You must wait before you may re-attempt this quiz. You will be allowed to start another attempt after {$a}.'; $string['youneedtoenrol'] = 'You need to enrol in this course before you can attempt this quiz'; $string['yourfinalgradeis'] = 'Your final grade for this quiz is {$a}.'; From 5adace99f8e67226d9e70332b823403926a01d5d Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 10 Oct 2011 17:16:31 +0100 Subject: [PATCH 27/67] MDL-29627 quiz access rules: backup and restore of data. --- ...backup_mod_quiz_access_subplugin.class.php | 56 +++++++++++++++++++ .../backup/moodle2/backup_quiz_stepslib.php | 6 ++ ...estore_mod_quiz_access_subplugin.class.php | 54 ++++++++++++++++++ .../backup/moodle2/restore_quiz_stepslib.php | 12 +++- 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 mod/quiz/backup/moodle2/backup_mod_quiz_access_subplugin.class.php create mode 100644 mod/quiz/backup/moodle2/restore_mod_quiz_access_subplugin.class.php diff --git a/mod/quiz/backup/moodle2/backup_mod_quiz_access_subplugin.class.php b/mod/quiz/backup/moodle2/backup_mod_quiz_access_subplugin.class.php new file mode 100644 index 0000000000000..5143157004fb8 --- /dev/null +++ b/mod/quiz/backup/moodle2/backup_mod_quiz_access_subplugin.class.php @@ -0,0 +1,56 @@ +. + +/** + * Defines the base class for quiz access plugins backup code. + * + * @package mod + * @subpackage quiz + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Base class for backing up all the quiz settings and attempt data for an + * access rule quiz sub-plugin. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class backup_mod_quiz_access_subplugin extends backup_subplugin { + + /** + * Use this method to describe the XML structure required to store your + * sub-plugin's settings for a particular quiz, and how that data is stored + * in the database. + */ + protected function define_quiz_subplugin_structure() { + // Do nothing by default. + } + + /** + * Use this method to describe the XML structure required to store your + * sub-plugin's settings for a particular quiz attempt, and how that data + * is stored in the database. + */ + protected function define_attempt_subplugin_structure() { + // Do nothing by default. + } +} diff --git a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php index 1e512974e8a15..a8c9e541232ad 100644 --- a/mod/quiz/backup/moodle2/backup_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/backup_quiz_stepslib.php @@ -52,6 +52,9 @@ protected function define_structure() { 'browsersecurity', 'delay1', 'delay2', 'showuserpicture', 'showblocks')); + // Define elements for access rule subplugin settings. + $this->add_subplugin_structure('quizaccess', $quiz, true); + $qinstances = new backup_nested_element('question_instances'); $qinstance = new backup_nested_element('question_instance', array('id'), array( @@ -84,6 +87,9 @@ protected function define_structure() { // attaching them to the $attempt element based in 'uniqueid' matching $this->add_question_usages($attempt, 'uniqueid'); + // Define elements for access rule subplugin attempt data. + $this->add_subplugin_structure('quizaccess', $attempt, true); + // Build the tree $quiz->add_child($qinstances); diff --git a/mod/quiz/backup/moodle2/restore_mod_quiz_access_subplugin.class.php b/mod/quiz/backup/moodle2/restore_mod_quiz_access_subplugin.class.php new file mode 100644 index 0000000000000..d138ba4a334ee --- /dev/null +++ b/mod/quiz/backup/moodle2/restore_mod_quiz_access_subplugin.class.php @@ -0,0 +1,54 @@ +. + +/** + * Restore code for the quizaccess_honestycheck plugin. + * + * @package mod + * @subpackage quiz + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Base class for restoring up all the quiz settings and attempt data for an + * access rule quiz sub-plugin. + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_mod_quiz_access_subplugin extends restore_subplugin { + + /** + * Use this method to describe the XML paths that store your sub-plugin's + * settings for a particular quiz. + */ + protected function define_quiz_subplugin_structure() { + // Do nothing by default. + } + + /** + * Use this method to describe the XML paths that store your sub-plugin's + * settings for a particular quiz attempt. + */ + protected function define_attempt_subplugin_structure() { + // Do nothing by default. + } +} diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index 90756fe1674fe..c8d9d8be70eef 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -38,11 +38,17 @@ protected function define_structure() { $paths = array(); $userinfo = $this->get_setting_value('userinfo'); - $paths[] = new restore_path_element('quiz', '/activity/quiz'); + $quiz = new restore_path_element('quiz', '/activity/quiz'); + $paths[] = $quiz; + + // A chance for access subplugings to set up their quiz data. + $this->add_subplugin_structure('quizaccess', $quiz); + $paths[] = new restore_path_element('quiz_question_instance', '/activity/quiz/question_instances/question_instance'); $paths[] = new restore_path_element('quiz_feedback', '/activity/quiz/feedbacks/feedback'); $paths[] = new restore_path_element('quiz_override', '/activity/quiz/overrides/override'); + if ($userinfo) { $paths[] = new restore_path_element('quiz_grade', '/activity/quiz/grades/grade'); @@ -52,9 +58,13 @@ protected function define_structure() { $quizattempt = new restore_path_element('quiz_attempt', '/activity/quiz/attempts/attempt'); $paths[] = $quizattempt; + // Add states and sessions $this->add_question_usages($quizattempt, $paths); + // A chance for access subplugings to set up their attempt data. + $this->add_subplugin_structure('quizaccess', $quizattempt); + } else { // Restoring from a version 2.0.x+ or earlier. // Upgrade the legacy attempt data. From c999d841b4d52474df84c3d5c39646f4dbd727de Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Wed, 12 Oct 2011 17:57:29 +0100 Subject: [PATCH 28/67] MDL-29627 add quizaccess plugins to standard_plugins_list. --- lib/pluginlib.php | 5 +++++ mod/quiz/lang/en/quiz.php | 2 ++ 2 files changed, 7 insertions(+) diff --git a/lib/pluginlib.php b/lib/pluginlib.php index 9cdc83a4f04cd..d5d5996cb5777 100644 --- a/lib/pluginlib.php +++ b/lib/pluginlib.php @@ -340,6 +340,11 @@ public static function standard_plugins_list($type) { 'grading', 'overview', 'responses', 'statistics' ), + 'quizaccess' => array( + 'delaybetweenattempts', 'ipaddress', 'numattempts', 'openclosedate', + 'password', 'safebrowser', 'securewindow', 'timelimit' + ), + 'report' => array( 'backups', 'configlog', 'courseoverview', 'log', 'questioninstances', 'security', 'stats' diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 119a3926e8fb8..c33daa9f2e671 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -720,6 +720,8 @@ $string['subnetnotice'] = 'This quiz has been locked so that it is only accessible from certain locations. Your computer is not on an allowed subnet. As teacher you are allowed to preview anyway.'; $string['subplugintype_quiz'] = 'Report'; $string['subplugintype_quiz_plural'] = 'Reports'; +$string['subplugintype_quizaccess'] = 'Access rule'; +$string['subplugintype_quizaccess_plural'] = 'Access rules'; $string['substitutedby'] = 'will be substituted by'; $string['summaryofattempt'] = 'Summary of attempt'; $string['summaryofattempts'] = 'Summary of your previous attempts'; From 2e98ae02ec2af3ab2312e2cc524c70c8d5ba6ad6 Mon Sep 17 00:00:00 2001 From: sam marshall Date: Thu, 13 Oct 2011 12:57:55 +0100 Subject: [PATCH 29/67] MDL-29768 debugstringids option did not do anything --- lang/en/admin.php | 2 +- lib/moodlelib.php | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lang/en/admin.php b/lang/en/admin.php index 03c0ed706c83e..3a0ce0be08459 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -168,7 +168,6 @@ $string['configdebugdisplay'] = 'Set to on, the error reporting will go to the HTML page. This is practical, but breaks XHTML, JS, cookies and HTTP headers in general. Set to off, it will send the output to your server logs, allowing better debugging. The PHP setting error_log controls which log this goes to.'; $string['configdebugpageinfo'] = 'Enable if you want page information printed in page footer.'; $string['configdebugsmtp'] = 'Enable verbose debug information during sending of email messages to SMTP server.'; -$string['configdebugstringids'] = 'This option is designed to help translators. It shows the language file and string id beside each string that is output. (Changing this setting will only take effect on the next page load.)'; $string['configdebugvalidators'] = 'Enable if you want to have links to external validator servers in page footer. You may need to create new user with username w3cvalidator, and enable guest access. These changes may allow unauthorized access to server, do not enable on production sites!'; $string['configdefaultallowedmodules'] = 'For the courses which fall into the above category, which modules do you want to allow by default when the course is created?'; $string['configdefaulthomepage'] = 'This determines the home page for logged in users'; @@ -403,6 +402,7 @@ $string['debugpageinfo'] = 'Show page information'; $string['debugsmtp'] = 'Debug email sending'; $string['debugstringids'] = 'Show origin of languages strings'; +$string['debugstringids_desc'] = 'This option is designed to help translators. When this option is enabled, if you add the parameter strings=1 to a request URL, it will show the language file and string id beside each string that is output.'; $string['debugvalidators'] = 'Show validator links'; $string['defaultallowedmodules'] = 'Default allowed modules'; $string['defaultcity'] = 'Default city'; diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 5f5e9350ed7a9..f993bab402099 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -6695,6 +6695,7 @@ public function reset_caches() {} * @return string The localized string. */ function get_string($identifier, $component = '', $a = NULL) { + global $CFG; $identifier = clean_param($identifier, PARAM_STRINGID); if (empty($identifier)) { @@ -6730,7 +6731,13 @@ function get_string($identifier, $component = '', $a = NULL) { } } - return get_string_manager()->get_string($identifier, $component, $a); + $result = get_string_manager()->get_string($identifier, $component, $a); + + // Debugging feature lets you display string identifier and component + if ($CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) { + $result .= ' {' . $identifier . '/' . $component . '}'; + } + return $result; } /** From 1f186372eac98014a3fa282ac4718c0bcc5e51e0 Mon Sep 17 00:00:00 2001 From: Tony Levi Date: Tue, 16 Aug 2011 12:00:29 +0930 Subject: [PATCH 30/67] MDL-28945: Fix course/activity completion reports initial selection broken --- course/report/completion/index.php | 25 ++++++++++++++++++------- course/report/progress/index.php | 25 ++++++++++++++++++------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/course/report/completion/index.php b/course/report/completion/index.php index 5936ed3a212ff..0118cf1b16c93 100644 --- a/course/report/completion/index.php +++ b/course/report/completion/index.php @@ -241,6 +241,9 @@ function csv_quote($value) { foreach ($initials as $initial) { $var = 'si'.$initial; + $othervar = $initial == 'first' ? 'silast' : 'sifirst'; + $othervar = $$othervar != 'all' ? "&{$othervar}={$$othervar}" : ''; + $pagingbar .= '
    '; $pagingbar .= get_string($initial.'name').': '; @@ -248,7 +251,7 @@ function csv_quote($value) { $pagingbar .= ''.get_string('all').' '; } else { - $pagingbar .= ''.get_string('all').' '; + $pagingbar .= "".get_string('all').' '; } foreach ($alphabet as $letter) { @@ -256,7 +259,7 @@ function csv_quote($value) { $pagingbar .= ''.$letter.' '; } else { - $pagingbar .= ''.$letter.' '; + $pagingbar .= "$letter "; } } @@ -270,10 +273,15 @@ function csv_quote($value) { $pagingbar .= '
    '; $pagingbar .= get_string('page').': '; + $sistrings = array(); + $sistrings[] = $sifirst != 'all' ? "sifirst={$sifirst}" : null; + $sistrings[] = $silast != 'all' ? "silast={$silast}" : null; + $sistring = !empty($sistrings) ? implode('&', $sistrings) : ''; + // Display previous link if ($start > 0) { $pstart = max($start - COMPLETION_REPORT_PAGE, 0); - $pagingbar .= '() '; + $pagingbar .= "(".get_string('previous').') '; } // Create page links @@ -286,7 +294,7 @@ function csv_quote($value) { $pagingbar .= ' '.$curpage.' '; } else { - $pagingbar .= ' '.$curpage.' '; + $pagingbar .= " $curpage "; } $curstart += COMPLETION_REPORT_PAGE; @@ -295,7 +303,7 @@ function csv_quote($value) { // Display next link $nstart = $start + COMPLETION_REPORT_PAGE; if ($nstart < $total) { - $pagingbar .= ' ()'; + $pagingbar .= " (".get_string('next').')'; } $pagingbar .= '
    '; @@ -441,12 +449,15 @@ function csv_quote($value) { // User heading / sort option print ''; + + $sistring = "&silast={$silast}&sifirst={$sifirst}"; + if($firstnamesort) { print - get_string('firstname').' / '. + get_string('firstname')." / id}{$sistring}\">". get_string('lastname').''; } else { - print ''. + print "id}&sort=firstname{$sistring}\">". get_string('firstname').' / '. get_string('lastname'); } diff --git a/course/report/progress/index.php b/course/report/progress/index.php index b205c0bd46b59..a45a6439c7123 100644 --- a/course/report/progress/index.php +++ b/course/report/progress/index.php @@ -173,6 +173,9 @@ function csv_quote($value) { foreach ($initials as $initial) { $var = 'si'.$initial; + $othervar = $initial == 'first' ? 'silast' : 'sifirst'; + $othervar = $$othervar != 'all' ? "&{$othervar}={$$othervar}" : ''; + $pagingbar .= '
    '; $pagingbar .= get_string($initial.'name').': '; @@ -180,7 +183,7 @@ function csv_quote($value) { $pagingbar .= ''.get_string('all').' '; } else { - $pagingbar .= ''.get_string('all').' '; + $pagingbar .= "".get_string('all').' '; } foreach ($alphabet as $letter) { @@ -188,7 +191,7 @@ function csv_quote($value) { $pagingbar .= ''.$letter.' '; } else { - $pagingbar .= ''.$letter.' '; + $pagingbar .= "$letter "; } } @@ -202,10 +205,15 @@ function csv_quote($value) { $pagingbar .= '
    '; $pagingbar .= get_string('page').': '; + $sistrings = array(); + $sistrings[] = $sifirst != 'all' ? "sifirst={$sifirst}" : null; + $sistrings[] = $silast != 'all' ? "silast={$silast}" : null; + $sistring = !empty($sistrings) ? implode('&', $sistrings) : ''; + // Display previous link if ($start > 0) { $pstart = max($start - COMPLETION_REPORT_PAGE, 0); - $pagingbar .= '() '; + $pagingbar .= "(".get_string('previous').') '; } // Create page links @@ -218,7 +226,7 @@ function csv_quote($value) { $pagingbar .= ' '.$curpage.' '; } else { - $pagingbar .= ' '.$curpage.' '; + $pagingbar .= " $curpage "; } $curstart += COMPLETION_REPORT_PAGE; @@ -227,7 +235,7 @@ function csv_quote($value) { // Display next link $nstart = $start + COMPLETION_REPORT_PAGE; if ($nstart < $total) { - $pagingbar .= ' ()'; + $pagingbar .= " (".get_string('next').')'; } $pagingbar .= '
    '; @@ -251,12 +259,15 @@ function csv_quote($value) { // User heading / sort option print ''; + + $sistring = "&silast={$silast}&sifirst={$sifirst}"; + if($firstnamesort) { print - get_string('firstname').' / '. + get_string('firstname')." / id}{$sistring}\">". get_string('lastname').''; } else { - print ''. + print "id}&sort=firstname{$sistring}\">". get_string('firstname').' / '. get_string('lastname'); } From 49bfc8be290f05be3ea579aaaa31820c43a536d1 Mon Sep 17 00:00:00 2001 From: AMOS bot Date: Fri, 14 Oct 2011 00:55:22 +0000 Subject: [PATCH 31/67] Automatically generated installer lang files --- install/lang/ru/install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/lang/ru/install.php b/install/lang/ru/install.php index f8e1c9e0fa0b6..545d48711cad9 100644 --- a/install/lang/ru/install.php +++ b/install/lang/ru/install.php @@ -46,7 +46,7 @@ ПожалуйÑта, ÑвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором Ñервера, еÑли не знаете, как уÑтановить новую верÑию или включить раÑÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ PHP.'; $string['errorsinenvironment'] = 'Проверка Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ð½Ðµ выполнена!'; $string['installation'] = 'УÑтановка'; -$string['langdownloaderror'] = 'К неÑчаÑтью, не удалоÑÑŒ уÑтановить Ñзык "{$a}". ПроцеÑÑ ÑƒÑтановки продолжитÑÑ Ð½Ð° английÑком.'; +$string['langdownloaderror'] = 'К Ñожалению, не удалоÑÑŒ уÑтановить Ñзык "{$a}". ПроцеÑÑ ÑƒÑтановки продолжитÑÑ Ð½Ð° английÑком.'; $string['memorylimithelp'] = '

    Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ памÑти в PHP на Вашем Ñервере уÑтановлено в {$a}.

    Из-за Ñтого у через какое-то Ð²Ñ€ÐµÐ¼Ñ Ñƒ Moodle могут возникнуть проблемы Ñ Ð¿Ð°Ð¼Ñтью, оÑобенно еÑли у Ð’Ð°Ñ Ð±ÑƒÐ´ÐµÑ‚ много модулей и/или пользователей.

    From d4bb76264391c21c2de109fdfc4feaefc20b6a6b Mon Sep 17 00:00:00 2001 From: Mary Evans Date: Fri, 14 Oct 2011 02:28:03 +0100 Subject: [PATCH 32/67] MDL-26953 FIX for side-post-only layout --- theme/nonzero/layout/general.php | 13 +++++-- theme/nonzero/style/pagelayout.css | 61 ++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/theme/nonzero/layout/general.php b/theme/nonzero/layout/general.php index 988a177d60cf3..d5aeff59f965a 100644 --- a/theme/nonzero/layout/general.php +++ b/theme/nonzero/layout/general.php @@ -3,28 +3,35 @@ $hasheading = ($PAGE->heading); $hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()); $hasfooter = (empty($PAGE->layout_options['nofooter'])); + $hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT); $hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT); + +$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT)); +$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT)); + $custommenu = $OUTPUT->custom_menu(); $hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu)); $bodyclasses = array(); -if ($hassidepre && !$hassidepost) { +if ($showsidepre && !$showsidepost) { $bodyclasses[] = 'side-pre-only'; -} else if ($hassidepost && !$hassidepre) { +} else if ($showsidepost && !$showsidepre) { $bodyclasses[] = 'side-post-only'; -} else if (!$hassidepost && !$hassidepre) { +} else if (!$showsidepost && !$showsidepre) { $bodyclasses[] = 'content-only'; } if ($hascustommenu) { $bodyclasses[] = 'has_custom_menu'; } + echo $OUTPUT->doctype() ?> htmlattributes() ?>> <?php echo $PAGE->title ?> + standard_head_html() ?> diff --git a/theme/nonzero/style/pagelayout.css b/theme/nonzero/style/pagelayout.css index b3527524e8cdf..83988b637f4f9 100644 --- a/theme/nonzero/style/pagelayout.css +++ b/theme/nonzero/style/pagelayout.css @@ -10,7 +10,7 @@ body {margin:auto 0px;width:auto;} #page-content #region-main-box { float: left; - margin-left: -[[setting:regionpostwidth]];; + margin-left: -[[setting:regionpostwidth]]; position: relative; width: 200%; right: 100%; @@ -18,7 +18,7 @@ body {margin:auto 0px;width:auto;} #page-content #region-post-box { float: left; - margin-left: -[[setting:regionprewidth]];; + margin-left: -[[setting:regionprewidth]]; width: 100%; } @@ -67,7 +67,7 @@ body {margin:auto 0px;width:auto;} /** Only side pre **/ .side-pre-only #page-content #region-main-box { - margin-left: 0px; + margin-left: 0; } .side-pre-only #page-content #region-post-box { @@ -80,16 +80,17 @@ body {margin:auto 0px;width:auto;} .side-pre-only #page-content #region-pre { left: [[setting:regionprewidth]]; - width: [[setting:regionprewidth]]; + width: [[setting:regionprewidth]]; } .side-pre-only #page-content #region-post { - width: 0%; + left: 0; + width: 0; } /** Only side post **/ .side-post-only #page-content #region-main-box { - margin-left: 0px; + margin-left: 0; } @@ -99,11 +100,14 @@ body {margin:auto 0px;width:auto;} .side-post-only #page-content #region-main { margin-left: [[setting:regionpostwidth]]; - } +.side-post-only #page-content #region-pre { + left: 0; + width: 0; +} .side-post-only #page-content #region-post { - left: [[setting:regionsumwidth]]; + left: [[setting:regionpostwidth]]; width: [[setting:regionpostwidth]]; } @@ -111,6 +115,47 @@ body {margin:auto 0px;width:auto;} margin-left: [[setting:regionprewidth]]; } +.blocks-moving.side-post-only #page-content #region-main-box { + float: left; + margin-left: -[[setting:regionpostwidth]]; + position: relative; + width: 200%; + right: 100%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box { + float: left; + margin-left: + -[[setting:regionprewidth]]; + width: 100%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap { + float: left; + width: 50%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { + overflow: hidden; + position: relative; + margin-left: [[setting:regionsumwidth]]; + left: 100%; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre { + float: right; + position: relative; + left: [[setting:leftregionwidthmargin]]; + width: [[setting:regionprewidth]]; +} + +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post { + float: right; + position: relative; + left: [[setting:regiondoublepresumwidth]]; + width: [[setting:regionpostwidth]]; +} + /** No blocks whatsoever **/ .content-only #page-content #region-main-box { margin-left: 0px; From 694f3b74c7def644bc837f0df724dcdc0c456b85 Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Fri, 14 Oct 2011 15:30:54 +0200 Subject: [PATCH 33/67] MDL-29773 use db debuginfo when file record insertion fails --- lib/filestorage/file_storage.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index 180bb0b32b77c..713bd3f9312c4 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -673,12 +673,8 @@ public function create_file_from_storedfile($file_record, $fileorid) { try { $newrecord->id = $DB->insert_record('files', $newrecord); } catch (dml_exception $e) { - $newrecord->id = false; - } - - if (!$newrecord->id) { throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, - $newrecord->filepath, $newrecord->filename); + $newrecord->filepath, $newrecord->filename, $e->debuginfo); } $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); @@ -820,15 +816,11 @@ public function create_file_from_pathname($file_record, $pathname) { try { $newrecord->id = $DB->insert_record('files', $newrecord); } catch (dml_exception $e) { - $newrecord->id = false; - } - - if (!$newrecord->id) { if ($newfile) { $this->deleted_file_cleanup($newrecord->contenthash); } throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, - $newrecord->filepath, $newrecord->filename); + $newrecord->filepath, $newrecord->filename, $e->debuginfo); } $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); @@ -915,15 +907,11 @@ public function create_file_from_string($file_record, $content) { try { $newrecord->id = $DB->insert_record('files', $newrecord); } catch (dml_exception $e) { - $newrecord->id = false; - } - - if (!$newrecord->id) { if ($newfile) { $this->deleted_file_cleanup($newrecord->contenthash); } throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, - $newrecord->filepath, $newrecord->filename); + $newrecord->filepath, $newrecord->filename, $e->debuginfo); } $this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid); From 260c4a5b0991425abd58cf85e7ccadc6376a7a9b Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Fri, 14 Oct 2011 15:50:27 +0200 Subject: [PATCH 34/67] MDL-29773 prevent negative unix timestamps in the files table --- lib/filestorage/file_storage.php | 64 ++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index 713bd3f9312c4..21779e8233bf5 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -656,6 +656,16 @@ public function create_file_from_storedfile($file_record, $fileorid) { } } + if ($key === 'timecreated' or $key === 'timemodified') { + if (!is_number($value)) { + throw new file_exception('storedfileproblem', 'Invalid file '.$key); + } + if ($value < 0) { + //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) + $value = 0; + } + } + $newrecord->$key = $value; } @@ -790,6 +800,29 @@ public function create_file_from_pathname($file_record, $pathname) { } $now = time(); + if (isset($file_record->timecreated)) { + if (!is_number($file_record->timecreated)) { + throw new file_exception('storedfileproblem', 'Invalid file timecreated'); + } + if ($file_record->timecreated < 0) { + //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) + $file_record->timecreated = 0; + } + } else { + $file_record->timecreated = $now; + } + + if (isset($file_record->timemodified)) { + if (!is_number($file_record->timemodified)) { + throw new file_exception('storedfileproblem', 'Invalid file timemodified'); + } + if ($file_record->timemodified < 0) { + //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) + $file_record->timemodified = 0; + } + } else { + $file_record->timemodified = $now; + } $newrecord = new stdClass(); @@ -800,8 +833,8 @@ public function create_file_from_pathname($file_record, $pathname) { $newrecord->filepath = $file_record->filepath; $newrecord->filename = $file_record->filename; - $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; - $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; + $newrecord->timecreated = $file_record->timecreated; + $newrecord->timemodified = $file_record->timemodified; $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; $newrecord->source = empty($file_record->source) ? null : $file_record->source; @@ -881,6 +914,29 @@ public function create_file_from_string($file_record, $content) { } $now = time(); + if (isset($file_record->timecreated)) { + if (!is_number($file_record->timecreated)) { + throw new file_exception('storedfileproblem', 'Invalid file timecreated'); + } + if ($file_record->timecreated < 0) { + //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) + $file_record->timecreated = 0; + } + } else { + $file_record->timecreated = $now; + } + + if (isset($file_record->timemodified)) { + if (!is_number($file_record->timemodified)) { + throw new file_exception('storedfileproblem', 'Invalid file timemodified'); + } + if ($file_record->timemodified < 0) { + //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak) + $file_record->timemodified = 0; + } + } else { + $file_record->timemodified = $now; + } $newrecord = new stdClass(); @@ -891,8 +947,8 @@ public function create_file_from_string($file_record, $content) { $newrecord->filepath = $file_record->filepath; $newrecord->filename = $file_record->filename; - $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated; - $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified; + $newrecord->timecreated = $file_record->timecreated; + $newrecord->timemodified = $file_record->timemodified; $newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype; $newrecord->userid = empty($file_record->userid) ? null : $file_record->userid; $newrecord->source = empty($file_record->source) ? null : $file_record->source; From 7846a85bc1848271ee0632256c5a038986ba4947 Mon Sep 17 00:00:00 2001 From: Mary Evans Date: Fri, 14 Oct 2011 20:39:36 +0100 Subject: [PATCH 35/67] MDL-29781 FIX for side-post-only layout --- theme/overlay/style/pagelayout.css | 116 ++++++++++++++++++----------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/theme/overlay/style/pagelayout.css b/theme/overlay/style/pagelayout.css index 9c951e6504c07..22f1792e187d9 100644 --- a/theme/overlay/style/pagelayout.css +++ b/theme/overlay/style/pagelayout.css @@ -1,37 +1,43 @@ /** Path: theme pagelayout **/ -body {margin:auto 0px;width:auto; height: 100%} -#page {width:100%; min-height: 100%;} +body {margin:auto 0px; + width:auto; + height: 100% +} +#page { + width:100%; + min-height: 100%; +} #page-content { clear: both; position: relative; - width: 100%;min-height: 100%; + width: 100%; + min-height: 100%; } - #page-content #region-main-box { float: left; margin-left: -200px; position: relative; width: 200%; - right: 100%;min-height: 100%; + right: 100%; + min-height: 100%; } - #page-content #region-post-box { float: left; margin-left: -200px; - width: 100%;min-height: 100%; + width: 100%; + min-height: 100%; } - #page-content #region-main-wrap { float: left; - width: 50%;min-height: 100%; + width: 50%; + min-height: 100%; } - #page-content #region-main { position: relative; margin-left: 400px; - left: 100%;min-height: 100%; + left: 100%; + min-height: 100%; } - #page-content #region-pre { float: right; position: relative; @@ -39,32 +45,29 @@ body {margin:auto 0px;width:auto; height: 100%} left: 200px; min-height: 100%; } - #page-content #region-post { float: right; position: relative; left: 600px; - width: 200px;min-height: 100%; + width: 200px; + min-height: 100%; } - #page-content #region-main .region-content { overflow: hidden; - padding: 20px 20px 20px 0;min-height: 100%; + padding: 20px 20px 20px 0; + min-height: 100%; } - .pagelayout-report #page-content #region-main .region-content { overflow: auto; - padding-bottom:0; - margin-bottom:20px; + padding-bottom: 0; + margin-bottom: 20px; } - #page-content #region-pre .region-content, #page-content #region-post .region-content { overflow: hidden; padding: 20px 10px; min-height: 100%; } - #page-footer { clear: both; float: left; @@ -74,45 +77,78 @@ body {margin:auto 0px;width:auto; height: 100%} /** Only side pre **/ .side-pre-only #page-content #region-main-box { - margin-left: 0px; + margin-left: 0; } - .side-pre-only #page-content #region-main-box #region-post-box { margin-left: -200px; } - .side-pre-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { margin-left: 200px; } - .side-pre-only #page-content #region-main-box #region-post-box #region-pre { left: 200px; width: 200px; } - .side-pre-only #page-content #region-main-box #region-post-box #region-post { - width: 0%; + width: 0; } /** Only side post **/ .side-post-only #page-content #region-main-box { - margin-left: 0px; + margin-left: 0; } - .side-post-only #page-content #region-main-box #region-post-box { margin-left: -200px; } - .side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { margin-left: 200px; } - +.side-post-only #page-content #region-main-box #region-post-box #region-pre { + left: 0; + width: 0; +} .side-post-only #page-content #region-main-box #region-post-box #region-post { - left: 400px; + left: 200px; width: 200px; } - +.blocks-moving.side-post-only #page-content #region-main-box { + float: left; + margin-left: -200px; + position: relative; + width: 200%; + right: 100%;min-height: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box { + float: left; + margin-left: -200px; + width: 100%;min-height: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap { + float: left; + width: 50%; + min-height: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { + position: relative; + margin-left: 400px; + left: 100%; + min-height: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre { + float: right; + position: relative; + width: 200px; + left: 200px; + min-height: 100%; +} +.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post { + float: right; + position: relative; + left: 600px; + width: 200px; + min-height: 100%; +} .has_dock.side-post-only .page-middle #region-main-box #region-post-box #region-main-wrap #region-main { margin-left: 200px; } @@ -120,21 +156,17 @@ body {margin:auto 0px;width:auto; height: 100%} /** No blocks whatsoever **/ .content-only #page-content #region-main-box { - margin-left: 0px; + margin-left: 0; } - .content-only #page-content #region-main-box #region-post-box { - margin-left: 0px; + margin-left: 0; } - .content-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main { - margin-left: 0px; + margin-left: 0; } - .content-only #page-content #region-main-box #region-post-box #region-pre { - width: 0px; + width: 0; } - .content-only #page-content #region-main-box #region-post-box #region-post { - width: 0px; + width: 0; } \ No newline at end of file From f3a99ef8f750a6305d43131a47da445349bced49 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Fri, 14 Oct 2011 23:55:27 +0200 Subject: [PATCH 36/67] MDL-29788 Fixed the version of installed language packs --- lib/componentlib.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/componentlib.class.php b/lib/componentlib.class.php index 0bb7c713c874d..efddc9dbcbac9 100644 --- a/lib/componentlib.class.php +++ b/lib/componentlib.class.php @@ -592,7 +592,7 @@ public function __construct($langcode = '') { global $CFG; $this->set_queue($langcode); - $this->version = '2.1'; + $this->version = '2.2'; if (!empty($CFG->langotherroot) and $CFG->langotherroot !== $CFG->dataroot . '/lang') { debugging('The in-built language pack installer does not support alternative location ' . From e922fe23b684eef2c82f0ade1f7ec4bbad9ac554 Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Fri, 14 Oct 2011 12:48:00 +0200 Subject: [PATCH 37/67] MDL-29602 accesslib improvements Refactoring and improvements of the accesslib.php library including prevention of access for not-logged-in users when forcelogin enabled, improved context caching, OOP refactoring of contexts, fixed context loading, deduplication of role definitions in user sessions, installation improvements, decoupling of enrolment checking from capability loading, added detection of deleted and non-existent users in has_capability(), new function accesslib test, auth and enrol upgrade notes. More details are available in tracker subtasks. --- admin/report/questioninstances/index.php | 12 +- admin/roles/usersroles.php | 9 +- admin/tool/capability/index.php | 6 +- auth/shibboleth/index.php | 7 +- auth/upgrade.txt | 10 + blog/edit.php | 4 +- blog/edit_form.php | 8 +- blog/locallib.php | 14 +- enrol/guest/lib.php | 4 +- enrol/paypal/return.php | 3 - enrol/upgrade.txt | 8 + lib/accesslib.php | 10164 +++++++++++---------- lib/datalib.php | 6 +- lib/db/install.php | 69 +- lib/deprecatedlib.php | 27 +- lib/enrollib.php | 10 +- lib/moodlelib.php | 14 +- lib/navigationlib.php | 2 +- lib/sessionlib.php | 12 +- lib/simpletest/fulltestaccesslib.php | 1172 +++ lib/simpletest/testaccesslib.php | 382 - lib/upgradelib.php | 10 +- login/token.php | 3 + rss/file.php | 10 +- webservice/lib.php | 1 + webservice/upload.php | 2 + 26 files changed, 6852 insertions(+), 5117 deletions(-) create mode 100644 auth/upgrade.txt create mode 100644 enrol/upgrade.txt create mode 100644 lib/simpletest/fulltestaccesslib.php delete mode 100644 lib/simpletest/testaccesslib.php diff --git a/admin/report/questioninstances/index.php b/admin/report/questioninstances/index.php index 2add057b6dfa3..3719baf229d91 100644 --- a/admin/report/questioninstances/index.php +++ b/admin/report/questioninstances/index.php @@ -65,13 +65,15 @@ // Get the question counts, and all the context information, for each // context. That is, rows of these results can be used as $context objects. + $ctxpreload = context_helper::get_preload_record_columns_sql('con'); + $ctxgroupby = implode(',', array_keys(context_helper::get_preload_record_columns('con'))); $counts = $DB->get_records_sql(" - SELECT qc.contextid, count(1) as numquestions, sum(hidden) as numhidden, con.id, con.contextlevel, con.instanceid, con.path, con.depth + SELECT qc.contextid, count(1) as numquestions, sum(hidden) as numhidden, $ctxpreload FROM {question} q JOIN {question_categories} qc ON q.category = qc.id JOIN {context} con ON con.id = qc.contextid $sqlqtypetest - GROUP BY contextid, con.id, con.contextlevel, con.instanceid, con.path, con.depth + GROUP BY qc.contextid, $ctxgroupby ORDER BY numquestions DESC, numhidden ASC, con.contextlevel ASC, con.id ASC", $params); // Print the report heading. @@ -94,8 +96,10 @@ $totalhidden = 0; foreach ($counts as $count) { // Work out a link for editing questions in this context. - $contextname = print_context_name($count); - $url = question_edit_url($count); + context_helper::preload_from_record($count); + $context = context::instance_by_id($count->contextid); + $contextname = $context->get_context_name(); + $url = question_edit_url($context); if ($url) { $contextname = 'wwwroot/$CFG->admin/roles/override.php?contextid=$contextid"; $title = get_string('changeoverrides', 'tool_capability'); } - echo '

    ', print_context_name($contexts[$contextid]), '

    '; + $context = context::instance_by_id($contextid); + echo '

    ', $context->get_context_name(), '

    '; // If there are any role overrides here, print them. if (!empty($contexts[$contextid]->rolecapabilities)) { diff --git a/auth/shibboleth/index.php b/auth/shibboleth/index.php index ea33b6cc8b8e0..7bc8f90c89294 100644 --- a/auth/shibboleth/index.php +++ b/auth/shibboleth/index.php @@ -39,7 +39,9 @@ if ($shibbolethauth->user_login($frm->username, $frm->password)) { - $USER = authenticate_user_login($frm->username, $frm->password); + $user = authenticate_user_login($frm->username, $frm->password); + enrol_check_plugins($user); + session_set_user($user); $USER->loggedin = true; $USER->site = $CFG->wwwroot; // for added security, store the site in the @@ -75,9 +77,6 @@ } } - enrol_check_plugins($USER); - load_all_capabilities(); /// This is what lets the user do anything on the site :-) - redirect($urltogo); exit; diff --git a/auth/upgrade.txt b/auth/upgrade.txt new file mode 100644 index 0000000000000..d3e1761a0cd46 --- /dev/null +++ b/auth/upgrade.txt @@ -0,0 +1,10 @@ +This files describes API changes in /auth/* - plugins, +information provided here is intended especially for developers. + +=== 2.2 === + +required changes in code: +* the correct sequence to set up global $USER is: + $user = get_complete_user_data('username', $username); // or $user = authenticate_user_login() + enrol_check_plugins($user); + session_set_user($user); diff --git a/blog/edit.php b/blog/edit.php index ba5bdfccf90d0..1ba7ac496dc48 100644 --- a/blog/edit.php +++ b/blog/edit.php @@ -143,9 +143,9 @@ if ($CFG->useblogassociations && ($blogassociations = $DB->get_records('blog_association', array('blogid' => $entry->id)))) { foreach ($blogassociations as $assocrec) { - $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid)); + $context = get_context_instance_by_id($assocrec->contextid); - switch ($contextrec->contextlevel) { + switch ($context->contextlevel) { case CONTEXT_COURSE: $entry->courseassoc = $assocrec->contextid; break; diff --git a/blog/edit_form.php b/blog/edit_form.php index cbfd4aed4d8aa..04c67e5b4cc6b 100644 --- a/blog/edit_form.php +++ b/blog/edit_form.php @@ -95,7 +95,7 @@ function definition() { $a->modname = $mod->name; $context = get_context_instance(CONTEXT_MODULE, $modid); } else { - $context = $DB->get_record('context', array('id' => $entry->modassoc)); + $context = get_context_instance_by_id($entry->modassoc); $cm = $DB->get_record('course_modules', array('id' => $context->instanceid)); $a = new stdClass(); $a->modtype = $DB->get_field('modules', 'name', array('id' => $cm->module)); @@ -134,7 +134,7 @@ function validation($data, $files) { // validate course association if (!empty($data['courseassoc']) && has_capability('moodle/blog:associatecourse', $sitecontext)) { - $coursecontext = $DB->get_record('context', array('id' => $data['courseassoc'], 'contextlevel' => CONTEXT_COURSE)); + $coursecontext = get_context_instance(CONTEXT_COURSE, $data['courseassoc']); if ($coursecontext) { if (!is_enrolled($coursecontext) and !is_viewing($coursecontext)) { @@ -149,12 +149,12 @@ function validation($data, $files) { if (!empty($data['modassoc'])) { $modcontextid = $data['modassoc']; - $modcontext = $DB->get_record('context', array('id' => $modcontextid, 'contextlevel' => CONTEXT_MODULE)); + $modcontext = get_context_instance(CONTEXT_MODULE, $modcontextid); if ($modcontext) { // get context of the mod's course $path = explode('/', $modcontext->path); - $coursecontext = $DB->get_record('context', array('id' => $path[(count($path) - 2)])); + $coursecontext = get_context_instance_by_id($path[(count($path) - 2)]); // ensure only one course is associated if (!empty($data['courseassoc'])) { diff --git a/blog/locallib.php b/blog/locallib.php index 51afba8b1f60d..19b01ca322cd0 100644 --- a/blog/locallib.php +++ b/blog/locallib.php @@ -247,10 +247,10 @@ public function print_html($return=false) { // First find and show the associated course foreach ($blogassociations as $assocrec) { - $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid)); - if ($contextrec->contextlevel == CONTEXT_COURSE) { - $assocurl = new moodle_url('/course/view.php', array('id' => $contextrec->instanceid)); - $text = $DB->get_field('course', 'shortname', array('id' => $contextrec->instanceid)); //TODO: performance!!!! + $context = get_context_instance_by_id($assocrec->contextid); + if ($context->contextlevel == CONTEXT_COURSE) { + $assocurl = new moodle_url('/course/view.php', array('id' => $context->instanceid)); + $text = $DB->get_field('course', 'shortname', array('id' => $context->instanceid)); //TODO: performance!!!! $assocstr .= $OUTPUT->action_icon($assocurl, new pix_icon('i/course', $text), null, array(), true); $hascourseassocs = true; $assoctype = get_string('course'); @@ -259,15 +259,15 @@ public function print_html($return=false) { // Now show mod association foreach ($blogassociations as $assocrec) { - $contextrec = $DB->get_record('context', array('id' => $assocrec->contextid)); + $context = get_context_instance_by_id($assocrec->contextid); - if ($contextrec->contextlevel == CONTEXT_MODULE) { + if ($context->contextlevel == CONTEXT_MODULE) { if ($hascourseassocs) { $assocstr .= ', '; $hascourseassocs = false; } - $modinfo = $DB->get_record('course_modules', array('id' => $contextrec->instanceid)); + $modinfo = $DB->get_record('course_modules', array('id' => $context->instanceid)); $modname = $DB->get_field('modules', 'name', array('id' => $modinfo->module)); $assocurl = new moodle_url('/mod/'.$modname.'/view.php', array('id' => $modinfo->id)); diff --git a/enrol/guest/lib.php b/enrol/guest/lib.php index 570539c6bc0e3..39516e14ba046 100644 --- a/enrol/guest/lib.php +++ b/enrol/guest/lib.php @@ -77,7 +77,7 @@ public function try_guestaccess(stdClass $instance) { if (empty($instance->password)) { // Temporarily assign them some guest role for this context $context = get_context_instance(CONTEXT_COURSE, $instance->courseid); - $USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access); + load_temp_course_role($context, $CFG->guestroleid); return ENROL_REQUIRE_LOGIN_CACHE_PERIOD + time(); } @@ -131,7 +131,7 @@ public function enrol_page_hook(stdClass $instance) { // add guest role $context = get_context_instance(CONTEXT_COURSE, $instance->courseid); - $USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access); + load_temp_course_role($context, $CFG->guestroleid); // go to the originally requested page if (!empty($SESSION->wantsurl)) { diff --git a/enrol/paypal/return.php b/enrol/paypal/return.php index 89cb2d0069c02..a8a65570c4c86 100644 --- a/enrol/paypal/return.php +++ b/enrol/paypal/return.php @@ -38,9 +38,6 @@ require_login(); -// Refreshing enrolment data in the USER session -load_all_capabilities(); - if ($SESSION->wantsurl) { $destination = $SESSION->wantsurl; unset($SESSION->wantsurl); diff --git a/enrol/upgrade.txt b/enrol/upgrade.txt new file mode 100644 index 0000000000000..4fb4c4996b022 --- /dev/null +++ b/enrol/upgrade.txt @@ -0,0 +1,8 @@ +This files describes API changes in /enrol/* - plugins, +information provided here is intended especially for developers. + +=== 2.2 === + +required changes in code: +* load_temp_role() is deprecated, use load_temp_course_role() instead, temp role not loaded +* remove_temp_role() is deprecated, use remove_temp_course_roles() instead diff --git a/lib/accesslib.php b/lib/accesslib.php index a2b50e237996c..21ba4b4075396 100644 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -1,5 +1,4 @@ id), context_coursecat::instance($catid) + * - context::instance_by_id($contextid) + * - $context->get_parent_contexts(); + * - $context->get_child_contexts(); * * Whether the user can do something... * - has_capability() @@ -34,26 +33,28 @@ * - has_all_capabilities() * - require_capability() * - require_login() (from moodlelib) + * - is_siteadmin() * * What courses has this user access to? - * - get_user_courses_bycap() + * - get_enrolled_users() * * What users can do X in this context? * - get_users_by_capability() * - * Enrol/unenrol - * - enrol_into_course() - * - role_assign()/role_unassign() + * Modify roles + * - role_assign() + * - role_unassign() + * - role_unassign_all() * * - * Advanced use + * Advanced - for internal use only * - load_all_capabilities() * - reload_all_capabilities() * - has_capability_in_accessdata() - * - is_siteadmin() * - get_user_access_sitewide() - * - load_subcontext() - * - get_role_access_bycontext() + * - load_course_context() + * - load_role_access_by_context() + * - etc. * * Name conventions * @@ -65,7 +66,7 @@ * which - for the logged-in user, will be in $USER->access * * For other users can be generated and passed around (but may also be cached - * against userid in $ACCESSLIB_PRIVATE->accessdatabyuser. + * against userid in $ACCESSLIB_PRIVATE->accessdatabyuser). * * $accessdata is a multidimensional array, holding * role assignments (RAs), role-capabilities-perm sets @@ -75,29 +76,27 @@ * Things are keyed on "contextpaths" (the path field of * the context table) for fast walking up/down the tree. * - * $accessdata[ra][$contextpath]= array($roleid) - * [$contextpath]= array($roleid) - * [$contextpath]= array($roleid) + * $accessdata['ra'][$contextpath] = array($roleid=>$roleid) + * [$contextpath] = array($roleid=>$roleid) + * [$contextpath] = array($roleid=>$roleid) * * * Role definitions are stored like this * (no cap merge is done - so it's compact) * * - * $accessdata[rdef][$contextpath:$roleid][mod/forum:viewpost] = 1 - * [mod/forum:editallpost] = -1 - * [mod/forum:startdiscussion] = -1000 + * $accessdata['rdef']["$contextpath:$roleid"]['mod/forum:viewpost'] = 1 + * ['mod/forum:editallpost'] = -1 + * ['mod/forum:startdiscussion'] = -1000 * * - * See how has_capability_in_accessdata() walks up/down the tree. + * See how has_capability_in_accessdata() walks up the tree. * - * Normally - specially for the logged-in user, we only load - * rdef and ra down to the course level, but not below. This - * keeps accessdata small and compact. Below-the-course ra/rdef - * are loaded as needed. We keep track of which courses we - * have loaded ra/rdef in + * First we only load rdef and ra down to the course level, but not below. + * This keeps accessdata small and compact. Below-the-course ra/rdef + * are loaded as needed. We keep track of which courses we have loaded ra/rdef in * - * $accessdata[loaded] = array($contextpath, $contextpath) + * $accessdata['loaded'] = array($courseid1=>1, $courseid2=>1) * * * Stale accessdata @@ -126,172 +125,61 @@ defined('MOODLE_INTERNAL') || die(); -/** permission definitions */ +/** No capability change */ define('CAP_INHERIT', 0); -/** permission definitions */ +/** Allow permission, overrides CAP_PREVENT defined in parent contexts */ define('CAP_ALLOW', 1); -/** permission definitions */ +/** Prevent permission, overrides CAP_ALLOW defined in parent contexts */ define('CAP_PREVENT', -1); -/** permission definitions */ +/** Prohibit permission, overrides everything in current and child contexts */ define('CAP_PROHIBIT', -1000); -/** context definitions */ +/** System context level - only one instance in every system */ define('CONTEXT_SYSTEM', 10); -/** context definitions */ +/** User context level - one instance for each user describing what others can do to user */ define('CONTEXT_USER', 30); -/** context definitions */ +/** Course category context level - one instance for each category */ define('CONTEXT_COURSECAT', 40); -/** context definitions */ +/** Course context level - one instances for each course */ define('CONTEXT_COURSE', 50); -/** context definitions */ +/** Course module context level - one instance for each course module */ define('CONTEXT_MODULE', 70); -/** context definitions */ +/** + * Block context level - one instance for each block, sticky blocks are tricky + * because ppl think they should be able to override them at lower contexts. + * Any other context level instance can be parent of block context. + */ define('CONTEXT_BLOCK', 80); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** Capability allow management of trusts - NOT IMPLEMENTED YET - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_MANAGETRUST', 0x0001); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** Capability allows changes in system configuration - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_CONFIG', 0x0002); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** Capability allows user to add scritped content - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_XSS', 0x0004); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** Capability allows access to personal user information - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_PERSONAL', 0x0008); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** Capability allows users to add content otehrs may see - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_SPAM', 0x0010); -/** capability risks - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ +/** capability allows mass delete of data belonging to other users - see {@link http://docs.moodle.org/dev/Hardening_new_Roles_system} */ define('RISK_DATALOSS', 0x0020); /** rolename displays - the name as defined in the role definition */ define('ROLENAME_ORIGINAL', 0); /** rolename displays - the name as defined by a role alias */ define('ROLENAME_ALIAS', 1); -/** rolename displays - Both, like this: Role alias (Original)*/ +/** rolename displays - Both, like this: Role alias (Original) */ define('ROLENAME_BOTH', 2); -/** rolename displays - the name as defined in the role definition and the shortname in brackets*/ +/** rolename displays - the name as defined in the role definition and the shortname in brackets */ define('ROLENAME_ORIGINALANDSHORT', 3); -/** rolename displays - the name as defined by a role alias, in raw form suitable for editing*/ +/** rolename displays - the name as defined by a role alias, in raw form suitable for editing */ define('ROLENAME_ALIAS_RAW', 4); -/** rolename displays - the name is simply short role name*/ +/** rolename displays - the name is simply short role name */ define('ROLENAME_SHORT', 5); -/** - * Internal class provides a cache of context information. The cache is - * restricted in size. - * - * This cache should NOT be used outside accesslib.php! - * - * @private - * @author Sam Marshall - */ -class context_cache { - private $contextsbyid; - private $contexts; - private $count; - - /** - * @var int Maximum number of contexts that will be cached. - */ - const MAX_SIZE = 2500; - /** - * @var int Once contexts reach maximum number, this many will be removed from cache. - */ - const REDUCE_SIZE = 1000; - - /** - * Initialises (empty) - */ - public function __construct() { - $this->reset(); - } - - /** - * Resets the cache to remove all data. - */ - public function reset() { - $this->contexts = array(); - $this->contextsbyid = array(); - $this->count = 0; - } - - /** - * Adds a context to the cache. If the cache is full, discards a batch of - * older entries. - * @param stdClass $context New context to add - */ - public function add(stdClass $context) { - if ($this->count >= self::MAX_SIZE) { - for ($i=0; $icontextsbyid)) { - unset($this->contextsbyid[$first->id]); - unset($this->contexts[$first->contextlevel][$first->instanceid]); - } - } - $this->count -= self::REDUCE_SIZE; - if ($this->count < 0) { - // most probably caused by the drift, the reset() above - // might have returned false because there might not be any more elements - $this->count = 0; - } - } - - $this->contexts[$context->contextlevel][$context->instanceid] = $context; - $this->contextsbyid[$context->id] = $context; - - // Note the count may get out of synch slightly if you cache a context - // that is already cached, but it doesn't really matter much and I - // didn't think it was worth the performance hit. - $this->count++; - } - - /** - * Removes a context from the cache. - * @param stdClass $context Context object to remove (must include fields - * ->id, ->contextlevel, ->instanceid at least) - */ - public function remove(stdClass $context) { - unset($this->contexts[$context->contextlevel][$context->instanceid]); - unset($this->contextsbyid[$context->id]); - - // Again the count may get a bit out of synch if you remove things - // that don't exist - $this->count--; - - if ($this->count < 0) { - $this->count = 0; - } - } - - /** - * Gets a context from the cache. - * @param int $contextlevel Context level - * @param int $instance Instance ID - * @return stdClass|bool Context or false if not in cache - */ - public function get($contextlevel, $instance) { - if (isset($this->contexts[$contextlevel][$instance])) { - return $this->contexts[$contextlevel][$instance]; - } - return false; - } - - /** - * Gets a context from the cache based on its id. - * @param int $id Context ID - * @return stdClass|bool Context or false if not in cache - */ - public function get_by_id($id) { - if (isset($this->contextsbyid[$id])) { - return $this->contextsbyid[$id]; - } - return false; - } - - /** - * @return int Count of contexts in cache (approximately) - */ - public function get_approx_count() { - return $this->count; - } +/** maximum size of context cache - it is possible to tweak this config.php or in any script before inclusion of context.php */ +if (!defined('CONTEXT_CACHE_MAX_SIZE')) { + define('CONTEXT_CACHE_MAX_SIZE', 2500); } /** @@ -302,19 +190,16 @@ public function get_approx_count() { * Sadly, a PHP global variable is the only way to implement this, without rewriting everything * as methods of a class, instead of functions. * + * @private * @global stdClass $ACCESSLIB_PRIVATE * @name $ACCESSLIB_PRIVATE */ global $ACCESSLIB_PRIVATE; $ACCESSLIB_PRIVATE = new stdClass(); -$ACCESSLIB_PRIVATE->contexcache = new context_cache(); -$ACCESSLIB_PRIVATE->systemcontext = null; // Used in get_system_context -$ACCESSLIB_PRIVATE->dirtycontexts = null; // Dirty contexts cache -$ACCESSLIB_PRIVATE->accessdatabyuser = array(); // Holds the $accessdata structure for users other than $USER -$ACCESSLIB_PRIVATE->roledefinitions = array(); // role definitions cache - helps a lot with mem usage in cron -$ACCESSLIB_PRIVATE->croncache = array(); // Used in get_role_access -$ACCESSLIB_PRIVATE->preloadedcourses = array(); // Used in preload_course_contexts. -$ACCESSLIB_PRIVATE->capabilities = null; // detailed information about the capabilities +$ACCESSLIB_PRIVATE->dirtycontexts = null; // Dirty contexts cache, loaded from DB once per page +$ACCESSLIB_PRIVATE->accessdatabyuser = array(); // Holds the cache of $accessdata structure for users (including $USER) +$ACCESSLIB_PRIVATE->rolepermissions = array(); // role permissions cache - helps a lot with mem usage +$ACCESSLIB_PRIVATE->capabilities = null; // detailed information about the capabilities /** * Clears accesslib's private caches. ONLY BE USED BY UNIT TESTS @@ -322,88 +207,62 @@ public function get_approx_count() { * This method should ONLY BE USED BY UNIT TESTS. It clears all of * accesslib's private caches. You need to do this before setting up test data, * and also at the end of the tests. + * + * @return void */ function accesslib_clear_all_caches_for_unit_testing() { - global $UNITTEST, $USER, $ACCESSLIB_PRIVATE; + global $UNITTEST, $USER; if (empty($UNITTEST->running)) { throw new coding_exception('You must not call clear_all_caches outside of unit tests.'); } - $ACCESSLIB_PRIVATE->contexcache = new context_cache(); - $ACCESSLIB_PRIVATE->systemcontext = null; - $ACCESSLIB_PRIVATE->dirtycontexts = null; - $ACCESSLIB_PRIVATE->accessdatabyuser = array(); - $ACCESSLIB_PRIVATE->roledefinitions = array(); - $ACCESSLIB_PRIVATE->croncache = array(); - $ACCESSLIB_PRIVATE->preloadedcourses = array(); - $ACCESSLIB_PRIVATE->capabilities = null; + + accesslib_clear_all_caches(true); unset($USER->access); } /** - * This is really slow!!! do not use above course context level + * Clears accesslib's private caches. ONLY BE USED FROM THIS LIBRARY FILE! * - * @param int $roleid - * @param object $context - * @return array + * This reset does not touch global $USER. + * + * @private + * @param bool $resetcontexts + * @return void */ -function get_role_context_caps($roleid, $context) { - global $DB; - - //this is really slow!!!! - do not use above course context level! - $result = array(); - $result[$context->id] = array(); +function accesslib_clear_all_caches($resetcontexts) { + global $ACCESSLIB_PRIVATE; - // first emulate the parent context capabilities merging into context - $searchcontexts = array_reverse(get_parent_contexts($context)); - array_push($searchcontexts, $context->id); - foreach ($searchcontexts as $cid) { - if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) { - foreach ($capabilities as $cap) { - if (!array_key_exists($cap->capability, $result[$context->id])) { - $result[$context->id][$cap->capability] = 0; - } - $result[$context->id][$cap->capability] += $cap->permission; - } - } - } + $ACCESSLIB_PRIVATE->dirtycontexts = null; + $ACCESSLIB_PRIVATE->accessdatabyuser = array(); + $ACCESSLIB_PRIVATE->rolepermissions = array(); + $ACCESSLIB_PRIVATE->capabilities = null; - // now go through the contexts bellow given context - $searchcontexts = array_keys(get_child_contexts($context)); - foreach ($searchcontexts as $cid) { - if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) { - foreach ($capabilities as $cap) { - if (!array_key_exists($cap->contextid, $result)) { - $result[$cap->contextid] = array(); - } - $result[$cap->contextid][$cap->capability] = $cap->permission; - } - } + if ($resetcontexts) { + context_helper::reset_caches(); } - - return $result; } /** * Gets the accessdata for role "sitewide" (system down to course) * + * @private * @param int $roleid - * @param array $accessdata defaults to null * @return array */ -function get_role_access($roleid, $accessdata = null) { - global $CFG, $DB; +function get_role_access($roleid) { + global $DB, $ACCESSLIB_PRIVATE; - /* Get it in 1 cheap DB query... + /* Get it in 1 DB query... * - relevant role caps at the root and down * to the course level - but not below */ - if (is_null($accessdata)) { - $accessdata = array(); // named list - $accessdata['ra'] = array(); - $accessdata['rdef'] = array(); - $accessdata['loaded'] = array(); - } + + //TODO: MUC - this could be cached in shared memory to speed up first page loading, web crawlers, etc. + + $accessdata = get_empty_accessdata(); + + $accessdata['ra']['/'.SYSCONTEXTID] = array((int)$roleid => (int)$roleid); // // Overrides for the role IN ANY CONTEXTS @@ -412,92 +271,37 @@ function get_role_access($roleid, $accessdata = null) { $sql = "SELECT ctx.path, rc.capability, rc.permission FROM {context} ctx - JOIN {role_capabilities} rc - ON rc.contextid=ctx.id - WHERE rc.roleid = ? - AND ctx.contextlevel <= ".CONTEXT_COURSE." - ORDER BY ctx.depth, ctx.path"; + JOIN {role_capabilities} rc ON rc.contextid = ctx.id + LEFT JOIN {context} cctx + ON (cctx.contextlevel = ".CONTEXT_COURSE." AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").") + WHERE rc.roleid = ? AND cctx.id IS NULL"; $params = array($roleid); // we need extra caching in CLI scripts and cron - if (CLI_SCRIPT) { - global $ACCESSLIB_PRIVATE; - - if (!isset($ACCESSLIB_PRIVATE->croncache[$roleid])) { - $ACCESSLIB_PRIVATE->croncache[$roleid] = array(); - $rs = $DB->get_recordset_sql($sql, $params); - foreach ($rs as $rd) { - $ACCESSLIB_PRIVATE->croncache[$roleid][] = $rd; - } - $rs->close(); - } - - foreach ($ACCESSLIB_PRIVATE->croncache[$roleid] as $rd) { - $k = "{$rd->path}:{$roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; - } - - } else { - $rs = $DB->get_recordset_sql($sql, $params); - if ($rs->valid()) { - foreach ($rs as $rd) { - $k = "{$rd->path}:{$roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; - } - unset($rd); - } - $rs->close(); + $rs = $DB->get_recordset_sql($sql, $params); + foreach ($rs as $rd) { + $k = "{$rd->path}:{$roleid}"; + $accessdata['rdef'][$k][$rd->capability] = (int)$rd->permission; } + $rs->close(); - return $accessdata; -} - -/** - * Gets the accessdata for role "sitewide" (system down to course) - * - * @param int $roleid - * @param array $accessdata defaults to null - * @return array - */ -function get_default_frontpage_role_access($roleid, $accessdata = null) { - - global $CFG, $DB; - - $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID); - $base = '/'. SYSCONTEXTID .'/'. $frontpagecontext->id; - - // - // Overrides for the role in any contexts related to the course - // - $sql = "SELECT ctx.path, - rc.capability, rc.permission - FROM {context} ctx - JOIN {role_capabilities} rc - ON rc.contextid=ctx.id - WHERE rc.roleid = ? - AND (ctx.id = ".SYSCONTEXTID." OR ctx.path LIKE ?) - AND ctx.contextlevel <= ".CONTEXT_COURSE." - ORDER BY ctx.depth, ctx.path"; - $params = array($roleid, "$base/%"); - - $rs = $DB->get_recordset_sql($sql, $params); - if ($rs->valid()) { - foreach ($rs as $rd) { - $k = "{$rd->path}:{$roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; + // share the role definitions + foreach ($accessdata['rdef'] as $k=>$unused) { + if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) { + $ACCESSLIB_PRIVATE->rolepermissions[$k] = $accessdata['rdef'][$k]; } - unset($rd); + $accessdata['rdef_count']++; + $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k]; } - $rs->close(); return $accessdata; } - /** - * Get the default guest role + * Get the default guest role, this is used for guest account, + * search engine spiders, etc. * - * @return stdClass role + * @return stdClass role record */ function get_guest_role() { global $CFG, $DB; @@ -515,7 +319,7 @@ function get_guest_role() { if ($guestrole = $DB->get_record('role', array('id'=>$CFG->guestroleid))) { return $guestrole; } else { - //somebody is messing with guest roles, remove incorrect setting and try to find a new one + // somebody is messing with guest roles, remove incorrect setting and try to find a new one set_config('guestroleid', ''); return get_guest_role(); } @@ -525,7 +329,7 @@ function get_guest_role() { /** * Check whether a user has a particular capability in a given context. * - * For example:: + * For example: * $context = get_context_instance(CONTEXT_MODULE, $cm->id); * has_capability('mod/forum:replypost',$context) * @@ -536,16 +340,16 @@ function get_guest_role() { * or capabilities with XSS, config or data loss risks. * * @param string $capability the name of the capability to check. For example mod/forum:view - * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}. + * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}. * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user. * @param boolean $doanything If false, ignores effect of admin role assignment * @return boolean true if the user has this capability. Otherwise false. */ -function has_capability($capability, $context, $user = null, $doanything = true) { - global $USER, $CFG, $DB, $SCRIPT, $ACCESSLIB_PRIVATE; +function has_capability($capability, context $context, $user = null, $doanything = true) { + global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE; if (during_initial_install()) { - if ($SCRIPT === "/$CFG->admin/index.php" or $SCRIPT === "/$CFG->admin/cliupgrade.php") { + if ($SCRIPT === "/$CFG->admin/index.php" or $SCRIPT === "/$CFG->admin/cli/install.php" or $SCRIPT === "/$CFG->admin/cli/install_database.php") { // we are in an installer - roles can not work yet return true; } else { @@ -557,90 +361,64 @@ function has_capability($capability, $context, $user = null, $doanything = true) throw new coding_exception('Legacy capabilities can not be used any more!'); } - // the original $CONTEXT here was hiding serious errors - // for security reasons do not reuse previous context - if (empty($context)) { - debugging('Incorrect context specified'); + if (!is_bool($doanything)) { + throw new coding_exception('Capability parameter "doanything" is wierd, only true or false is allowed. This has to be fixed in code.'); + } + + // capability must exist + if (!$capinfo = get_capability_info($capability)) { + debugging('Capability "'.$capability.'" was not found! This has to be fixed in code.'); return false; } - if (!is_bool($doanything)) { - throw new coding_exception('Capability parameter "doanything" is wierd ("'.$doanything.'"). This has to be fixed in code.'); + + if (!isset($USER->id)) { + // should never happen + $USER->id = 0; } // make sure there is a real user specified if ($user === null) { - $userid = isset($USER->id) ? $USER->id : 0; + $userid = $USER->id; } else { $userid = is_object($user) ? $user->id : $user; } - // capability must exist - if (!$capinfo = get_capability_info($capability)) { - debugging('Capability "'.$capability.'" was not found! This should be fixed in code.'); + // make sure forcelogin cuts off not-logged-in users if enabled + if (!empty($CFG->forcelogin) and $userid == 0) { return false; } + // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are. - if (($capinfo->captype === 'write') or ((int)$capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) { + if (($capinfo->captype === 'write') or ($capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) { if (isguestuser($userid) or $userid == 0) { return false; } } - if (is_null($context->path) or $context->depth == 0) { - //this should not happen - $contexts = array(SYSCONTEXTID, $context->id); - $context->path = '/'.SYSCONTEXTID.'/'.$context->id; - debugging('Context id '.$context->id.' does not have valid path, please use build_context_path()', DEBUG_DEVELOPER); - - } else { - $contexts = explode('/', $context->path); - array_shift($contexts); - } - - if (CLI_SCRIPT && !isset($USER->access)) { - // In cron, some modules setup a 'fake' $USER, - // ensure we load the appropriate accessdata. - if (isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) { - $ACCESSLIB_PRIVATE->dirtycontexts = null; //load fresh dirty contexts + // somehow make sure the user is not deleted and actually exists + if ($userid != 0) { + if ($userid == $USER->id and isset($USER->deleted)) { + // this prevents one query per page, it is a bit of cheating, + // but hopefully session is terminated properly once user is deleted + if ($USER->deleted) { + return false; + } } else { - load_user_accessdata($userid); - $ACCESSLIB_PRIVATE->dirtycontexts = array(); - } - $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; - - } else if (isset($USER->id) && ($USER->id == $userid) && !isset($USER->access)) { - // caps not loaded yet - better to load them to keep BC with 1.8 - // not-logged-in user or $USER object set up manually first time here - load_all_capabilities(); - $ACCESSLIB_PRIVATE->accessdatabyuser = array(); // reset the cache for other users too, the dirty contexts are empty now - $ACCESSLIB_PRIVATE->roledefinitions = array(); - } - - // Load dirty contexts list if needed - if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) { - if (isset($USER->access['time'])) { - $ACCESSLIB_PRIVATE->dirtycontexts = get_dirty_contexts($USER->access['time']); - } - else { - $ACCESSLIB_PRIVATE->dirtycontexts = array(); + if (!context_user::instance($userid, IGNORE_MISSING)) { + // no user context == invalid userid + return false; + } } } - // Careful check for staleness... - if (count($ACCESSLIB_PRIVATE->dirtycontexts) !== 0 and is_contextpath_dirty($contexts, $ACCESSLIB_PRIVATE->dirtycontexts)) { - // reload all capabilities - preserving loginas, roleswitches, etc - // and then cleanup any marks of dirtyness... at least from our short - // term memory! :-) - $ACCESSLIB_PRIVATE->accessdatabyuser = array(); - $ACCESSLIB_PRIVATE->roledefinitions = array(); - - if (CLI_SCRIPT) { - load_user_accessdata($userid); - $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; - $ACCESSLIB_PRIVATE->dirtycontexts = array(); - + // context path/depth must be valid + if (empty($context->path) or $context->depth == 0) { + // this should not happen often, each upgrade tries to rebuild the context paths + debugging('Context id '.$context->id.' does not have valid path, please use build_context_path()'); + if (is_siteadmin($userid)) { + return true; } else { - reload_all_capabilities(); + return false; } } @@ -672,49 +450,29 @@ function has_capability($capability, $context, $user = null, $doanything = true) } } - // divulge how many times we are called - //// error_log("has_capability: id:{$context->id} path:{$context->path} userid:$userid cap:$capability"); + // Careful check for staleness... + $context->reload_if_dirty(); - if (isset($USER->id) && ($USER->id == $userid)) { // we must accept strings and integers in $userid - // - // For the logged in user, we have $USER->access - // which will have all RAs and caps preloaded for - // course and above contexts. - // - // Contexts below courses && contexts that do not - // hang from courses are loaded into $USER->access - // on demand, and listed in $USER->access[loaded] - // - if ($context->contextlevel <= CONTEXT_COURSE) { - // Course and above are always preloaded - return has_capability_in_accessdata($capability, $context, $USER->access); + if ($USER->id == $userid) { + if (!isset($USER->access)) { + load_all_capabilities(); } - // Load accessdata for below-the-course contexts - if (!path_inaccessdata($context->path,$USER->access)) { - // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}"); - // $bt = debug_backtrace(); - // error_log("bt {$bt[0]['file']} {$bt[0]['line']}"); - load_subcontext($USER->id, $context, $USER->access); - } - return has_capability_in_accessdata($capability, $context, $USER->access); - } + $access =& $USER->access; - if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) { - load_user_accessdata($userid); + } else { + // make sure user accessdata is really loaded + get_user_accessdata($userid, true); + $access =& $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; } - if ($context->contextlevel <= CONTEXT_COURSE) { - // Course and above are always preloaded - return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]); - } - // Load accessdata for below-the-course contexts as needed - if (!path_inaccessdata($context->path, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) { - // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}"); - // $bt = debug_backtrace(); - // error_log("bt {$bt[0]['file']} {$bt[0]['line']}"); - load_subcontext($userid, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]); + + // Load accessdata for below-the-course context if necessary, + // all contexts at and above all courses are already loaded + if ($context->contextlevel != CONTEXT_COURSE and $coursecontext = $context->get_course_context(false)) { + load_course_context($userid, $coursecontext, $access); } - return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]); + + return has_capability_in_accessdata($capability, $context, $access); } /** @@ -724,21 +482,14 @@ function has_capability($capability, $context, $user = null, $doanything = true) * the capabilities that most users are likely to have first in the list for best * performance. * - * There are probably tricks that could be done to improve the performance here, for example, - * check the capabilities that are already cached first. - * * @see has_capability() * @param array $capabilities an array of capability names. - * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}. + * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}. * @param integer $userid A user id. By default (null) checks the permissions of the current user. * @param boolean $doanything If false, ignore effect of admin role assignment * @return boolean true if the user has any of these capabilities. Otherwise false. */ -function has_any_capability($capabilities, $context, $userid = null, $doanything = true) { - if (!is_array($capabilities)) { - debugging('Incorrect $capabilities parameter in has_any_capabilities() call - must be an array'); - return false; - } +function has_any_capability(array $capabilities, context $context, $userid = null, $doanything = true) { foreach ($capabilities as $capability) { if (has_capability($capability, $context, $userid, $doanything)) { return true; @@ -754,21 +505,14 @@ function has_any_capability($capabilities, $context, $userid = null, $doanything * the capabilities that fewest users are likely to have first in the list for best * performance. * - * There are probably tricks that could be done to improve the performance here, for example, - * check the capabilities that are already cached first. - * * @see has_capability() * @param array $capabilities an array of capability names. - * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}. + * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}. * @param integer $userid A user id. By default (null) checks the permissions of the current user. * @param boolean $doanything If false, ignore effect of admin role assignment * @return boolean true if the user has all of these capabilities. Otherwise false. */ -function has_all_capabilities($capabilities, $context, $userid = null, $doanything = true) { - if (!is_array($capabilities)) { - debugging('Incorrect $capabilities parameter in has_all_capabilities() call - must be an array'); - return false; - } +function has_all_capabilities(array $capabilities, context $context, $userid = null, $doanything = true) { foreach ($capabilities as $capability) { if (!has_capability($capability, $context, $userid, $doanything)) { return false; @@ -783,8 +527,8 @@ function has_all_capabilities($capabilities, $context, $userid = null, $doanythi * Please note that use of proper capabilities is always encouraged, * this function is supposed to be used from core or for temporary hacks. * - * @param int|object $user_or_id user id or user object - * @returns bool true if user is one of the administrators, false otherwise + * @param int|stdClass $user_or_id user id or user object + * @return bool true if user is one of the administrators, false otherwise */ function is_siteadmin($user_or_id = null) { global $CFG, $USER; @@ -797,7 +541,6 @@ function is_siteadmin($user_or_id = null) { return false; } if (!empty($user_or_id->id)) { - // we support $userid = $user_or_id->id; } else { $userid = $user_or_id; @@ -811,8 +554,8 @@ function is_siteadmin($user_or_id = null) { * Returns true if user has at least one role assign * of 'coursecontact' role (is potentially listed in some course descriptions). * - * @param $userid - * @return stdClass + * @param int $userid + * @return bool */ function has_coursecontact_role($userid) { global $DB, $CFG; @@ -826,52 +569,11 @@ function has_coursecontact_role($userid) { return $DB->record_exists_sql($sql, array('userid'=>$userid)); } -/** - * @param string $path - * @return string - */ -function get_course_from_path($path) { - // assume that nothing is more than 1 course deep - if (preg_match('!^(/.+)/\d+$!', $path, $matches)) { - return $matches[1]; - } - return false; -} - -/** - * @param string $path - * @param array $accessdata - * @return bool - */ -function path_inaccessdata($path, $accessdata) { - if (empty($accessdata['loaded'])) { - return false; - } - - // assume that contexts hang from sys or from a course - // this will only work well with stuff that hangs from a course - if (in_array($path, $accessdata['loaded'], true)) { - // error_log("found it!"); - return true; - } - $base = '/' . SYSCONTEXTID; - while (preg_match('!^(/.+)/\d+$!', $path, $matches)) { - $path = $matches[1]; - if ($path === $base) { - return false; - } - if (in_array($path, $accessdata['loaded'], true)) { - return true; - } - } - return false; -} - /** * Does the user have a capability to do something? * * Walk the accessdata array and return true/false. - * Deals with prohibits, roleswitching, aggregating + * Deals with prohibits, role switching, aggregating * capabilities, etc. * * The main feature of here is being FAST and with no @@ -879,12 +581,6 @@ function path_inaccessdata($path, $accessdata) { * * Notes: * - * Switch Roles exits early - * ------------------------ - * cap checks within a switchrole need to exit early - * in our bottom up processing so they don't "see" that - * there are real RAs that can do all sorts of things. - * * Switch Role merges with default role * ------------------------------------ * If you are a teacher in course X, you have at least @@ -901,26 +597,25 @@ function path_inaccessdata($path, $accessdata) { * and then verify if user has at least one role with allow * and at the same time no role with prohibit. * + * @private * @param string $capability - * @param object $context + * @param context $context * @param array $accessdata * @return bool */ -function has_capability_in_accessdata($capability, $context, array $accessdata) { +function has_capability_in_accessdata($capability, context $context, array &$accessdata) { global $CFG; - if (empty($context->id)) { - throw new coding_exception('Invalid context specified'); - } - // Build $paths as a list of current + all parent "paths" with order bottom-to-top - $contextids = explode('/', trim($context->path, '/')); - $paths = array($context->path); - while ($contextids) { - array_pop($contextids); - $paths[] = '/' . implode('/', $contextids); + $path = $context->path; + $paths = array($path); + while($path = rtrim($path, '0123456789')) { + $path = rtrim($path, '/'); + if ($path === '') { + break; + } + $paths[] = $path; } - unset($contextids); $roles = array(); $switchedrole = false; @@ -950,56 +645,25 @@ function has_capability_in_accessdata($capability, $context, array $accessdata) } // Now find out what access is given to each role, going bottom-->up direction + $allowed = false; foreach ($roles as $roleid => $ignored) { foreach ($paths as $path) { if (isset($accessdata['rdef']["{$path}:$roleid"][$capability])) { $perm = (int)$accessdata['rdef']["{$path}:$roleid"][$capability]; - if ($perm === CAP_PROHIBIT or is_null($roles[$roleid])) { + if ($perm === CAP_PROHIBIT) { + // any CAP_PROHIBIT found means no permission for the user + return false; + } + if (is_null($roles[$roleid])) { $roles[$roleid] = $perm; } } } - } - // any CAP_PROHIBIT found means no permission for the user - if (array_search(CAP_PROHIBIT, $roles) !== false) { - return false; + // CAP_ALLOW in any role means the user has a permission, we continue only to detect prohibits + $allowed = ($allowed or $roles[$roleid] === CAP_ALLOW); } - // at least one CAP_ALLOW means the user has a permission - return (array_search(CAP_ALLOW, $roles) !== false); -} - -/** - * @param object $context - * @param array $accessdata - * @return array - */ -function aggregate_roles_from_accessdata($context, $accessdata) { - - $path = $context->path; - - // build $contexts as a list of "paths" of the current - // contexts and parents with the order top-to-bottom - $contexts = array($path); - while (preg_match('!^(/.+)/\d+$!', $path, $matches)) { - $path = $matches[1]; - array_unshift($contexts, $path); - } - - $cc = count($contexts); - - $roles = array(); - // From the bottom up... - for ($n=$cc-1; $n>=0; $n--) { - $ctxp = $contexts[$n]; - if (isset($accessdata['ra'][$ctxp]) && count($accessdata['ra'][$ctxp])) { - // Found assignments on this leaf - $addroles = $accessdata['ra'][$ctxp]; - $roles = array_merge($roles, $addroles); - } - } - - return array_unique($roles); + return $allowed; } /** @@ -1014,343 +678,269 @@ function aggregate_roles_from_accessdata($context, $accessdata) { * @see has_capability() * * @param string $capability the name of the capability to check. For example mod/forum:view - * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}. - * @param integer $userid A user id. By default (null) checks the permissions of the current user. + * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}. + * @param int $userid A user id. By default (null) checks the permissions of the current user. * @param bool $doanything If false, ignore effect of admin role assignment - * @param string $errorstring The error string to to user. Defaults to 'nopermissions'. + * @param string $errormessage The error string to to user. Defaults to 'nopermissions'. * @param string $stringfile The language file to load the error string from. Defaults to 'error'. * @return void terminates with an error if the user does not have the given capability. */ -function require_capability($capability, $context, $userid = null, $doanything = true, +function require_capability($capability, context $context, $userid = null, $doanything = true, $errormessage = 'nopermissions', $stringfile = '') { if (!has_capability($capability, $context, $userid, $doanything)) { throw new required_capability_exception($context, $capability, $errormessage, $stringfile); } } -/** - * Get an array of courses where cap requested is available - * and user is enrolled, this can be relatively slow. - * - * @param string $capability - name of the capability - * @param array $accessdata_ignored - * @param bool $doanything_ignored - * @param string $sort - sorting fields - prefix each fieldname with "c." - * @param array $fields - additional fields you are interested in... - * @param int $limit_ignored - * @return array $courses - ordered array of course objects - see notes above - */ -function get_user_courses_bycap($userid, $cap, $accessdata_ignored, $doanything_ignored, $sort = 'c.sortorder ASC', $fields = null, $limit_ignored = 0) { - - //TODO: this should be most probably deprecated - - $courses = enrol_get_users_courses($userid, true, $fields, $sort); - foreach ($courses as $id=>$course) { - $context = get_context_instance(CONTEXT_COURSE, $id); - if (!has_capability($cap, $context, $userid)) { - unset($courses[$id]); - } - } - - return $courses; -} - - /** * Return a nested array showing role assignments * all relevant role capabilities for the user at * site/course_category/course levels * * We do _not_ delve deeper than courses because the number of - * overrides at the module/block levels is HUGE. + * overrides at the module/block levels can be HUGE. * - * [ra] => [/path/][]=roleid - * [rdef] => [/path/:roleid][capability]=permission - * [loaded] => array('/path', '/path') + * [ra] => [/path][roleid]=roleid + * [rdef] => [/path:roleid][capability]=permission * + * @private * @param int $userid - the id of the user - * @return array + * @return array access info array */ function get_user_access_sitewide($userid) { - global $CFG, $DB; + global $CFG, $DB, $ACCESSLIB_PRIVATE; - /* Get in 3 cheap DB queries... + /* Get in a few cheap DB queries... * - role assignments * - relevant role caps * - above and within this user's RAs * - below this user's RAs - limited to course level */ - $accessdata = array(); // named list - $accessdata['ra'] = array(); - $accessdata['rdef'] = array(); - $accessdata['loaded'] = array(); + // raparents collects paths & roles we need to walk up the parenthood to build the minimal rdef + $raparents = array(); + $accessdata = get_empty_accessdata(); - // - // Role assignments - // + // start with the default role + if (!empty($CFG->defaultuserroleid)) { + $syscontext = context_system::instance(); + $accessdata['ra'][$syscontext->path][(int)$CFG->defaultuserroleid] = (int)$CFG->defaultuserroleid; + $raparents[$CFG->defaultuserroleid][$syscontext->path] = $syscontext->path; + } + + // load the "default frontpage role" + if (!empty($CFG->defaultfrontpageroleid)) { + $frontpagecontext = context_course::instance(get_site()->id); + if ($frontpagecontext->path) { + $accessdata['ra'][$frontpagecontext->path][(int)$CFG->defaultfrontpageroleid] = (int)$CFG->defaultfrontpageroleid; + $raparents[$CFG->defaultfrontpageroleid][$frontpagecontext->path] = $frontpagecontext->path; + } + } + + // preload every assigned role at and above course context $sql = "SELECT ctx.path, ra.roleid FROM {role_assignments} ra - JOIN {context} ctx ON ctx.id=ra.contextid - WHERE ra.userid = ? AND ctx.contextlevel <= ".CONTEXT_COURSE; - $params = array($userid); - $rs = $DB->get_recordset_sql($sql, $params); + JOIN {context} ctx ON ctx.id = ra.contextid + LEFT JOIN {context} cctx + ON (cctx.contextlevel = ".CONTEXT_COURSE." AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").") + WHERE ra.userid = :userid AND cctx.id IS NULL"; - // - // raparents collects paths & roles we need to walk up - // the parenthood to build the rdef - // - $raparents = array(); - if ($rs) { - foreach ($rs as $ra) { - // RAs leafs are arrays to support multi - // role assignments... - if (!isset($accessdata['ra'][$ra->path])) { - $accessdata['ra'][$ra->path] = array(); - } - $accessdata['ra'][$ra->path][$ra->roleid] = $ra->roleid; - // Concatenate as string the whole path (all related context) - // for this role. This is damn faster than using array_merge() - // Will unique them later - if (isset($raparents[$ra->roleid])) { - $raparents[$ra->roleid] .= $ra->path; - } else { - $raparents[$ra->roleid] = $ra->path; - } - } - unset($ra); - $rs->close(); + $params = array('userid'=>$userid); + $rs = $DB->get_recordset_sql($sql, $params); + foreach ($rs as $ra) { + // RAs leafs are arrays to support multi-role assignments... + $accessdata['ra'][$ra->path][(int)$ra->roleid] = (int)$ra->roleid; + $raparents[$ra->roleid][$ra->path] = $ra->path; } + $rs->close(); - // Walk up the tree to grab all the roledefs - // of interest to our user... - // - // NOTE: we use a series of IN clauses here - which - // might explode on huge sites with very convoluted nesting of - // categories... - extremely unlikely that the number of categories - // and roletypes is so large that we hit the limits of IN() - $clauses = ''; - $cparams = array(); - foreach ($raparents as $roleid=>$strcontexts) { - $contexts = implode(',', array_unique(explode('/', trim($strcontexts, '/')))); - if ($contexts ==! '') { - if ($clauses) { - $clauses .= ' OR '; - } - $clauses .= "(roleid=? AND contextid IN ($contexts))"; - $cparams[] = $roleid; - } + if (empty($raparents)) { + return $accessdata; } - if ($clauses !== '') { - $sql = "SELECT ctx.path, rc.roleid, rc.capability, rc.permission - FROM {role_capabilities} rc - JOIN {context} ctx ON rc.contextid=ctx.id - WHERE $clauses"; + // now get overrides of interesting roles in all interesting child contexts + // hopefully we will not run out of SQL limits here, + // users would have to have very many roles above course context... + $sqls = array(); + $params = array(); - unset($clauses); - $rs = $DB->get_recordset_sql($sql, $cparams); + static $cp = 0; + foreach ($raparents as $roleid=>$paths) { + $cp++; + list($paths, $rparams) = $DB->get_in_or_equal($paths, SQL_PARAMS_NAMED, 'p'.$cp.'_'); + $params = array_merge($params, $rparams); + $params['r'.$cp] = $roleid; + $sqls[] = "(SELECT ctx.path, rc.roleid, rc.capability, rc.permission + FROM {role_capabilities} rc + JOIN {context} ctx + ON (ctx.id = rc.contextid) + LEFT JOIN {context} cctx + ON (cctx.contextlevel = ".CONTEXT_COURSE." + AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").") + JOIN {context} pctx + ON (pctx.path $paths + AND (ctx.id = pctx.id + OR ctx.path LIKE ".$DB->sql_concat('pctx.path',"'/%'")." + OR pctx.path LIKE ".$DB->sql_concat('ctx.path',"'/%'").")) + WHERE rc.roleid = :r{$cp} + AND cctx.id IS NULL)"; + } + + // fixed capability order is necessary for rdef dedupe + $rs = $DB->get_recordset_sql(implode("\nUNION\n", $sqls). "ORDER BY capability", $params); - if ($rs) { - foreach ($rs as $rd) { - $k = "{$rd->path}:{$rd->roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; - } - unset($rd); - $rs->close(); - } + foreach ($rs as $rd) { + $k = $rd->path.':'.$rd->roleid; + $accessdata['rdef'][$k][$rd->capability] = (int)$rd->permission; } + $rs->close(); - // - // Overrides for the role assignments IN SUBCONTEXTS - // (though we still do _not_ go below the course level. - // - // NOTE that the JOIN w sctx is with 3-way triangulation to - // catch overrides to the applicable role in any subcontext, based - // on the path field of the parent. - // - $sql = "SELECT sctx.path, ra.roleid, - ctx.path AS parentpath, - rco.capability, rco.permission - FROM {role_assignments} ra - JOIN {context} ctx - ON ra.contextid=ctx.id - JOIN {context} sctx - ON (sctx.path LIKE " . $DB->sql_concat('ctx.path',"'/%'"). " ) - JOIN {role_capabilities} rco - ON (rco.roleid=ra.roleid AND rco.contextid=sctx.id) - WHERE ra.userid = ? - AND ctx.contextlevel <= ".CONTEXT_COURSECAT." - AND sctx.contextlevel <= ".CONTEXT_COURSE." - ORDER BY sctx.depth, sctx.path, ra.roleid"; - $params = array($userid); - $rs = $DB->get_recordset_sql($sql, $params); - if ($rs) { - foreach ($rs as $rd) { - $k = "{$rd->path}:{$rd->roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; + // share the role definitions + foreach ($accessdata['rdef'] as $k=>$unused) { + if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) { + $ACCESSLIB_PRIVATE->rolepermissions[$k] = $accessdata['rdef'][$k]; } - unset($rd); - $rs->close(); + $accessdata['rdef_count']++; + $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k]; } + return $accessdata; } /** - * Add to the access ctrl array the data needed by a user for a given context + * Add to the access ctrl array the data needed by a user for a given course. * - * @param integer $userid the id of the user - * @param object $context needs path! - * @param array $accessdata accessdata array - * @return void + * This function injects all course related access info into the accessdata array. + * + * @private + * @param int $userid the id of the user + * @param context_course $coursecontext course context + * @param array $accessdata accessdata array (modified) + * @return void modifies $accessdata parameter */ -function load_subcontext($userid, $context, &$accessdata) { - global $CFG, $DB; +function load_course_context($userid, context_course $coursecontext, &$accessdata) { + global $DB, $CFG, $ACCESSLIB_PRIVATE; - /* Get the additional RAs and relevant rolecaps - * - role assignments - with role_caps - * - relevant role caps - * - above this user's RAs - * - below this user's RAs - limited to course level - */ + if (empty($coursecontext->path)) { + // weird, this should not happen + return; + } - $base = "/" . SYSCONTEXTID; + if (isset($accessdata['loaded'][$coursecontext->instanceid])) { + // already loaded, great! + return; + } - // - // Replace $context with the target context we will - // load. Normally, this will be a course context, but - // may be a different top-level context. - // - // We have 3 cases - // - // - Course - // - BLOCK/PERSON/USER/COURSE(sitecourse) hanging from SYSTEM - // - BLOCK/MODULE/GROUP hanging from a course - // - // For course contexts, we _already_ have the RAs - // but the cost of re-fetching is minimal so we don't care. - // - if ($context->contextlevel !== CONTEXT_COURSE - && $context->path !== "$base/{$context->id}") { - // Case BLOCK/MODULE/GROUP hanging from a course - // Assumption: the course _must_ be our parent - // If we ever see stuff nested further this needs to - // change to do 1 query over the exploded path to - // find out which one is the course - $courses = explode('/',get_course_from_path($context->path)); - $targetid = array_pop($courses); - $context = get_context_instance_by_id($targetid); + $roles = array(); - } + if (empty($userid)) { + if (!empty($CFG->notloggedinroleid)) { + $roles[$CFG->notloggedinroleid] = $CFG->notloggedinroleid; + } - // - // Role assignments in the context and below - // - $sql = "SELECT ctx.path, ra.roleid - FROM {role_assignments} ra - JOIN {context} ctx - ON ra.contextid=ctx.id - WHERE ra.userid = ? - AND (ctx.path = ? OR ctx.path LIKE ?) - ORDER BY ctx.depth, ctx.path, ra.roleid"; - $params = array($userid, $context->path, $context->path."/%"); - $rs = $DB->get_recordset_sql($sql, $params); + } else if (isguestuser($userid)) { + if ($guestrole = get_guest_role()) { + $roles[$guestrole->id] = $guestrole->id; + } - // - // Read in the RAs, preventing duplicates - // - if ($rs) { - $localroles = array(); - $lastseen = ''; + } else { + // Interesting role assignments at, above and below the course context + list($parentsaself, $params) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_'); + $params['userid'] = $userid; + $params['children'] = $coursecontext->path."/%"; + $sql = "SELECT ra.*, ctx.path + FROM {role_assignments} ra + JOIN {context} ctx ON ra.contextid = ctx.id + WHERE ra.userid = :userid AND (ctx.id $parentsaself OR ctx.path LIKE :children)"; + $rs = $DB->get_recordset_sql($sql, $params); + + // add missing role definitions foreach ($rs as $ra) { - if (!isset($accessdata['ra'][$ra->path])) { - $accessdata['ra'][$ra->path] = array(); - } - // only add if is not a repeat caused - // by capability join... - // (this check is cheaper than in_array()) - if ($lastseen !== $ra->path.':'.$ra->roleid) { - $lastseen = $ra->path.':'.$ra->roleid; - $accessdata['ra'][$ra->path][$ra->roleid] = $ra->roleid; - array_push($localroles, $ra->roleid); - } + $accessdata['ra'][$ra->path][(int)$ra->roleid] = (int)$ra->roleid; + $roles[$ra->roleid] = $ra->roleid; } $rs->close(); - } - // - // Walk up and down the tree to grab all the roledefs - // of interest to our user... - // - // NOTES - // - we use IN() but the number of roles is very limited. - // - $courseroles = aggregate_roles_from_accessdata($context, $accessdata); + // add the "default frontpage role" when on the frontpage + if (!empty($CFG->defaultfrontpageroleid)) { + $frontpagecontext = context_course::instance(get_site()->id); + if ($frontpagecontext->id == $coursecontext->id) { + $roles[$CFG->defaultfrontpageroleid] = $CFG->defaultfrontpageroleid; + } + } - // Do we have any interesting "local" roles? - $localroles = array_diff($localroles,$courseroles); // only "new" local roles - $wherelocalroles=''; - if (count($localroles)) { - // Role defs for local roles in 'higher' contexts... - $contexts = substr($context->path, 1); // kill leading slash - $contexts = str_replace('/', ',', $contexts); - $localroleids = implode(',',$localroles); - $wherelocalroles="OR (rc.roleid IN ({$localroleids}) - AND ctx.id IN ($contexts))" ; + // do not forget the default role + if (!empty($CFG->defaultuserroleid)) { + $roles[$CFG->defaultuserroleid] = $CFG->defaultuserroleid; + } } - // We will want overrides for all of them - $whereroles = ''; - if ($roleids = implode(',',array_merge($courseroles,$localroles))) { - $whereroles = "rc.roleid IN ($roleids) AND"; + if (!$roles) { + // weird, default roles must be missing... + $accessdata['loaded'][$coursecontext->instanceid] = 1; + return; } + + // now get overrides of interesting roles in all interesting contexts (this course + children + parents) + $params = array('c'=>$coursecontext->id); + list($parentsaself, $rparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_'); + $params = array_merge($params, $rparams); + list($roleids, $rparams) = $DB->get_in_or_equal($roles, SQL_PARAMS_NAMED, 'r_'); + $params = array_merge($params, $rparams); + $sql = "SELECT ctx.path, rc.roleid, rc.capability, rc.permission - FROM {role_capabilities} rc - JOIN {context} ctx - ON rc.contextid=ctx.id - WHERE ($whereroles - (ctx.id=? OR ctx.path LIKE ?)) - $wherelocalroles - ORDER BY ctx.depth ASC, ctx.path DESC, rc.roleid ASC "; - $params = array($context->id, $context->path."/%"); + FROM {role_capabilities} rc + JOIN {context} ctx + ON (ctx.id = rc.contextid) + JOIN {context} cctx + ON (cctx.id = :c + AND (ctx.id $parentsaself OR ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").")) + WHERE rc.roleid $roleids + ORDER BY rc.capability"; // fixed capability order is necessary for rdef dedupe + $rs = $DB->get_recordset_sql($sql, $params); $newrdefs = array(); - $rs = $DB->get_recordset_sql($sql, $params); foreach ($rs as $rd) { - $k = "{$rd->path}:{$rd->roleid}"; - if (!array_key_exists($k, $newrdefs)) { - $newrdefs[$k] = array(); + $k = $rd->path.':'.$rd->roleid; + if (isset($accessdata['rdef'][$k])) { + continue; } - $newrdefs[$k][$rd->capability] = $rd->permission; + $newrdefs[$k][$rd->capability] = (int)$rd->permission; } $rs->close(); - compact_rdefs($newrdefs); - foreach ($newrdefs as $key=>$value) { - $accessdata['rdef'][$key] =& $newrdefs[$key]; + // share new role definitions + foreach ($newrdefs as $k=>$unused) { + if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) { + $ACCESSLIB_PRIVATE->rolepermissions[$k] = $newrdefs[$k]; + } + $accessdata['rdef_count']++; + $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k]; } - // error_log("loaded {$context->path}"); - $accessdata['loaded'][] = $context->path; + $accessdata['loaded'][$coursecontext->instanceid] = 1; + + // we want to deduplicate the USER->access from time to time, this looks like a good place, + // because we have to do it before the end of session + dedupe_user_access(); } /** * Add to the access ctrl array the data needed by a role for a given context. * * The data is added in the rdef key. - * * This role-centric function is useful for role_switching - * and to get an overview of what a role gets under a - * given context and below... + * and temporary course roles. * - * @param integer $roleid the id of the user - * @param object $context needs path! - * @param array $accessdata accessdata array null by default + * @private + * @param int $roleid the id of the user + * @param context $context needs path! + * @param array $accessdata accessdata array (is modified) * @return array */ -function get_role_access_bycontext($roleid, $context, $accessdata = null) { - global $CFG, $DB; +function load_role_access_by_context($roleid, context $context, &$accessdata) { + global $DB, $ACCESSLIB_PRIVATE; /* Get the relevant rolecaps into rdef * - relevant role caps @@ -1358,306 +948,277 @@ function get_role_access_bycontext($roleid, $context, $accessdata = null) { * - below this ctx */ - if (is_null($accessdata)) { - $accessdata = array(); // named list - $accessdata['ra'] = array(); - $accessdata['rdef'] = array(); - $accessdata['loaded'] = array(); + if (empty($context->path)) { + // weird, this should not happen + return; } - $contexts = substr($context->path, 1); // kill leading slash - $contexts = str_replace('/', ',', $contexts); + list($parentsaself, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'pc_'); + $params['roleid'] = $roleid; + $params['childpath'] = $context->path.'/%'; - // - // Walk up and down the tree to grab all the roledefs - // of interest to our role... - // - // NOTE: we use an IN clauses here - which - // might explode on huge sites with very convoluted nesting of - // categories... - extremely unlikely that the number of nested - // categories is so large that we hit the limits of IN() - // $sql = "SELECT ctx.path, rc.capability, rc.permission FROM {role_capabilities} rc - JOIN {context} ctx - ON rc.contextid=ctx.id - WHERE rc.roleid=? AND - ( ctx.id IN ($contexts) OR - ctx.path LIKE ? ) - ORDER BY ctx.depth ASC, ctx.path DESC, rc.roleid ASC "; - $params = array($roleid, $context->path."/%"); - + JOIN {context} ctx ON (rc.contextid = ctx.id) + WHERE rc.roleid = :roleid AND (ctx.id $parentsaself OR ctx.path LIKE :childpath) + ORDER BY rc.capability"; // fixed capability order is necessary for rdef dedupe $rs = $DB->get_recordset_sql($sql, $params); + + $newrdefs = array(); foreach ($rs as $rd) { - $k = "{$rd->path}:{$roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; + $k = $rd->path.':'.$roleid; + if (isset($accessdata['rdef'][$k])) { + continue; + } + $newrdefs[$k][$rd->capability] = (int)$rd->permission; } $rs->close(); + // share new role definitions + foreach ($newrdefs as $k=>$unused) { + if (!isset($ACCESSLIB_PRIVATE->rolepermissions[$k])) { + $ACCESSLIB_PRIVATE->rolepermissions[$k] = $newrdefs[$k]; + } + $accessdata['rdef_count']++; + $accessdata['rdef'][$k] =& $ACCESSLIB_PRIVATE->rolepermissions[$k]; + } +} + +/** + * Returns empty accessdata structure. + * @return array empt accessdata + */ +function get_empty_accessdata() { + $accessdata = array(); // named list + $accessdata['ra'] = array(); + $accessdata['rdef'] = array(); + $accessdata['rdef_count'] = 0; // this bloody hack is necessary because count($array) is slooooowwww in PHP + $accessdata['rdef_lcc'] = 0; // rdef_count during the last compression + $accessdata['loaded'] = array(); // loaded course contexts + $accessdata['time'] = time(); + return $accessdata; } /** - * Load accessdata for a user into the $ACCESSLIB_PRIVATE->accessdatabyuser global - * - * Used by has_capability() - but feel free - * to call it if you are about to run a BIG - * cron run across a bazillion users. + * Get accessdata for a given user. * + * @private * @param int $userid - * @return array returns ACCESSLIB_PRIVATE->accessdatabyuser[userid] + * @param bool $preloadonly true means do not return access array + * @return array accessdata */ -function load_user_accessdata($userid) { - global $CFG, $ACCESSLIB_PRIVATE; - - $base = '/'.SYSCONTEXTID; +function get_user_accessdata($userid, $preloadonly=false) { + global $CFG, $ACCESSLIB_PRIVATE, $USER; - $accessdata = get_user_access_sitewide($userid); - $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID); - // - // provide "default role" & set 'dr' - // - if (!empty($CFG->defaultuserroleid)) { - $accessdata = get_role_access($CFG->defaultuserroleid, $accessdata); - if (!isset($accessdata['ra'][$base])) { - $accessdata['ra'][$base] = array(); + if (!empty($USER->acces['rdef']) and empty($ACCESSLIB_PRIVATE->rolepermissions)) { + // share rdef from USER session with rolepermissions cache in order to conserve memory + foreach($USER->acces['rdef'] as $k=>$v) { + $ACCESSLIB_PRIVATE->rolepermissions[$k] =& $USER->acces['rdef'][$k]; } - $accessdata['ra'][$base][$CFG->defaultuserroleid] = $CFG->defaultuserroleid; - $accessdata['dr'] = $CFG->defaultuserroleid; + $ACCESSLIB_PRIVATE->accessdatabyuser[$USER->id] = $USER->acces; } - // - // provide "default frontpage role" - // - if (!empty($CFG->defaultfrontpageroleid)) { - $base = '/'. SYSCONTEXTID .'/'. $frontpagecontext->id; - $accessdata = get_default_frontpage_role_access($CFG->defaultfrontpageroleid, $accessdata); - if (!isset($accessdata['ra'][$base])) { - $accessdata['ra'][$base] = array(); + if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) { + if (empty($userid)) { + if (!empty($CFG->notloggedinroleid)) { + $accessdata = get_role_access($CFG->notloggedinroleid); + } else { + // weird + return get_empty_accessdata(); + } + + } else if (isguestuser($userid)) { + if ($guestrole = get_guest_role()) { + $accessdata = get_role_access($guestrole->id); + } else { + //weird + return get_empty_accessdata(); + } + + } else { + $accessdata = get_user_access_sitewide($userid); // includes default role and frontpage role } - $accessdata['ra'][$base][$CFG->defaultfrontpageroleid] = $CFG->defaultfrontpageroleid; - } - // for dirty timestamps in cron - $accessdata['time'] = time(); - $ACCESSLIB_PRIVATE->accessdatabyuser[$userid] = $accessdata; - compact_rdefs($ACCESSLIB_PRIVATE->accessdatabyuser[$userid]['rdef']); + $ACCESSLIB_PRIVATE->accessdatabyuser[$userid] = $accessdata; + } - return $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; + if ($preloadonly) { + return; + } else { + return $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]; + } } /** - * Use shared copy of role definitions stored in ACCESSLIB_PRIVATE->roledefinitions; + * Try to minimise the size of $USER->access by eliminating duplicate override storage, + * this function looks for contexts with the same overrides and shares them. * - * @param array $rdefs array of role definitions in contexts + * @private + * @return void */ -function compact_rdefs(&$rdefs) { - global $ACCESSLIB_PRIVATE; +function dedupe_user_access() { + global $USER; - /* - * This is a basic sharing only, we could also - * use md5 sums of values. The main purpose is to - * reduce mem in cron jobs - many users in $ACCESSLIB_PRIVATE->accessdatabyuser array. - */ + if (CLI_SCRIPT) { + // no session in CLI --> no compression necessary + return; + } - foreach ($rdefs as $key => $value) { - if (!array_key_exists($key, $ACCESSLIB_PRIVATE->roledefinitions)) { - $ACCESSLIB_PRIVATE->roledefinitions[$key] = $rdefs[$key]; + if (empty($USER->access['rdef_count'])) { + // weird, this should not happen + return; + } + + // the rdef is growing only, we never remove stuff from it, the rdef_lcc helps us to detect new stuff in rdef + if ($USER->access['rdef_count'] - $USER->access['rdef_lcc'] > 10) { + // do not compress after each change, wait till there is more stuff to be done + return; + } + + $hashmap = array(); + foreach ($USER->access['rdef'] as $k=>$def) { + $hash = sha1(serialize($def)); + if (isset($hashmap[$hash])) { + $USER->access['rdef'][$k] =& $hashmap[$hash]; + } else { + $hashmap[$hash] =& $USER->access['rdef'][$k]; } - $rdefs[$key] =& $ACCESSLIB_PRIVATE->roledefinitions[$key]; } + + $USER->access['rdef_lcc'] = $USER->access['rdef_count']; } /** * A convenience function to completely load all the capabilities - * for the current user. This is what gets called from complete_user_login() - * for example. Call it only _after_ you've setup $USER and called - * check_enrolment_plugins(); + * for the current user. It is called from has_capability() and functions change permissions. + * + * Call it only _after_ you've setup $USER and called check_enrolment_plugins(); * @see check_enrolment_plugins() * + * @private * @return void */ function load_all_capabilities() { - global $CFG, $ACCESSLIB_PRIVATE; - - //NOTE: we can not use $USER here because it may no be linked to $_SESSION['USER'] yet! + global $USER; // roles not installed yet - we are in the middle of installation if (during_initial_install()) { return; } - $base = '/'.SYSCONTEXTID; - - if (isguestuser($_SESSION['USER'])) { - $guest = get_guest_role(); - - // Load the rdefs - $_SESSION['USER']->access = get_role_access($guest->id); - // Put the ghost enrolment in place... - $_SESSION['USER']->access['ra'][$base] = array($guest->id => $guest->id); - - - } else if (!empty($_SESSION['USER']->id)) { // can not use isloggedin() yet - - $accessdata = get_user_access_sitewide($_SESSION['USER']->id); - - // - // provide "default role" & set 'dr' - // - if (!empty($CFG->defaultuserroleid)) { - $accessdata = get_role_access($CFG->defaultuserroleid, $accessdata); - if (!isset($accessdata['ra'][$base])) { - $accessdata['ra'][$base] = array(); - } - $accessdata['ra'][$base][$CFG->defaultuserroleid] = $CFG->defaultuserroleid; - $accessdata['dr'] = $CFG->defaultuserroleid; - } - - $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID); - - // - // provide "default frontpage role" - // - if (!empty($CFG->defaultfrontpageroleid)) { - $base = '/'. SYSCONTEXTID .'/'. $frontpagecontext->id; - $accessdata = get_default_frontpage_role_access($CFG->defaultfrontpageroleid, $accessdata); - if (!isset($accessdata['ra'][$base])) { - $accessdata['ra'][$base] = array(); - } - $accessdata['ra'][$base][$CFG->defaultfrontpageroleid] = $CFG->defaultfrontpageroleid; - } - $_SESSION['USER']->access = $accessdata; - - } else if (!empty($CFG->notloggedinroleid)) { - $_SESSION['USER']->access = get_role_access($CFG->notloggedinroleid); - $_SESSION['USER']->access['ra'][$base] = array($CFG->notloggedinroleid => $CFG->notloggedinroleid); + if (!isset($USER->id)) { + // this should not happen + $USER->id = 0; } - // Timestamp to read dirty context timestamps later - $_SESSION['USER']->access['time'] = time(); - $ACCESSLIB_PRIVATE->dirtycontexts = array(); + unset($USER->access); + $USER->access = get_user_accessdata($USER->id); + + // deduplicate the overrides to minimize session size + dedupe_user_access(); // Clear to force a refresh - unset($_SESSION['USER']->mycourses); + unset($USER->mycourses); + unset($USER->enrol); } /** * A convenience function to completely reload all the capabilities * for the current user when roles have been updated in a relevant * context -- but PRESERVING switchroles and loginas. + * This function resets all accesslib and context caches. * * That is - completely transparent to the user. * - * Note: rewrites $USER->access completely. + * Note: reloads $USER->access completely. * + * @private * @return void */ function reload_all_capabilities() { - global $USER, $DB; + global $USER, $DB, $ACCESSLIB_PRIVATE; - // error_log("reloading"); // copy switchroles $sw = array(); if (isset($USER->access['rsw'])) { $sw = $USER->access['rsw']; - // error_log(print_r($sw,1)); } + accesslib_clear_all_caches(true); unset($USER->access); - unset($USER->mycourses); + $ACCESSLIB_PRIVATE->dirtycontexts = array(); // prevent dirty flags refetching on this page load_all_capabilities(); foreach ($sw as $path => $roleid) { - $context = $DB->get_record('context', array('path'=>$path)); - role_switch($roleid, $context); + if ($record = $DB->get_record('context', array('path'=>$path))) { + $context = context::instance_by_id($record->id); + role_switch($roleid, $context); + } } - } /** - * Adds a temp role to an accessdata array. - * - * Useful for the "temporary guest" access - * we grant to logged-in users. + * Adds a temp role to current USER->access array. * - * Note - assumes a course context! + * Useful for the "temporary guest" access we grant to logged-in users. * - * @param object $content + * @param context_course $coursecontext * @param int $roleid - * @param array $accessdata - * @return array Returns access data + * @return void */ -function load_temp_role($context, $roleid, array $accessdata) { - global $CFG, $DB; +function load_temp_course_role(context_course $coursecontext, $roleid) { + global $USER; - // - // Load rdefs for the role in - - // - this context - // - all the parents - // - and below - IOWs overrides... - // + //TODO: this gets removed if there are any dirty contexts, we should probably store list of these temp roles somewhere (skodak) - // turn the path into a list of context ids - $contexts = substr($context->path, 1); // kill leading slash - $contexts = str_replace('/', ',', $contexts); + if (empty($roleid)) { + debugging('invalid role specified in load_temp_course_role()'); + return; + } - $sql = "SELECT ctx.path, rc.capability, rc.permission - FROM {context} ctx - JOIN {role_capabilities} rc - ON rc.contextid=ctx.id - WHERE (ctx.id IN ($contexts) - OR ctx.path LIKE ?) - AND rc.roleid = ? - ORDER BY ctx.depth, ctx.path"; - $params = array($context->path."/%", $roleid); - $rs = $DB->get_recordset_sql($sql, $params); - foreach ($rs as $rd) { - $k = "{$rd->path}:{$roleid}"; - $accessdata['rdef'][$k][$rd->capability] = $rd->permission; + if (!isset($USER->access)) { + load_all_capabilities(); } - $rs->close(); - // - // Say we loaded everything for the course context - // - which we just did - if the user gets a proper - // RA in this session, this data will need to be reloaded, - // but that is handled by the complete accessdata reload - // - array_push($accessdata['loaded'], $context->path); + $coursecontext->reload_if_dirty(); - // - // Add the ghost RA - // - if (!isset($accessdata['ra'][$context->path])) { - $accessdata['ra'][$context->path] = array(); + if (isset($USER->access['ra'][$coursecontext->path][$roleid])) { + return; } - $accessdata['ra'][$context->path][$roleid] = $roleid; - return $accessdata; + // load course stuff first + load_course_context($USER->id, $coursecontext, $USER->access); + + $USER->access['ra'][$coursecontext->path][(int)$roleid] = (int)$roleid; + + load_role_access_by_context($roleid, $coursecontext, $USER->access); } /** - * Removes any extra guest roles from accessdata - * @param object $context - * @param array $accessdata - * @return array access data + * Removes any extra guest roles from current USER->access array. + * + * @param context_course $coursecontext + * @return void */ -function remove_temp_roles($context, array $accessdata) { +function remove_temp_course_roles(context_course $coursecontext) { global $DB, $USER; + + if (empty($USER->access['ra'][$coursecontext->path])) { + //no roles here, weird + return; + } + $sql = "SELECT DISTINCT ra.roleid AS id FROM {role_assignments} ra WHERE ra.contextid = :contextid AND ra.userid = :userid"; - $ras = $DB->get_records_sql($sql, array('contextid'=>$context->id, 'userid'=>$USER->id)); + $ras = $DB->get_records_sql($sql, array('contextid'=>$coursecontext->id, 'userid'=>$USER->id)); - if ($ras) { - $accessdata['ra'][$context->path] = array_combine(array_keys($ras), array_keys($ras)); - } else { - $accessdata['ra'][$context->path] = array(); + $USER->access['ra'][$coursecontext->path] = array(); + foreach($ras as $r) { + $USER->access['ra'][$coursecontext->path][(int)$r->id] = (int)$r->id; } - - return $accessdata; } /** @@ -1698,7 +1259,7 @@ function assign_legacy_capabilities($capability, $legacyperms) { foreach ($legacyperms as $type => $perm) { - $systemcontext = get_context_instance(CONTEXT_SYSTEM); + $systemcontext = context_system::instance(); if ($type === 'admin') { debugging('Legacy type admin in access.php was renamed to manager, please update the code.'); $type = 'manager'; @@ -1721,6 +1282,8 @@ function assign_legacy_capabilities($capability, $legacyperms) { } /** + * Verify capability risks. + * * @param object $capability a capability - a row from the capabilities table. * @return boolean whether this capability is safe - that is, whether people with the * safeoverrides capability should be allowed to change it. @@ -1729,4255 +1292,5627 @@ function is_safe_capability($capability) { return !((RISK_DATALOSS | RISK_MANAGETRUST | RISK_CONFIG | RISK_XSS | RISK_PERSONAL) & $capability->riskbitmask); } -/********************************** - * Context Manipulation functions * - **********************************/ - /** - * Context creation - internal implementation. - * - * Create a new context record for use by all roles-related stuff - * assumes that the caller has done the homework. - * - * DO NOT CALL THIS DIRECTLY, instead use {@link get_context_instance}! + * Get the local override (if any) for a given capability in a role in a context * - * @param int $contextlevel - * @param int $instanceid - * @param int $strictness - * @return object newly created context + * @param int $roleid + * @param int $contextid + * @param string $capability + * @return stdClass local capability override */ -function create_context($contextlevel, $instanceid, $strictness = IGNORE_MISSING) { - global $CFG, $DB; - - if ($contextlevel == CONTEXT_SYSTEM) { - return get_system_context(); - } - - $context = new stdClass(); - $context->contextlevel = $contextlevel; - $context->instanceid = $instanceid; - - // Define $context->path based on the parent - // context. In other words... Who is your daddy? - $basepath = '/' . SYSCONTEXTID; - $basedepth = 1; - - $result = true; - $error_message = null; - - switch ($contextlevel) { - case CONTEXT_COURSECAT: - $sql = "SELECT ctx.path, ctx.depth - FROM {context} ctx - JOIN {course_categories} cc - ON (cc.parent=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSECAT.") - WHERE cc.id=?"; - $params = array($instanceid); - if ($p = $DB->get_record_sql($sql, $params)) { - $basepath = $p->path; - $basedepth = $p->depth; - } else if ($category = $DB->get_record('course_categories', array('id'=>$instanceid), '*', $strictness)) { - if (empty($category->parent)) { - // ok - this is a top category - } else if ($parent = get_context_instance(CONTEXT_COURSECAT, $category->parent)) { - $basepath = $parent->path; - $basedepth = $parent->depth; - } else { - // wrong parent category - no big deal, this can be fixed later - $basepath = null; - $basedepth = 0; - } - } else { - // incorrect category id - $error_message = "incorrect course category id ($instanceid)"; - $result = false; - } - break; +function get_local_override($roleid, $contextid, $capability) { + global $DB; + return $DB->get_record('role_capabilities', array('roleid'=>$roleid, 'capability'=>$capability, 'contextid'=>$contextid)); +} - case CONTEXT_COURSE: - $sql = "SELECT ctx.path, ctx.depth - FROM {context} ctx - JOIN {course} c - ON (c.category=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSECAT.") - WHERE c.id=? AND c.id !=" . SITEID; - $params = array($instanceid); - if ($p = $DB->get_record_sql($sql, $params)) { - $basepath = $p->path; - $basedepth = $p->depth; - } else if ($course = $DB->get_record('course', array('id'=>$instanceid), '*', $strictness)) { - if ($course->id == SITEID) { - //ok - no parent category - } else if ($parent = get_context_instance(CONTEXT_COURSECAT, $course->category)) { - $basepath = $parent->path; - $basedepth = $parent->depth; - } else { - // wrong parent category of course - no big deal, this can be fixed later - $basepath = null; - $basedepth = 0; - } - } else if ($instanceid == SITEID) { - // no errors for missing site course during installation - return false; - } else { - // incorrect course id - $error_message = "incorrect course id ($instanceid)"; - $result = false; - } - break; +/** + * Returns context instance plus related course and cm instances + * + * @param int $contextid + * @return array of ($context, $course, $cm) + */ +function get_context_info_array($contextid) { + global $DB; - case CONTEXT_MODULE: - $sql = "SELECT ctx.path, ctx.depth - FROM {context} ctx - JOIN {course_modules} cm - ON (cm.course=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.") - WHERE cm.id=?"; - $params = array($instanceid); - if ($p = $DB->get_record_sql($sql, $params)) { - $basepath = $p->path; - $basedepth = $p->depth; - } else if ($cm = $DB->get_record('course_modules', array('id'=>$instanceid), '*', $strictness)) { - if ($parent = get_context_instance(CONTEXT_COURSE, $cm->course, $strictness)) { - $basepath = $parent->path; - $basedepth = $parent->depth; - } else { - // course does not exist - modules can not exist without a course - $error_message = "course does not exist ($cm->course) - modules can not exist without a course"; - $result = false; - } - } else { - // cm does not exist - $error_message = "cm with id $instanceid does not exist"; - $result = false; - } - break; + $context = context::instance_by_id($contextid, MUST_EXIST); + $course = null; + $cm = null; - case CONTEXT_BLOCK: - $sql = "SELECT ctx.path, ctx.depth - FROM {context} ctx - JOIN {block_instances} bi ON (bi.parentcontextid=ctx.id) - WHERE bi.id = ?"; - $params = array($instanceid, CONTEXT_COURSE); - if ($p = $DB->get_record_sql($sql, $params, '*', $strictness)) { - $basepath = $p->path; - $basedepth = $p->depth; - } else { - // block does not exist - $error_message = 'block or parent context does not exist'; - $result = false; - } - break; - case CONTEXT_USER: - // default to basepath - break; - } + if ($context->contextlevel == CONTEXT_COURSE) { + $course = $DB->get_record('course', array('id'=>$context->instanceid), '*', MUST_EXIST); - // if grandparents unknown, maybe rebuild_context_path() will solve it later - if ($basedepth != 0) { - $context->depth = $basedepth+1; - } + } else if ($context->contextlevel == CONTEXT_MODULE) { + $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); + $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); - if (!$result) { - debugging('Error: could not insert new context level "'. - s($contextlevel).'", instance "'. - s($instanceid).'". ' . $error_message); + } else if ($context->contextlevel == CONTEXT_BLOCK) { + $parent = $context->get_parent_context(); - return false; + if ($parent->contextlevel == CONTEXT_COURSE) { + $course = $DB->get_record('course', array('id'=>$parent->instanceid), '*', MUST_EXIST); + } else if ($parent->contextlevel == CONTEXT_MODULE) { + $cm = get_coursemodule_from_id('', $parent->instanceid, 0, false, MUST_EXIST); + $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); + } } - $id = $DB->insert_record('context', $context); - // can't set the full path till we know the id! - if ($basedepth != 0 and !empty($basepath)) { - $DB->set_field('context', 'path', $basepath.'/'. $id, array('id'=>$id)); - } - return get_context_instance_by_id($id); + return array($context, $course, $cm); } /** - * Returns system context or null if can not be created yet. + * Function that creates a role * - * @param bool $cache use caching - * @return mixed system context or null + * @param string $name role name + * @param string $shortname role short name + * @param string $description role description + * @param string $archetype + * @return int id or dml_exception */ -function get_system_context($cache = true) { - global $DB, $ACCESSLIB_PRIVATE; - if ($cache and defined('SYSCONTEXTID')) { - if (is_null($ACCESSLIB_PRIVATE->systemcontext)) { - $ACCESSLIB_PRIVATE->systemcontext = new stdClass(); - $ACCESSLIB_PRIVATE->systemcontext->id = SYSCONTEXTID; - $ACCESSLIB_PRIVATE->systemcontext->contextlevel = CONTEXT_SYSTEM; - $ACCESSLIB_PRIVATE->systemcontext->instanceid = 0; - $ACCESSLIB_PRIVATE->systemcontext->path = '/'.SYSCONTEXTID; - $ACCESSLIB_PRIVATE->systemcontext->depth = 1; - } - return $ACCESSLIB_PRIVATE->systemcontext; - } - try { - $context = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM)); - } catch (dml_exception $e) { - //table does not exist yet, sorry - return null; - } - - if (!$context) { - $context = new stdClass(); - $context->contextlevel = CONTEXT_SYSTEM; - $context->instanceid = 0; - $context->depth = 1; - $context->path = null; //not known before insert +function create_role($name, $shortname, $description, $archetype = '') { + global $DB; - try { - $context->id = $DB->insert_record('context', $context); - } catch (dml_exception $e) { - // can not create context yet, sorry - return null; - } + if (strpos($archetype, 'moodle/legacy:') !== false) { + throw new coding_exception('Use new role archetype parameter in create_role() instead of old legacy capabilities.'); } - if (!isset($context->depth) or $context->depth != 1 or $context->instanceid != 0 or $context->path != '/'.$context->id) { - $context->instanceid = 0; - $context->path = '/'.$context->id; - $context->depth = 1; - $DB->update_record('context', $context); + // verify role archetype actually exists + $archetypes = get_role_archetypes(); + if (empty($archetypes[$archetype])) { + $archetype = ''; } - if (!defined('SYSCONTEXTID')) { - define('SYSCONTEXTID', $context->id); + // Insert the role record. + $role = new stdClass(); + $role->name = $name; + $role->shortname = $shortname; + $role->description = $description; + $role->archetype = $archetype; + + //find free sortorder number + $role->sortorder = $DB->get_field('role', 'MAX(sortorder) + 1', array()); + if (empty($role->sortorder)) { + $role->sortorder = 1; } + $id = $DB->insert_record('role', $role); - $ACCESSLIB_PRIVATE->systemcontext = $context; - return $ACCESSLIB_PRIVATE->systemcontext; + return $id; } /** - * Remove a context record and any dependent entries, - * removes context from static context cache too + * Function that deletes a role and cleanups up after it * - * @param int $level - * @param int $instanceid - * @param bool $deleterecord false means keep record for now - * @return bool returns true or throws an exception + * @param int $roleid id of role to delete + * @return bool always true */ -function delete_context($contextlevel, $instanceid, $deleterecord = true) { - global $DB, $ACCESSLIB_PRIVATE, $CFG; - - // do not use get_context_instance(), because the related object might not exist, - // or the context does not exist yet and it would be created now - if ($context = $DB->get_record('context', array('contextlevel'=>$contextlevel, 'instanceid'=>$instanceid))) { - // delete these first because they might fetch the context and try to recreate it! - blocks_delete_all_for_context($context->id); - filter_delete_all_for_context($context->id); - - require_once($CFG->dirroot . '/comment/lib.php'); - comment::delete_comments(array('contextid'=>$context->id)); +function delete_role($roleid) { + global $DB; - require_once($CFG->dirroot.'/rating/lib.php'); - $delopt = new stdclass(); - $delopt->contextid = $context->id; - $rm = new rating_manager(); - $rm->delete_ratings($delopt); + // first unssign all users + role_unassign_all(array('roleid'=>$roleid)); - // delete all files attached to this context - $fs = get_file_storage(); - $fs->delete_area_files($context->id); + // cleanup all references to this role, ignore errors + $DB->delete_records('role_capabilities', array('roleid'=>$roleid)); + $DB->delete_records('role_allow_assign', array('roleid'=>$roleid)); + $DB->delete_records('role_allow_assign', array('allowassign'=>$roleid)); + $DB->delete_records('role_allow_override', array('roleid'=>$roleid)); + $DB->delete_records('role_allow_override', array('allowoverride'=>$roleid)); + $DB->delete_records('role_names', array('roleid'=>$roleid)); + $DB->delete_records('role_context_levels', array('roleid'=>$roleid)); - // now delete stuff from role related tables, role_unassign_all - // and unenrol should be called earlier to do proper cleanup - $DB->delete_records('role_assignments', array('contextid'=>$context->id)); - $DB->delete_records('role_capabilities', array('contextid'=>$context->id)); - $DB->delete_records('role_names', array('contextid'=>$context->id)); + // finally delete the role itself + // get this before the name is gone for logging + $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); - // and finally it is time to delete the context record if requested - if ($deleterecord) { - $DB->delete_records('context', array('id'=>$context->id)); - // purge static context cache if entry present - $ACCESSLIB_PRIVATE->contexcache->remove($context); - } + $DB->delete_records('role', array('id'=>$roleid)); - // do not mark dirty contexts if parents unknown - if (!is_null($context->path) and $context->depth > 0) { - mark_context_dirty($context->path); - } - } + add_to_log(SITEID, 'role', 'delete', 'admin/roles/action=delete&roleid='.$roleid, $rolename, ''); return true; } /** - * Precreates all contexts including all parents + * Function to write context specific overrides, or default capabilities. * - * @param int $contextlevel empty means all - * @param bool $buildpaths update paths and depths - * @return void + * NOTE: use $context->mark_dirty() after this + * + * @param string $capability string name + * @param int $permission CAP_ constants + * @param int $roleid role id + * @param int|context $contextid context id + * @param bool $overwrite + * @return bool always true or exception */ -function create_contexts($contextlevel = null, $buildpaths = true) { - global $DB; - - //make sure system context exists - $syscontext = get_system_context(false); - - if (empty($contextlevel) or $contextlevel == CONTEXT_COURSECAT - or $contextlevel == CONTEXT_COURSE - or $contextlevel == CONTEXT_MODULE - or $contextlevel == CONTEXT_BLOCK) { - $sql = "INSERT INTO {context} (contextlevel, instanceid) - SELECT ".CONTEXT_COURSECAT.", cc.id - FROM {course}_categories cc - WHERE NOT EXISTS (SELECT 'x' - FROM {context} cx - WHERE cc.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSECAT.")"; - $DB->execute($sql); - - } - - if (empty($contextlevel) or $contextlevel == CONTEXT_COURSE - or $contextlevel == CONTEXT_MODULE - or $contextlevel == CONTEXT_BLOCK) { - $sql = "INSERT INTO {context} (contextlevel, instanceid) - SELECT ".CONTEXT_COURSE.", c.id - FROM {course} c - WHERE NOT EXISTS (SELECT 'x' - FROM {context} cx - WHERE c.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSE.")"; - $DB->execute($sql); +function assign_capability($capability, $permission, $roleid, $contextid, $overwrite = false) { + global $USER, $DB; + if ($contextid instanceof context) { + $context = $contextid; + } else { + $context = context::instance_by_id($contextid); } - if (empty($contextlevel) or $contextlevel == CONTEXT_MODULE - or $contextlevel == CONTEXT_BLOCK) { - $sql = "INSERT INTO {context} (contextlevel, instanceid) - SELECT ".CONTEXT_MODULE.", cm.id - FROM {course}_modules cm - WHERE NOT EXISTS (SELECT 'x' - FROM {context} cx - WHERE cm.id = cx.instanceid AND cx.contextlevel=".CONTEXT_MODULE.")"; - $DB->execute($sql); + if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set + unassign_capability($capability, $roleid, $context->id); + return true; } - if (empty($contextlevel) or $contextlevel == CONTEXT_USER - or $contextlevel == CONTEXT_BLOCK) { - $sql = "INSERT INTO {context} (contextlevel, instanceid) - SELECT ".CONTEXT_USER.", u.id - FROM {user} u - WHERE u.deleted=0 - AND NOT EXISTS (SELECT 'x' - FROM {context} cx - WHERE u.id = cx.instanceid AND cx.contextlevel=".CONTEXT_USER.")"; - $DB->execute($sql); + $existing = $DB->get_record('role_capabilities', array('contextid'=>$context->id, 'roleid'=>$roleid, 'capability'=>$capability)); + if ($existing and !$overwrite) { // We want to keep whatever is there already + return true; } - if (empty($contextlevel) or $contextlevel == CONTEXT_BLOCK) { - $sql = "INSERT INTO {context} (contextlevel, instanceid) - SELECT ".CONTEXT_BLOCK.", bi.id - FROM {block_instances} bi - WHERE NOT EXISTS (SELECT 'x' - FROM {context} cx - WHERE bi.id = cx.instanceid AND cx.contextlevel=".CONTEXT_BLOCK.")"; - $DB->execute($sql); - } + $cap = new stdClass(); + $cap->contextid = $context->id; + $cap->roleid = $roleid; + $cap->capability = $capability; + $cap->permission = $permission; + $cap->timemodified = time(); + $cap->modifierid = empty($USER->id) ? 0 : $USER->id; - if ($buildpaths) { - build_context_path(false); + if ($existing) { + $cap->id = $existing->id; + $DB->update_record('role_capabilities', $cap); + } else { + if ($DB->record_exists('context', array('id'=>$context->id))) { + $DB->insert_record('role_capabilities', $cap); + } } + return true; } /** - * Remove stale context records + * Unassign a capability from a role. * - * @return bool + * NOTE: use $context->mark_dirty() after this + * + * @param string $capability the name of the capability + * @param int $roleid the role id + * @param int|context $contextid null means all contexts + * @return boolean true or exception */ -function cleanup_contexts() { +function unassign_capability($capability, $roleid, $contextid = null) { global $DB; - $sql = " SELECT c.contextlevel, - c.instanceid AS instanceid - FROM {context} c - LEFT OUTER JOIN {course}_categories t - ON c.instanceid = t.id - WHERE t.id IS NULL AND c.contextlevel = ".CONTEXT_COURSECAT." - UNION - SELECT c.contextlevel, - c.instanceid - FROM {context} c - LEFT OUTER JOIN {course} t - ON c.instanceid = t.id - WHERE t.id IS NULL AND c.contextlevel = ".CONTEXT_COURSE." - UNION - SELECT c.contextlevel, - c.instanceid - FROM {context} c - LEFT OUTER JOIN {course}_modules t - ON c.instanceid = t.id - WHERE t.id IS NULL AND c.contextlevel = ".CONTEXT_MODULE." - UNION - SELECT c.contextlevel, - c.instanceid - FROM {context} c - LEFT OUTER JOIN {user} t - ON c.instanceid = t.id - WHERE t.id IS NULL AND c.contextlevel = ".CONTEXT_USER." - UNION - SELECT c.contextlevel, - c.instanceid - FROM {context} c - LEFT OUTER JOIN {block_instances} t - ON c.instanceid = t.id - WHERE t.id IS NULL AND c.contextlevel = ".CONTEXT_BLOCK." - "; - - // transactions used only for performance reasons here - $transaction = $DB->start_delegated_transaction(); - - $rs = $DB->get_recordset_sql($sql); - foreach ($rs as $ctx) { - delete_context($ctx->contextlevel, $ctx->instanceid); + if (!empty($contextid)) { + if ($contextid instanceof context) { + $context = $contextid; + } else { + $context = context::instance_by_id($contextid); + } + // delete from context rel, if this is the last override in this context + $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid, 'contextid'=>$context->id)); + } else { + $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid)); } - $rs->close(); - - $transaction->allow_commit(); return true; } /** - * Preloads all contexts relating to a course: course, modules. Block contexts - * are no longer loaded here. The contexts for all the blocks on the current - * page are now efficiently loaded by {@link block_manager::load_blocks()}. + * Get the roles that have a given capability assigned to it * - * @param int $courseid Course ID - * @return void + * This function does not resolve the actual permission of the capability. + * It just checks for permissions and overrides. + * Use get_roles_with_cap_in_context() if resolution is required. + * + * @param string $capability - capability name (string) + * @param string $permission - optional, the permission defined for this capability + * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT. Defaults to null which means any. + * @param stdClass $context, null means any + * @return array of role records */ -function preload_course_contexts($courseid) { - global $DB, $ACCESSLIB_PRIVATE; +function get_roles_with_capability($capability, $permission = null, $context = null) { + global $DB; - // Users can call this multiple times without doing any harm - global $ACCESSLIB_PRIVATE; - if (array_key_exists($courseid, $ACCESSLIB_PRIVATE->preloadedcourses)) { - return; + if ($context) { + $contexts = $context->get_parent_context_ids(true); + list($insql, $params) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED, 'ctx'); + $contextsql = "AND rc.contextid $insql"; + } else { + $params = array(); + $contextsql = ''; } - $params = array($courseid, $courseid, $courseid); - $sql = "SELECT x.instanceid, x.id, x.contextlevel, x.path, x.depth - FROM {course_modules} cm - JOIN {context} x ON x.instanceid=cm.id - WHERE cm.course=? AND x.contextlevel=".CONTEXT_MODULE." - - UNION ALL - - SELECT x.instanceid, x.id, x.contextlevel, x.path, x.depth - FROM {context} x - WHERE x.instanceid=? AND x.contextlevel=".CONTEXT_COURSE.""; - - $rs = $DB->get_recordset_sql($sql, $params); - foreach($rs as $context) { - $ACCESSLIB_PRIVATE->contexcache->add($context); + if ($permission) { + $permissionsql = " AND rc.permission = :permission"; + $params['permission'] = $permission; + } else { + $permissionsql = ''; } - $rs->close(); - $ACCESSLIB_PRIVATE->preloadedcourses[$courseid] = true; + + $sql = "SELECT r.* + FROM {role} r + WHERE r.id IN (SELECT rc.roleid + FROM {role_capabilities} rc + WHERE rc.capability = :capname + $contextsql + $permissionsql)"; + $params['capname'] = $capability; + + + return $DB->get_records_sql($sql, $params); } + /** - * Get the context instance as an object. This function will create the - * context instance if it does not exist yet. - * - * @todo Remove code branch from previous fix MDL-9016 which is no longer needed + * This function makes a role-assignment (a role for a user in a particular context) * - * @param integer $level The context level, for example CONTEXT_COURSE, or CONTEXT_MODULE. - * @param integer $instance The instance id. For $level = CONTEXT_COURSE, this would be $course->id, - * for $level = CONTEXT_MODULE, this would be $cm->id. And so on. Defaults to 0 - * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found; - * MUST_EXIST means throw exception if no record or multiple records found - * @return object The context object. + * @param int $roleid the role of the id + * @param int $userid userid + * @param int|context $contextid id of the context + * @param string $component example 'enrol_ldap', defaults to '' which means manual assignment, + * @param int $itemid id of enrolment/auth plugin + * @param string $timemodified defaults to current time + * @return int new/existing id of the assignment */ -function get_context_instance($contextlevel, $instance = 0, $strictness = IGNORE_MISSING) { - global $DB, $ACCESSLIB_PRIVATE; - static $allowed_contexts = array(CONTEXT_SYSTEM, CONTEXT_USER, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_BLOCK); +function role_assign($roleid, $userid, $contextid, $component = '', $itemid = 0, $timemodified = '') { + global $USER, $DB; -/// System context has special cache - if ($contextlevel == CONTEXT_SYSTEM) { - return get_system_context(); + // first of all detect if somebody is using old style parameters + if ($contextid === 0 or is_numeric($component)) { + throw new coding_exception('Invalid call to role_assign(), code needs to be updated to use new order of parameters'); } -/// check allowed context levels - if (!in_array($contextlevel, $allowed_contexts)) { - // fatal error, code must be fixed - probably typo or switched parameters - print_error('invalidcourselevel'); + // now validate all parameters + if (empty($roleid)) { + throw new coding_exception('Invalid call to role_assign(), roleid can not be empty'); } - // Various operations rely on context cache - $cache = $ACCESSLIB_PRIVATE->contexcache; + if (empty($userid)) { + throw new coding_exception('Invalid call to role_assign(), userid can not be empty'); + } - if (!is_array($instance)) { - /// Check the cache - $context = $cache->get($contextlevel, $instance); - if ($context) { - return $context; + if ($itemid) { + if (strpos($component, '_') === false) { + throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as"enrol_" when itemid specified', 'component:'.$component); } - - /// Get it from the database, or create it - if (!$context = $DB->get_record('context', array('contextlevel'=>$contextlevel, 'instanceid'=>$instance))) { - $context = create_context($contextlevel, $instance, $strictness); + } else { + $itemid = 0; + if ($component !== '' and strpos($component, '_') === false) { + throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component); } + } - /// Only add to cache if context isn't empty. - if (!empty($context)) { - $cache->add($context); - } + if (!$DB->record_exists('user', array('id'=>$userid, 'deleted'=>0))) { + throw new coding_exception('User ID does not exist or is deleted!', 'userid:'.$userid); + } - return $context; + if ($contextid instanceof context) { + $context = $contextid; + } else { + $context = context::instance_by_id($contextid, MUST_EXIST); } + if (!$timemodified) { + $timemodified = time(); + } -/// ok, somebody wants to load several contexts to save some db queries ;-) - $instances = $instance; - $result = array(); +/// Check for existing entry + $ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id'); - foreach ($instances as $key=>$instance) { - /// Check the cache first - if ($context = $cache->get($contextlevel, $instance)) { // Already cached - $result[$instance] = $context; - unset($instances[$key]); - continue; + if ($ras) { + // role already assigned - this should not happen + if (count($ras) > 1) { + // very weird - remove all duplicates! + $ra = array_shift($ras); + foreach ($ras as $r) { + $DB->delete_records('role_assignments', array('id'=>$r->id)); + } + } else { + $ra = reset($ras); } - } - if ($instances) { - list($instanceids, $params) = $DB->get_in_or_equal($instances, SQL_PARAMS_QM); - array_unshift($params, $contextlevel); - $sql = "SELECT instanceid, id, contextlevel, path, depth - FROM {context} - WHERE contextlevel=? AND instanceid $instanceids"; + // actually there is no need to update, reset anything or trigger any event, so just return + return $ra->id; + } - if (!$contexts = $DB->get_records_sql($sql, $params)) { - $contexts = array(); - } + // Create a new entry + $ra = new stdClass(); + $ra->roleid = $roleid; + $ra->contextid = $context->id; + $ra->userid = $userid; + $ra->component = $component; + $ra->itemid = $itemid; + $ra->timemodified = $timemodified; + $ra->modifierid = empty($USER->id) ? 0 : $USER->id; - foreach ($instances as $instance) { - if (isset($contexts[$instance])) { - $context = $contexts[$instance]; - } else { - $context = create_context($contextlevel, $instance); - } + $ra->id = $DB->insert_record('role_assignments', $ra); - if (!empty($context)) { - $cache->add($context); - } + // mark context as dirty - again expensive, but needed + $context->mark_dirty(); - $result[$instance] = $context; - } + if (!empty($USER->id) && $USER->id == $userid) { + // If the user is the current user, then do full reload of capabilities too. + reload_all_capabilities(); } - return $result; -} + events_trigger('role_assigned', $ra); + return $ra->id; +} /** - * Get a context instance as an object, from a given context id. + * Removes one role assignment * - * @param int $id context id - * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found; - * MUST_EXIST means throw exception if no record or multiple records found - * @return stdClass|bool the context object or false if not found. + * @param int $roleid + * @param int $userid + * @param int|context $contextid + * @param string $component + * @param int $itemid + * @return void */ -function get_context_instance_by_id($id, $strictness = IGNORE_MISSING) { - global $DB, $ACCESSLIB_PRIVATE; - - if ($id == SYSCONTEXTID) { - return get_system_context(); - } - - $cache = $ACCESSLIB_PRIVATE->contexcache; - if ($context = $cache->get_by_id($id)) { - return $context; +function role_unassign($roleid, $userid, $contextid, $component = '', $itemid = 0) { + // first make sure the params make sense + if ($roleid == 0 or $userid == 0 or $contextid == 0) { + throw new coding_exception('Invalid call to role_unassign(), please use role_unassign_all() when removing multiple role assignments'); } - if ($context = $DB->get_record('context', array('id'=>$id), '*', $strictness)) { - $cache->add($context); - return $context; + if ($itemid) { + if (strpos($component, '_') === false) { + throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as "enrol_" when itemid specified', 'component:'.$component); + } + } else { + $itemid = 0; + if ($component !== '' and strpos($component, '_') === false) { + throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component); + } } - return false; + role_unassign_all(array('roleid'=>$roleid, 'userid'=>$userid, 'contextid'=>$contextid, 'component'=>$component, 'itemid'=>$itemid), false, false); } - /** - * Get the local override (if any) for a given capability in a role in a context + * Removes multiple role assignments, parameters may contain: + * 'roleid', 'userid', 'contextid', 'component', 'enrolid'. * - * @param int $roleid - * @param int $contextid - * @param string $capability - */ -function get_local_override($roleid, $contextid, $capability) { - global $DB; - return $DB->get_record('role_capabilities', array('roleid'=>$roleid, 'capability'=>$capability, 'contextid'=>$contextid)); -} - -/** - * Returns context instance plus related course and cm instances - * @param int $contextid - * @return array of ($context, $course, $cm) + * @param array $params role assignment parameters + * @param bool $subcontexts unassign in subcontexts too + * @param bool $includemanual include manual role assignments too + * @return void */ -function get_context_info_array($contextid) { - global $DB; - - $context = get_context_instance_by_id($contextid, MUST_EXIST); - $course = null; - $cm = null; - - if ($context->contextlevel == CONTEXT_COURSE) { - $course = $DB->get_record('course', array('id'=>$context->instanceid), '*', MUST_EXIST); - - } else if ($context->contextlevel == CONTEXT_MODULE) { - $cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); - $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); +function role_unassign_all(array $params, $subcontexts = false, $includemanual = false) { + global $USER, $CFG, $DB; - } else if ($context->contextlevel == CONTEXT_BLOCK) { - $parentcontexts = get_parent_contexts($context, false); - $parent = reset($parentcontexts); - $parent = get_context_instance_by_id($parent); + if (!$params) { + throw new coding_exception('Missing parameters in role_unsassign_all() call'); + } - if ($parent->contextlevel == CONTEXT_COURSE) { - $course = $DB->get_record('course', array('id'=>$parent->instanceid), '*', MUST_EXIST); - } else if ($parent->contextlevel == CONTEXT_MODULE) { - $cm = get_coursemodule_from_id('', $parent->instanceid, 0, false, MUST_EXIST); - $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); + $allowed = array('roleid', 'userid', 'contextid', 'component', 'itemid'); + foreach ($params as $key=>$value) { + if (!in_array($key, $allowed)) { + throw new coding_exception('Unknown role_unsassign_all() parameter key', 'key:'.$key); } } - return array($context, $course, $cm); -} - -/** - * Returns current course id or null if outside of course based on context parameter. - * @param object $context - * @return int|bool related course id or false - */ -function get_courseid_from_context($context) { - if (empty($context->contextlevel)) { - debugging('Invalid context object specified in get_courseid_from_context() call'); - return false; + if (isset($params['component']) and $params['component'] !== '' and strpos($params['component'], '_') === false) { + throw new coding_exception('Invalid component paramter in role_unsassign_all() call', 'component:'.$params['component']); } - if ($context->contextlevel == CONTEXT_COURSE) { - return $context->instanceid; + + if ($includemanual) { + if (!isset($params['component']) or $params['component'] === '') { + throw new coding_exception('include manual parameter requires component parameter in role_unsassign_all() call'); + } } - if ($context->contextlevel < CONTEXT_COURSE) { - return false; + if ($subcontexts) { + if (empty($params['contextid'])) { + throw new coding_exception('subcontexts paramtere requires component parameter in role_unsassign_all() call'); + } } - if ($context->contextlevel == CONTEXT_MODULE) { - $parentcontexts = get_parent_contexts($context, false); - $parent = reset($parentcontexts); - $parent = get_context_instance_by_id($parent); - return $parent->instanceid; + $ras = $DB->get_records('role_assignments', $params); + foreach($ras as $ra) { + $DB->delete_records('role_assignments', array('id'=>$ra->id)); + if ($context = context::instance_by_id($ra->contextid, IGNORE_MISSING)) { + // this is a bit expensive but necessary + $context->mark_dirty(); + /// If the user is the current user, then do full reload of capabilities too. + if (!empty($USER->id) && $USER->id == $ra->userid) { + reload_all_capabilities(); + } + } + events_trigger('role_unassigned', $ra); } + unset($ras); + + // process subcontexts + if ($subcontexts and $context = context::instance_by_id($params['contextid'], IGNORE_MISSING)) { + if ($params['contextid'] instanceof context) { + $context = $params['contextid']; + } else { + $context = context::instance_by_id($params['contextid'], IGNORE_MISSING); + } - if ($context->contextlevel == CONTEXT_BLOCK) { - $parentcontexts = get_parent_contexts($context, false); - $parent = reset($parentcontexts); - return get_courseid_from_context(get_context_instance_by_id($parent)); + if ($context) { + $contexts = $context->get_child_contexts(); + $mparams = $params; + foreach($contexts as $context) { + $mparams['contextid'] = $context->id; + $ras = $DB->get_records('role_assignments', $mparams); + foreach($ras as $ra) { + $DB->delete_records('role_assignments', array('id'=>$ra->id)); + // this is a bit expensive but necessary + $context->mark_dirty(); + /// If the user is the current user, then do full reload of capabilities too. + if (!empty($USER->id) && $USER->id == $ra->userid) { + reload_all_capabilities(); + } + events_trigger('role_unassigned', $ra); + } + } + } } - return false; + // do this once more for all manual role assignments + if ($includemanual) { + $params['component'] = ''; + role_unassign_all($params, $subcontexts, false); + } } +/** + * Determines if a user is currently logged in + * + * @return bool + */ +function isloggedin() { + global $USER; -////////////////////////////////////// -// DB TABLE RELATED FUNCTIONS // -////////////////////////////////////// + return (!empty($USER->id)); +} /** - * function that creates a role + * Determines if a user is logged in as real guest user with username 'guest'. * - * @param string $name role name - * @param string $shortname role short name - * @param string $description role description - * @param string $archetype - * @return int id or dml_exception + * @param int|object $user mixed user object or id, $USER if not specified + * @return bool true if user is the real guest user, false if not logged in or other user */ -function create_role($name, $shortname, $description, $archetype = '') { - global $DB; +function isguestuser($user = null) { + global $USER, $DB, $CFG; - if (strpos($archetype, 'moodle/legacy:') !== false) { - throw new coding_exception('Use new role archetype parameter in create_role() instead of old legacy capabilities.'); + // make sure we have the user id cached in config table, because we are going to use it a lot + if (empty($CFG->siteguest)) { + if (!$guestid = $DB->get_field('user', 'id', array('username'=>'guest', 'mnethostid'=>$CFG->mnet_localhost_id))) { + // guest does not exist yet, weird + return false; + } + set_config('siteguest', $guestid); } - - // verify role archetype actually exists - $archetypes = get_role_archetypes(); - if (empty($archetypes[$archetype])) { - $archetype = ''; + if ($user === null) { + $user = $USER; } - // Get the system context. - $context = get_context_instance(CONTEXT_SYSTEM); + if ($user === null) { + // happens when setting the $USER + return false; - // Insert the role record. - $role = new stdClass(); - $role->name = $name; - $role->shortname = $shortname; - $role->description = $description; - $role->archetype = $archetype; + } else if (is_numeric($user)) { + return ($CFG->siteguest == $user); - //find free sortorder number - $role->sortorder = $DB->get_field('role', 'MAX(sortorder) + 1', array()); - if (empty($role->sortorder)) { - $role->sortorder = 1; - } - $id = $DB->insert_record('role', $role); + } else if (is_object($user)) { + if (empty($user->id)) { + return false; // not logged in means is not be guest + } else { + return ($CFG->siteguest == $user->id); + } - return $id; + } else { + throw new coding_exception('Invalid user parameter supplied for isguestuser() function!'); + } } /** - * Function that deletes a role and cleanups up after it + * Does user have a (temporary or real) guest access to course? * - * @param int $roleid id of role to delete - * @return bool always true + * @param context $context + * @param stdClass|int $user + * @return bool */ -function delete_role($roleid) { - global $CFG, $DB; +function is_guest(context $context, $user = null) { + global $USER; - // first unssign all users - role_unassign_all(array('roleid'=>$roleid)); + // first find the course context + $coursecontext = $context->get_course_context(); - // cleanup all references to this role, ignore errors - $DB->delete_records('role_capabilities', array('roleid'=>$roleid)); - $DB->delete_records('role_allow_assign', array('roleid'=>$roleid)); - $DB->delete_records('role_allow_assign', array('allowassign'=>$roleid)); - $DB->delete_records('role_allow_override', array('roleid'=>$roleid)); - $DB->delete_records('role_allow_override', array('allowoverride'=>$roleid)); - $DB->delete_records('role_names', array('roleid'=>$roleid)); - $DB->delete_records('role_context_levels', array('roleid'=>$roleid)); + // make sure there is a real user specified + if ($user === null) { + $userid = isset($USER->id) ? $USER->id : 0; + } else { + $userid = is_object($user) ? $user->id : $user; + } - // finally delete the role itself - // get this before the name is gone for logging - $rolename = $DB->get_field('role', 'name', array('id'=>$roleid)); + if (isguestuser($userid)) { + // can not inspect or be enrolled + return true; + } - $DB->delete_records('role', array('id'=>$roleid)); + if (has_capability('moodle/course:view', $coursecontext, $user)) { + // viewing users appear out of nowhere, they are neither guests nor participants + return false; + } - add_to_log(SITEID, 'role', 'delete', 'admin/roles/action=delete&roleid='.$roleid, $rolename, ''); + // consider only real active enrolments here + if (is_enrolled($coursecontext, $user, '', true)) { + return false; + } return true; } /** - * Function to write context specific overrides, or default capabilities. + * Returns true if the user has moodle/course:view capability in the course, + * this is intended for admins, managers (aka small admins), inspectors, etc. * - * @param string $capability string name - * @param int $permission CAP_ constants - * @param int $roleid role id - * @param int $contextid context id - * @param bool $overwrite - * @return bool always true or exception + * @param context $context + * @param int|stdClass $user, if null $USER is used + * @param string $withcapability extra capability name + * @return bool */ -function assign_capability($capability, $permission, $roleid, $contextid, $overwrite = false) { - global $USER, $DB; +function is_viewing(context $context, $user = null, $withcapability = '') { + // first find the course context + $coursecontext = $context->get_course_context(); - if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set - unassign_capability($capability, $roleid, $contextid); - return true; + if (isguestuser($user)) { + // can not inspect + return false; } - $existing = $DB->get_record('role_capabilities', array('contextid'=>$contextid, 'roleid'=>$roleid, 'capability'=>$capability)); - - if ($existing and !$overwrite) { // We want to keep whatever is there already - return true; + if (!has_capability('moodle/course:view', $coursecontext, $user)) { + // admins are allowed to inspect courses + return false; } - $cap = new stdClass(); - $cap->contextid = $contextid; - $cap->roleid = $roleid; - $cap->capability = $capability; - $cap->permission = $permission; - $cap->timemodified = time(); - $cap->modifierid = empty($USER->id) ? 0 : $USER->id; - - if ($existing) { - $cap->id = $existing->id; - $DB->update_record('role_capabilities', $cap); - } else { - $c = $DB->get_record('context', array('id'=>$contextid)); - $DB->insert_record('role_capabilities', $cap); + if ($withcapability and !has_capability($withcapability, $context, $user)) { + // site admins always have the capability, but the enrolment above blocks + return false; } + return true; } /** - * Unassign a capability from a role. + * Returns true if user is enrolled (is participating) in course + * this is intended for students and teachers. * - * @param string $capability the name of the capability - * @param int $roleid the role id - * @param int $contextid null means all contexts - * @return boolean success or failure + * @param context $context + * @param int|stdClass $user, if null $USER is used, otherwise user object or id expected + * @param string $withcapability extra capability name + * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions + * @return bool */ -function unassign_capability($capability, $roleid, $contextid = null) { - global $DB; +function is_enrolled(context $context, $user = null, $withcapability = '', $onlyactive = false) { + global $USER, $DB; - if (!empty($contextid)) { - // delete from context rel, if this is the last override in this context - $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid, 'contextid'=>$contextid)); + // first find the course context + $coursecontext = $context->get_course_context(); + + // make sure there is a real user specified + if ($user === null) { + $userid = isset($USER->id) ? $USER->id : 0; } else { - $DB->delete_records('role_capabilities', array('capability'=>$capability, 'roleid'=>$roleid)); + $userid = is_object($user) ? $user->id : $user; } - return true; -} - - -/** - * Get the roles that have a given capability assigned to it - * - * This function does not resolve the actual permission of the capability. - * It just checks for permissions and overrides. - * Use get_roles_with_cap_in_context() if resolution is required. - * - * @param string $capability - capability name (string) - * @param string $permission - optional, the permission defined for this capability - * either CAP_ALLOW, CAP_PREVENT or CAP_PROHIBIT. Defaults to null which means any. - * @param stdClass $context, null means any - * @return array of role objects - */ -function get_roles_with_capability($capability, $permission = null, $context = null) { - global $DB; - if ($context) { - $contexts = get_parent_contexts($context, true); - list($insql, $params) = $DB->get_in_or_equal($contexts, SQL_PARAMS_NAMED, 'ctx'); - $contextsql = "AND rc.contextid $insql"; - } else { - $params = array(); - $contextsql = ''; + if (empty($userid)) { + // not-logged-in! + return false; + } else if (isguestuser($userid)) { + // guest account can not be enrolled anywhere + return false; } - if ($permission) { - $permissionsql = " AND rc.permission = :permission"; - $params['permission'] = $permission; + if ($coursecontext->instanceid == SITEID) { + // everybody participates on frontpage } else { - $permissionsql = ''; - } + if ($onlyactive) { + $sql = "SELECT ue.* + FROM {user_enrolments} ue + JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid) + JOIN {user} u ON u.id = ue.userid + WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0"; + $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE, 'userid'=>$userid, 'courseid'=>$coursecontext->instanceid); + // this result should be very small, better not do the complex time checks in sql for now ;-) + $enrolments = $DB->get_records_sql($sql, $params); + $now = time(); + // make sure the enrol period is ok + $result = false; + foreach ($enrolments as $e) { + if ($e->timestart > $now) { + continue; + } + if ($e->timeend and $e->timeend < $now) { + continue; + } + $result = true; + break; + } + if (!$result) { + return false; + } - $sql = "SELECT r.* - FROM {role} r - WHERE r.id IN (SELECT rc.roleid - FROM {role_capabilities} rc - WHERE rc.capability = :capname - $contextsql - $permissionsql)"; - $params['capname'] = $capability; + } else { + // any enrolment is good for us here, even outdated, disabled or inactive + $sql = "SELECT 'x' + FROM {user_enrolments} ue + JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid) + JOIN {user} u ON u.id = ue.userid + WHERE ue.userid = :userid AND u.deleted = 0"; + $params = array('userid'=>$userid, 'courseid'=>$coursecontext->instanceid); + if (!$DB->record_exists_sql($sql, $params)) { + return false; + } + } + } + if ($withcapability and !has_capability($withcapability, $context, $userid)) { + return false; + } - return $DB->get_records_sql($sql, $params); + return true; } - /** - * This function makes a role-assignment (a role for a user in a particular context) + * Returns true if the user is able to access the course. * - * @param int $roleid the role of the id - * @param int $userid userid - * @param int $contextid id of the context - * @param string $component example 'enrol_ldap', defaults to '' which means manual assignment, - * @prama int $itemid id of enrolment/auth plugin - * @param string $timemodified defaults to current time - * @return int new/existing id of the assignment - */ -function role_assign($roleid, $userid, $contextid, $component = '', $itemid = 0, $timemodified = '') { - global $USER, $CFG, $DB; - - // first of all detect if somebody is using old style parameters - if ($contextid === 0 or is_numeric($component)) { - throw new coding_exception('Invalid call to role_assign(), code needs to be updated to use new order of parameters'); - } - - // now validate all parameters - if (empty($roleid)) { - throw new coding_exception('Invalid call to role_assign(), roleid can not be empty'); - } - - if (empty($userid)) { - throw new coding_exception('Invalid call to role_assign(), userid can not be empty'); - } + * This function is in no way, shape, or form a substitute for require_login. + * It should only be used in circumstances where it is not possible to call require_login + * such as the navigation. + * + * This function checks many of the methods of access to a course such as the view + * capability, enrollments, and guest access. It also makes use of the cache + * generated by require_login for guest access. + * + * The flags within the $USER object that are used here should NEVER be used outside + * of this function can_access_course and require_login. Doing so WILL break future + * versions. + * + * @param context $context + * @param stdClass|null $user + * @param string $withcapability Check for this capability as well. + * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions + * @param boolean $trustcache If set to false guest access will always be checked + * against the enrolment plugins from the course, rather + * than the cache generated by require_login. + * @return boolean Returns true if the user is able to access the course + */ +function can_access_course(context $context, $user = null, $withcapability = '', $onlyactive = false, $trustcache = true) { + global $DB, $USER; - if ($itemid) { - if (strpos($component, '_') === false) { - throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as"enrol_" when itemid specified', 'component:'.$component); - } - } else { - $itemid = 0; - if ($component !== '' and strpos($component, '_') === false) { - throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component); - } - } + $coursecontext = $context->get_course_context(); + $courseid = $coursecontext->instanceid; - if (!$DB->record_exists('user', array('id'=>$userid, 'deleted'=>0))) { - throw new coding_exception('User ID does not exist or is deleted!', 'userid:'.$userid); + // First check the obvious, is the user viewing or is the user enrolled. + if (is_viewing($coursecontext, $user, $withcapability) || is_enrolled($coursecontext, $user, $withcapability, $onlyactive)) { + // How easy was that! + return true; } - $context = get_context_instance_by_id($contextid, MUST_EXIST); - - if (!$timemodified) { - $timemodified = time(); + $access = false; + if (!isset($USER->enrol)) { + // Cache hasn't been generated yet so we can't trust it + $trustcache = false; + /** + * These flags within the $USER object should NEVER be used outside of this + * function can_access_course and the function require_login. + * Doing so WILL break future versions!!!! + */ + $USER->enrol = array(); + $USER->enrol['enrolled'] = array(); + $USER->enrol['tempguest'] = array(); } -/// Check for existing entry - $ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id'); - - if ($ras) { - // role already assigned - this should not happen - if (count($ras) > 1) { - //very weird - remove all duplicates! - $ra = array_shift($ras); - foreach ($ras as $r) { - $DB->delete_records('role_assignments', array('id'=>$r->id)); + // If we don't trust the cache we need to check with the courses enrolment + // plugin instances to see if the user can access the course as a guest. + if (!$trustcache) { + // Ok, off to the database we go! + $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC'); + $enrols = enrol_get_plugins(true); + foreach($instances as $instance) { + if (!isset($enrols[$instance->enrol])) { + continue; + } + $until = $enrols[$instance->enrol]->try_guestaccess($instance); + if ($until !== false) { + // Never use me anywhere but here and require_login + $USER->enrol['tempguest'][$courseid] = $until; + $access = true; + break; } - } else { - $ra = reset($ras); } - - // actually there is no need to update, reset anything or trigger any event, so just return - return $ra->id; } - // Create a new entry - $ra = new stdClass(); - $ra->roleid = $roleid; - $ra->contextid = $context->id; - $ra->userid = $userid; - $ra->component = $component; - $ra->itemid = $itemid; - $ra->timemodified = $timemodified; - $ra->modifierid = empty($USER->id) ? 0 : $USER->id; - - $ra->id = $DB->insert_record('role_assignments', $ra); - - // mark context as dirty - again expensive, but needed - mark_context_dirty($context->path); - - if (!empty($USER->id) && $USER->id == $userid) { - // If the user is the current user, then do full reload of capabilities too. - load_all_capabilities(); + // If we don't already have access (from above) check the cache and see whether + // there is record of it in there. + if (!$access && isset($USER->enrol['tempguest'][$courseid])) { + // Never use me anywhere but here and require_login + if ($USER->enrol['tempguest'][$courseid] == 0) { + $access = true; + } else if ($USER->enrol['tempguest'][$courseid] > time()) { + $access = true; + } else { + //expired + unset($USER->enrol['tempguest'][$courseid]); + } } - - events_trigger('role_assigned', $ra); - - return $ra->id; + return $access; } /** - * Removes one role assignment + * Returns array with sql code and parameters returning all ids + * of users enrolled into course. * - * @param int $roleid - * @param int $userid - * @param int $contextid - * @param string $component - * @param int $itemid - * @return void + * This function is using 'eu[0-9]+_' prefix for table names and parameters. + * + * @param context $context + * @param string $withcapability + * @param int $groupid 0 means ignore groups, any other value limits the result by group id + * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions + * @return array list($sql, $params) */ -function role_unassign($roleid, $userid, $contextid, $component = '', $itemid = 0) { - global $USER, $CFG, $DB; - - // first make sure the params make sense - if ($roleid == 0 or $userid == 0 or $contextid == 0) { - throw new coding_exception('Invalid call to role_unassign(), please use role_unassign_all() when removing multiple role assignments'); - } - - if ($itemid) { - if (strpos($component, '_') === false) { - throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as "enrol_" when itemid specified', 'component:'.$component); - } - } else { - $itemid = 0; - if ($component !== '' and strpos($component, '_') === false) { - throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component); - } - } +function get_enrolled_sql(context $context, $withcapability = '', $groupid = 0, $onlyactive = false) { + global $DB, $CFG; - role_unassign_all(array('roleid'=>$roleid, 'userid'=>$userid, 'contextid'=>$contextid, 'component'=>$component, 'itemid'=>$itemid), false, false); -} + // use unique prefix just in case somebody makes some SQL magic with the result + static $i = 0; + $i++; + $prefix = 'eu'.$i.'_'; -/** - * Removes multiple role assignments, parameters may contain: - * 'roleid', 'userid', 'contextid', 'component', 'enrolid'. - * - * @param array $params role assignment parameters - * @param bool $subcontexts unassign in subcontexts too - * @param bool $includmanual include manual role assignments too - * @return void - */ -function role_unassign_all(array $params, $subcontexts = false, $includemanual = false) { - global $USER, $CFG, $DB; + // first find the course context + $coursecontext = $context->get_course_context(); - if (!$params) { - throw new coding_exception('Missing parameters in role_unsassign_all() call'); - } + $isfrontpage = ($coursecontext->instanceid == SITEID); - $allowed = array('roleid', 'userid', 'contextid', 'component', 'itemid'); - foreach ($params as $key=>$value) { - if (!in_array($key, $allowed)) { - throw new coding_exception('Unknown role_unsassign_all() parameter key', 'key:'.$key); - } - } + $joins = array(); + $wheres = array(); + $params = array(); - if (isset($params['component']) and $params['component'] !== '' and strpos($params['component'], '_') === false) { - throw new coding_exception('Invalid component paramter in role_unsassign_all() call', 'component:'.$params['component']); - } + list($contextids, $contextpaths) = get_context_info_list($context); - if ($includemanual) { - if (!isset($params['component']) or $params['component'] === '') { - throw new coding_exception('include manual parameter requires component parameter in role_unsassign_all() call'); - } - } + // get all relevant capability info for all roles + if ($withcapability) { + list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx'); + $cparams['cap'] = $withcapability; - if ($subcontexts) { - if (empty($params['contextid'])) { - throw new coding_exception('subcontexts paramtere requires component parameter in role_unsassign_all() call'); + $defs = array(); + $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path + FROM {role_capabilities} rc + JOIN {context} ctx on rc.contextid = ctx.id + WHERE rc.contextid $incontexts AND rc.capability = :cap"; + $rcs = $DB->get_records_sql($sql, $cparams); + foreach ($rcs as $rc) { + $defs[$rc->path][$rc->roleid] = $rc->permission; } - } - $ras = $DB->get_records('role_assignments', $params); - foreach($ras as $ra) { - $DB->delete_records('role_assignments', array('id'=>$ra->id)); - if ($context = get_context_instance_by_id($ra->contextid)) { - // this is a bit expensive but necessary - mark_context_dirty($context->path); - /// If the user is the current user, then do full reload of capabilities too. - if (!empty($USER->id) && $USER->id == $ra->userid) { - load_all_capabilities(); + $access = array(); + if (!empty($defs)) { + foreach ($contextpaths as $path) { + if (empty($defs[$path])) { + continue; + } + foreach($defs[$path] as $roleid => $perm) { + if ($perm == CAP_PROHIBIT) { + $access[$roleid] = CAP_PROHIBIT; + continue; + } + if (!isset($access[$roleid])) { + $access[$roleid] = (int)$perm; + } + } } } - events_trigger('role_unassigned', $ra); - } - unset($ras); - // process subcontexts - if ($subcontexts and $context = get_context_instance_by_id($params['contextid'])) { - $contexts = get_child_contexts($context); - $mparams = $params; - foreach($contexts as $context) { - $mparams['contextid'] = $context->id; - $ras = $DB->get_records('role_assignments', $mparams); - foreach($ras as $ra) { - $DB->delete_records('role_assignments', array('id'=>$ra->id)); - // this is a bit expensive but necessary - mark_context_dirty($context->path); - /// If the user is the current user, then do full reload of capabilities too. - if (!empty($USER->id) && $USER->id == $ra->userid) { - load_all_capabilities(); - } - events_trigger('role_unassigned', $ra); + unset($defs); + + // make lists of roles that are needed and prohibited + $needed = array(); // one of these is enough + $prohibited = array(); // must not have any of these + foreach ($access as $roleid => $perm) { + if ($perm == CAP_PROHIBIT) { + unset($needed[$roleid]); + $prohibited[$roleid] = true; + } else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) { + $needed[$roleid] = true; } } - } - // do this once more for all manual role assignments - if ($includemanual) { - $params['component'] = ''; - role_unassign_all($params, $subcontexts, false); - } -} + $defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0; + $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0; + $nobody = false; -/** - * Determines if a user is currently logged in - * - * @return bool - */ -function isloggedin() { - global $USER; + if ($isfrontpage) { + if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) { + $nobody = true; + } else if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) { + // everybody not having prohibit has the capability + $needed = array(); + } else if (empty($needed)) { + $nobody = true; + } + } else { + if (!empty($prohibited[$defaultuserroleid])) { + $nobody = true; + } else if (!empty($needed[$defaultuserroleid])) { + // everybody not having prohibit has the capability + $needed = array(); + } else if (empty($needed)) { + $nobody = true; + } + } - return (!empty($USER->id)); -} + if ($nobody) { + // nobody can match so return some SQL that does not return any results + $wheres[] = "1 = 2"; -/** - * Determines if a user is logged in as real guest user with username 'guest'. - * - * @param int|object $user mixed user object or id, $USER if not specified - * @return bool true if user is the real guest user, false if not logged in or other user - */ -function isguestuser($user = null) { - global $USER, $DB, $CFG; + } else { - // make sure we have the user id cached in config table, because we are going to use it a lot - if (empty($CFG->siteguest)) { - if (!$guestid = $DB->get_field('user', 'id', array('username'=>'guest', 'mnethostid'=>$CFG->mnet_localhost_id))) { - // guest does not exist yet, weird - return false; - } - set_config('siteguest', $guestid); - } - if ($user === null) { - $user = $USER; - } + if ($needed) { + $ctxids = implode(',', $contextids); + $roleids = implode(',', array_keys($needed)); + $joins[] = "JOIN {role_assignments} {$prefix}ra3 ON ({$prefix}ra3.userid = {$prefix}u.id AND {$prefix}ra3.roleid IN ($roleids) AND {$prefix}ra3.contextid IN ($ctxids))"; + } - if ($user === null) { - // happens when setting the $USER - return false; + if ($prohibited) { + $ctxids = implode(',', $contextids); + $roleids = implode(',', array_keys($prohibited)); + $joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4 ON ({$prefix}ra4.userid = {$prefix}u.id AND {$prefix}ra4.roleid IN ($roleids) AND {$prefix}ra4.contextid IN ($ctxids))"; + $wheres[] = "{$prefix}ra4.id IS NULL"; + } - } else if (is_numeric($user)) { - return ($CFG->siteguest == $user); + if ($groupid) { + $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)"; + $params["{$prefix}gmid"] = $groupid; + } + } - } else if (is_object($user)) { - if (empty($user->id)) { - return false; // not logged in means is not be guest - } else { - return ($CFG->siteguest == $user->id); + } else { + if ($groupid) { + $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)"; + $params["{$prefix}gmid"] = $groupid; } + } + $wheres[] = "{$prefix}u.deleted = 0 AND {$prefix}u.id <> :{$prefix}guestid"; + $params["{$prefix}guestid"] = $CFG->siteguest; + + if ($isfrontpage) { + // all users are "enrolled" on the frontpage } else { - throw new coding_exception('Invalid user parameter supplied for isguestuser() function!'); + $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id"; + $joins[] = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)"; + $params[$prefix.'courseid'] = $coursecontext->instanceid; + + if ($onlyactive) { + $wheres[] = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled"; + $wheres[] = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)"; + $now = round(time(), -2); // rounding helps caching in DB + $params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED, + $prefix.'active'=>ENROL_USER_ACTIVE, + $prefix.'now1'=>$now, $prefix.'now2'=>$now)); + } } + + $joins = implode("\n", $joins); + $wheres = "WHERE ".implode(" AND ", $wheres); + + $sql = "SELECT DISTINCT {$prefix}u.id + FROM {user} {$prefix}u + $joins + $wheres"; + + return array($sql, $params); } /** - * Does user have a (temporary or real) guest access to course? + * Returns list of users enrolled into course. * - * @param stdClass $context - * @param stdClass|int $user - * @return bool + * @param context $context + * @param string $withcapability + * @param int $groupid 0 means ignore groups, any other value limits the result by group id + * @param string $userfields requested user record fields + * @param string $orderby + * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). + * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). + * @return array of user records */ -function is_guest($context, $user = null) { - global $USER; +function get_enrolled_users(context $context, $withcapability = '', $groupid = 0, $userfields = 'u.*', $orderby = '', $limitfrom = 0, $limitnum = 0) { + global $DB; - // first find the course context - $coursecontext = get_course_context($context); + list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid); + $sql = "SELECT $userfields + FROM {user} u + JOIN ($esql) je ON je.id = u.id + WHERE u.deleted = 0"; - // make sure there is a real user specified - if ($user === null) { - $userid = isset($USER->id) ? $USER->id : 0; + if ($orderby) { + $sql = "$sql ORDER BY $orderby"; } else { - $userid = is_object($user) ? $user->id : $user; + $sql = "$sql ORDER BY u.lastname ASC, u.firstname ASC"; } - if (isguestuser($userid)) { - // can not inspect or be enrolled - return true; - } + return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); +} - if (has_capability('moodle/course:view', $coursecontext, $user)) { - // viewing users appear out of nowhere, they are neither guests nor participants - return false; - } +/** + * Counts list of users enrolled into course (as per above function) + * + * @param context $context + * @param string $withcapability + * @param int $groupid 0 means ignore groups, any other value limits the result by group id + * @return array of user records + */ +function count_enrolled_users(context $context, $withcapability = '', $groupid = 0) { + global $DB; - // consider only real active enrolments here - if (is_enrolled($coursecontext, $user, '', true)) { - return false; + list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid); + $sql = "SELECT count(u.id) + FROM {user} u + JOIN ($esql) je ON je.id = u.id + WHERE u.deleted = 0"; + + return $DB->count_records_sql($sql, $params); +} + +/** + * Loads the capability definitions for the component (from file). + * + * Loads the capability definitions for the component (from file). If no + * capabilities are defined for the component, we simply return an empty array. + * + * @param string $component full plugin name, examples: 'moodle', 'mod_forum' + * @return array array of capabilities + */ +function load_capability_def($component) { + $defpath = get_component_directory($component).'/db/access.php'; + + $capabilities = array(); + if (file_exists($defpath)) { + require($defpath); + if (!empty(${$component.'_capabilities'})) { + // BC capability array name + // since 2.0 we prefer $capabilities instead - it is easier to use and matches db/* files + debugging('componentname_capabilities array is deprecated, please use capabilities array only in access.php files'); + $capabilities = ${$component.'_capabilities'}; + } } - return true; + return $capabilities; } +/** + * Gets the capabilities that have been cached in the database for this component. + * + * @param string $component - examples: 'moodle', 'mod_forum' + * @return array array of capabilities + */ +function get_cached_capabilities($component = 'moodle') { + global $DB; + return $DB->get_records('capabilities', array('component'=>$component)); +} /** - * Returns true if the user has moodle/course:view capability in the course, - * this is intended for admins, managers (aka small admins), inspectors, etc. + * Returns default capabilities for given role archetype. * - * @param stdClass $context - * @param int|object $user, if null $USER is used - * @param string $withcapability extra capability name - * @return bool + * @param string $archetype role archetype + * @return array */ -function is_viewing($context, $user = null, $withcapability = '') { - // first find the course context - $coursecontext = get_course_context($context); +function get_default_capabilities($archetype) { + global $DB; - if (isguestuser($user)) { - // can not inspect - return false; + if (!$archetype) { + return array(); } - if (!has_capability('moodle/course:view', $coursecontext, $user)) { - // admins are allowed to inspect courses - return false; - } + $alldefs = array(); + $defaults = array(); + $components = array(); + $allcaps = $DB->get_records('capabilities'); - if ($withcapability and !has_capability($withcapability, $context, $user)) { - // site admins always have the capability, but the enrolment above blocks - return false; + foreach ($allcaps as $cap) { + if (!in_array($cap->component, $components)) { + $components[] = $cap->component; + $alldefs = array_merge($alldefs, load_capability_def($cap->component)); + } + } + foreach($alldefs as $name=>$def) { + // Use array 'archetypes if available. Only if not specified, use 'legacy'. + if (isset($def['archetypes'])) { + if (isset($def['archetypes'][$archetype])) { + $defaults[$name] = $def['archetypes'][$archetype]; + } + // 'legacy' is for backward compatibility with 1.9 access.php + } else { + if (isset($def['legacy'][$archetype])) { + $defaults[$name] = $def['legacy'][$archetype]; + } + } } - return true; + return $defaults; } /** - * Returns true if user is enrolled (is participating) in course - * this is intended for students and teachers. + * Reset role capabilities to default according to selected role archetype. + * If no archetype selected, removes all capabilities. * - * @param object $context - * @param int|object $user, if null $USER is used, otherwise user object or id expected - * @param string $withcapability extra capability name - * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions - * @return bool + * @param int $roleid + * @return void */ -function is_enrolled($context, $user = null, $withcapability = '', $onlyactive = false) { - global $USER, $DB; +function reset_role_capabilities($roleid) { + global $DB; - // first find the course context - $coursecontext = get_course_context($context); - - // make sure there is a real user specified - if ($user === null) { - $userid = isset($USER->id) ? $USER->id : 0; - } else { - $userid = is_object($user) ? $user->id : $user; - } - - if (empty($userid)) { - // not-logged-in! - return false; - } else if (isguestuser($userid)) { - // guest account can not be enrolled anywhere - return false; - } + $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST); + $defaultcaps = get_default_capabilities($role->archetype); - if ($coursecontext->instanceid == SITEID) { - // everybody participates on frontpage - } else { - if ($onlyactive) { - $sql = "SELECT ue.* - FROM {user_enrolments} ue - JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid) - JOIN {user} u ON u.id = ue.userid - WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0"; - $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE, 'userid'=>$userid, 'courseid'=>$coursecontext->instanceid); - // this result should be very small, better not do the complex time checks in sql for now ;-) - $enrolments = $DB->get_records_sql($sql, $params); - $now = time(); - // make sure the enrol period is ok - $result = false; - foreach ($enrolments as $e) { - if ($e->timestart > $now) { - continue; - } - if ($e->timeend and $e->timeend < $now) { - continue; - } - $result = true; - break; - } - if (!$result) { - return false; - } + $systemcontext = context_system::instance(); - } else { - // any enrolment is good for us here, even outdated, disabled or inactive - $sql = "SELECT 'x' - FROM {user_enrolments} ue - JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid) - JOIN {user} u ON u.id = ue.userid - WHERE ue.userid = :userid AND u.deleted = 0"; - $params = array('userid'=>$userid, 'courseid'=>$coursecontext->instanceid); - if (!$DB->record_exists_sql($sql, $params)) { - return false; - } - } - } + $DB->delete_records('role_capabilities', array('roleid'=>$roleid)); - if ($withcapability and !has_capability($withcapability, $context, $userid)) { - return false; + foreach($defaultcaps as $cap=>$permission) { + assign_capability($cap, $permission, $roleid, $systemcontext->id); } - - return true; } /** - * Returns true if the user is able to access the course. - * - * This function is in no way, shape, or form a substitute for require_login. - * It should only be used in circumstances where it is not possible to call require_login - * such as the navigation. - * - * This function checks many of the methods of access to a course such as the view - * capability, enrollments, and guest access. It also makes use of the cache - * generated by require_login for guest access. + * Updates the capabilities table with the component capability definitions. + * If no parameters are given, the function updates the core moodle + * capabilities. * - * The flags within the $USER object that are used here should NEVER be used outside - * of this function can_access_course and require_login. Doing so WILL break future - * versions. + * Note that the absence of the db/access.php capabilities definition file + * will cause any stored capabilities for the component to be removed from + * the database. * - * @global moodle_database $DB - * @param stdClass $context - * @param stdClass|null $user - * @param string $withcapability Check for this capability as well. - * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions - * @param boolean $trustcache If set to false guest access will always be checked - * against the enrolment plugins from the course, rather - * than the cache generated by require_login. - * @return boolean Returns true if the user is able to access the course + * @param string $component examples: 'moodle', 'mod/forum', 'block/quiz_results' + * @return boolean true if success, exception in case of any problems */ -function can_access_course($context, $user = null, $withcapability = '', $onlyactive = false, $trustcache = true) { - global $DB, $USER; +function update_capabilities($component = 'moodle') { + global $DB, $OUTPUT; - $coursecontext = get_course_context($context); - $courseid = $coursecontext->instanceid; + $storedcaps = array(); - // First check the obvious, is the user viewing or is the user enrolled. - if (is_viewing($coursecontext, $user, $withcapability) || is_enrolled($coursecontext, $user, $withcapability, $onlyactive)) { - // How easy was that! - return true; + $filecaps = load_capability_def($component); + foreach($filecaps as $capname=>$unused) { + if (!preg_match('|^[a-z]+/[a-z_0-9]+:[a-z_0-9]+$|', $capname)) { + debugging("Coding problem: Invalid capability name '$capname', use 'clonepermissionsfrom' field for migration."); + } } - $access = false; - if (!isset($USER->enrol)) { - // Cache hasn't been generated yet so we can't trust it - $trustcache = false; - /** - * These flags within the $USER object should NEVER be used outside of this - * function can_access_course and the function require_login. - * Doing so WILL break future versions!!!! - */ - $USER->enrol = array(); - $USER->enrol['enrolled'] = array(); - $USER->enrol['tempguest'] = array(); - } + $cachedcaps = get_cached_capabilities($component); + if ($cachedcaps) { + foreach ($cachedcaps as $cachedcap) { + array_push($storedcaps, $cachedcap->name); + // update risk bitmasks and context levels in existing capabilities if needed + if (array_key_exists($cachedcap->name, $filecaps)) { + if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) { + $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified + } + if ($cachedcap->captype != $filecaps[$cachedcap->name]['captype']) { + $updatecap = new stdClass(); + $updatecap->id = $cachedcap->id; + $updatecap->captype = $filecaps[$cachedcap->name]['captype']; + $DB->update_record('capabilities', $updatecap); + } + if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) { + $updatecap = new stdClass(); + $updatecap->id = $cachedcap->id; + $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask']; + $DB->update_record('capabilities', $updatecap); + } - // If we don't trust the cache we need to check with the courses enrolment - // plugin instances to see if the user can access the course as a guest. - if (!$trustcache) { - // Ok, off to the database we go! - $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC'); - $enrols = enrol_get_plugins(true); - foreach($instances as $instance) { - if (!isset($enrols[$instance->enrol])) { - continue; + if (!array_key_exists('contextlevel', $filecaps[$cachedcap->name])) { + $filecaps[$cachedcap->name]['contextlevel'] = 0; // no context level defined + } + if ($cachedcap->contextlevel != $filecaps[$cachedcap->name]['contextlevel']) { + $updatecap = new stdClass(); + $updatecap->id = $cachedcap->id; + $updatecap->contextlevel = $filecaps[$cachedcap->name]['contextlevel']; + $DB->update_record('capabilities', $updatecap); + } } - $until = $enrols[$instance->enrol]->try_guestaccess($instance); - if ($until !== false) { - // Never use me anywhere but here and require_login - $USER->enrol['tempguest'][$courseid] = $until; - $access = true; - break; + } + } + + // Are there new capabilities in the file definition? + $newcaps = array(); + + foreach ($filecaps as $filecap => $def) { + if (!$storedcaps || + ($storedcaps && in_array($filecap, $storedcaps) === false)) { + if (!array_key_exists('riskbitmask', $def)) { + $def['riskbitmask'] = 0; // no risk if not specified } + $newcaps[$filecap] = $def; } } + // Add new capabilities to the stored definition. + foreach ($newcaps as $capname => $capdef) { + $capability = new stdClass(); + $capability->name = $capname; + $capability->captype = $capdef['captype']; + $capability->contextlevel = $capdef['contextlevel']; + $capability->component = $component; + $capability->riskbitmask = $capdef['riskbitmask']; - // If we don't already have access (from above) check the cache and see whether - // there is record of it in there. - if (!$access && isset($USER->enrol['tempguest'][$courseid])) { - // Never use me anywhere but here and require_login - if ($USER->enrol['tempguest'][$courseid] == 0) { - $access = true; - } else if ($USER->enrol['tempguest'][$courseid] > time()) { - $access = true; - } else { - //expired - unset($USER->enrol['tempguest'][$courseid]); + $DB->insert_record('capabilities', $capability, false); + + if (isset($capdef['clonepermissionsfrom']) && in_array($capdef['clonepermissionsfrom'], $storedcaps)){ + if ($rolecapabilities = $DB->get_records('role_capabilities', array('capability'=>$capdef['clonepermissionsfrom']))){ + foreach ($rolecapabilities as $rolecapability){ + //assign_capability will update rather than insert if capability exists + if (!assign_capability($capname, $rolecapability->permission, + $rolecapability->roleid, $rolecapability->contextid, true)){ + echo $OUTPUT->notification('Could not clone capabilities for '.$capname); + } + } + } + // we ignore archetype key if we have cloned permissions + } else if (isset($capdef['archetypes']) && is_array($capdef['archetypes'])) { + assign_legacy_capabilities($capname, $capdef['archetypes']); + // 'legacy' is for backward compatibility with 1.9 access.php + } else if (isset($capdef['legacy']) && is_array($capdef['legacy'])) { + assign_legacy_capabilities($capname, $capdef['legacy']); } } - return $access; + // Are there any capabilities that have been removed from the file + // definition that we need to delete from the stored capabilities and + // role assignments? + capabilities_cleanup($component, $filecaps); + + // reset static caches + accesslib_clear_all_caches(false); + + return true; } /** - * Returns array with sql code and parameters returning all ids - * of users enrolled into course. - * - * This function is using 'eu[0-9]+_' prefix for table names and parameters. + * Deletes cached capabilities that are no longer needed by the component. + * Also unassigns these capabilities from any roles that have them. * - * @param object $context - * @param string $withcapability - * @param int $groupid 0 means ignore groups, any other value limits the result by group id - * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions - * @return array list($sql, $params) + * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results' + * @param array $newcapdef array of the new capability definitions that will be + * compared with the cached capabilities + * @return int number of deprecated capabilities that have been removed */ -function get_enrolled_sql($context, $withcapability = '', $groupid = 0, $onlyactive = false) { - global $DB, $CFG; - - // use unique prefix just in case somebody makes some SQL magic with the result - static $i = 0; - $i++; - $prefix = 'eu'.$i.'_'; - - // first find the course context - $coursecontext = get_course_context($context); +function capabilities_cleanup($component, $newcapdef = null) { + global $DB; - $isfrontpage = ($coursecontext->instanceid == SITEID); + $removedcount = 0; - $joins = array(); - $wheres = array(); - $params = array(); + if ($cachedcaps = get_cached_capabilities($component)) { + foreach ($cachedcaps as $cachedcap) { + if (empty($newcapdef) || + array_key_exists($cachedcap->name, $newcapdef) === false) { - list($contextids, $contextpaths) = get_context_info_list($context); + // Remove from capabilities cache. + $DB->delete_records('capabilities', array('name'=>$cachedcap->name)); + $removedcount++; + // Delete from roles. + if ($roles = get_roles_with_capability($cachedcap->name)) { + foreach($roles as $role) { + if (!unassign_capability($cachedcap->name, $role->id)) { + print_error('cannotunassigncap', 'error', '', (object)array('cap'=>$cachedcap->name, 'role'=>$role->name)); + } + } + } + } // End if. + } + } + return $removedcount; +} + + + +////////////////// +// UI FUNCTIONS // +////////////////// + +/** + * Returns an array of all the known types of risk + * The array keys can be used, for example as CSS class names, or in calls to + * print_risk_icon. The values are the corresponding RISK_ constants. + * + * @return array all the known types of risk. + */ +function get_all_risks() { + return array( + 'riskmanagetrust' => RISK_MANAGETRUST, + 'riskconfig' => RISK_CONFIG, + 'riskxss' => RISK_XSS, + 'riskpersonal' => RISK_PERSONAL, + 'riskspam' => RISK_SPAM, + 'riskdataloss' => RISK_DATALOSS, + ); +} + +/** + * Return a link to moodle docs for a given capability name + * + * @param object $capability a capability - a row from the mdl_capabilities table. + * @return string the human-readable capability name as a link to Moodle Docs. + */ +function get_capability_docs_link($capability) { + $url = get_docs_url('Capabilities/' . $capability->name); + return '' . get_capability_string($capability->name) . ''; +} + +/** + * This function pulls out all the resolved capabilities (overrides and + * defaults) of a role used in capability overrides in contexts at a given + * context. + * + * @param context $context + * @param int $roleid + * @param string $cap capability, optional, defaults to '' + * @return array of capabilities + */ +function role_context_capabilities($roleid, context $context, $cap = '') { + global $DB; + + $contexts = $context->get_parent_context_ids(true); + $contexts = '('.implode(',', $contexts).')'; + + $params = array($roleid); + + if ($cap) { + $search = " AND rc.capability = ? "; + $params[] = $cap; + } else { + $search = ''; + } + + $sql = "SELECT rc.* + FROM {role_capabilities} rc, {context} c + WHERE rc.contextid in $contexts + AND rc.roleid = ? + AND rc.contextid = c.id $search + ORDER BY c.contextlevel DESC, rc.capability DESC"; + + $capabilities = array(); + + if ($records = $DB->get_records_sql($sql, $params)) { + // We are traversing via reverse order. + foreach ($records as $record) { + // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit + if (!isset($capabilities[$record->capability]) || $record->permission<-500) { + $capabilities[$record->capability] = $record->permission; + } + } + } + return $capabilities; +} + +/** + * Constructs array with contextids as first parameter and context paths, + * in both cases bottom top including self. + * + * @private + * @param context $context + * @return array + */ +function get_context_info_list(context $context) { + $contextids = explode('/', ltrim($context->path, '/')); + $contextpaths = array(); + $contextids2 = $contextids; + while ($contextids2) { + $contextpaths[] = '/' . implode('/', $contextids2); + array_pop($contextids2); + } + return array($contextids, $contextpaths); +} + +/** + * Check if context is the front page context or a context inside it + * + * Returns true if this context is the front page context, or a context inside it, + * otherwise false. + * + * @param context $context a context object. + * @return bool + */ +function is_inside_frontpage(context $context) { + $frontpagecontext = context_course::instance(SITEID); + return strpos($context->path . '/', $frontpagecontext->path . '/') === 0; +} + +/** + * Returns capability information (cached) + * + * @param string $capabilityname + * @return object or null if capability not found + */ +function get_capability_info($capabilityname) { + global $ACCESSLIB_PRIVATE, $DB; // one request per page only + + //TODO: MUC - this could be cached in shared memory, it would eliminate 1 query per page + + if (empty($ACCESSLIB_PRIVATE->capabilities)) { + $ACCESSLIB_PRIVATE->capabilities = array(); + $caps = $DB->get_records('capabilities', array(), 'id, name, captype, riskbitmask'); + foreach ($caps as $cap) { + $capname = $cap->name; + unset($cap->id); + unset($cap->name); + $cap->riskbitmask = (int)$cap->riskbitmask; + $ACCESSLIB_PRIVATE->capabilities[$capname] = $cap; + } + } + + return isset($ACCESSLIB_PRIVATE->capabilities[$capabilityname]) ? $ACCESSLIB_PRIVATE->capabilities[$capabilityname] : null; +} + +/** + * Returns the human-readable, translated version of the capability. + * Basically a big switch statement. + * + * @param string $capabilityname e.g. mod/choice:readresponses + * @return string + */ +function get_capability_string($capabilityname) { + + // Typical capability name is 'plugintype/pluginname:capabilityname' + list($type, $name, $capname) = preg_split('|[/:]|', $capabilityname); + + if ($type === 'moodle') { + $component = 'core_role'; + } else if ($type === 'quizreport') { + //ugly hack!! + $component = 'quiz_'.$name; + } else { + $component = $type.'_'.$name; + } + + $stringname = $name.':'.$capname; + + if ($component === 'core_role' or get_string_manager()->string_exists($stringname, $component)) { + return get_string($stringname, $component); + } + + $dir = get_component_directory($component); + if (!file_exists($dir)) { + // plugin broken or does not exist, do not bother with printing of debug message + return $capabilityname.' ???'; + } + + // something is wrong in plugin, better print debug + return get_string($stringname, $component); +} + +/** + * This gets the mod/block/course/core etc strings. + * + * @param string $component + * @param int $contextlevel + * @return string|bool String is success, false if failed + */ +function get_component_string($component, $contextlevel) { + + if ($component === 'moodle' or $component === 'core') { + switch ($contextlevel) { + // TODO: this should probably use context level names instead + case CONTEXT_SYSTEM: return get_string('coresystem'); + case CONTEXT_USER: return get_string('users'); + case CONTEXT_COURSECAT: return get_string('categories'); + case CONTEXT_COURSE: return get_string('course'); + case CONTEXT_MODULE: return get_string('activities'); + case CONTEXT_BLOCK: return get_string('block'); + default: print_error('unknowncontext'); + } + } + + list($type, $name) = normalize_component($component); + $dir = get_plugin_directory($type, $name); + if (!file_exists($dir)) { + // plugin not installed, bad luck, there is no way to find the name + return $component.' ???'; + } + + switch ($type) { + // TODO: this is really hacky, anyway it should be probably moved to lib/pluginlib.php + case 'quiz': return get_string($name.':componentname', $component);// insane hack!!! + case 'repository': return get_string('repository', 'repository').': '.get_string('pluginname', $component); + case 'gradeimport': return get_string('gradeimport', 'grades').': '.get_string('pluginname', $component); + case 'gradeexport': return get_string('gradeexport', 'grades').': '.get_string('pluginname', $component); + case 'gradereport': return get_string('gradereport', 'grades').': '.get_string('pluginname', $component); + case 'webservice': return get_string('webservice', 'webservice').': '.get_string('pluginname', $component); + case 'block': return get_string('block').': '.get_string('pluginname', basename($component)); + case 'mod': + if (get_string_manager()->string_exists('pluginname', $component)) { + return get_string('activity').': '.get_string('pluginname', $component); + } else { + return get_string('activity').': '.get_string('modulename', $component); + } + default: return get_string('pluginname', $component); + } +} + +/** + * Gets the list of roles assigned to this context and up (parents) + * from the list of roles that are visible on user profile page + * and participants page. + * + * @param context $context + * @return array + */ +function get_profile_roles(context $context) { + global $CFG, $DB; + + if (empty($CFG->profileroles)) { + return array(); + } + + list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a'); + list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p'); + $params = array_merge($params, $cparams); + + $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder + FROM {role_assignments} ra, {role} r + WHERE r.id = ra.roleid + AND ra.contextid $contextlist + AND r.id $rallowed + ORDER BY r.sortorder ASC"; + + return $DB->get_records_sql($sql, $params); +} + +/** + * Gets the list of roles assigned to this context and up (parents) + * + * @param context $context + * @return array + */ +function get_roles_used_in_context(context $context) { + global $DB; + + list($contextlist, $params) = $DB->get_in_or_equal($context->get_parent_context_ids(true)); + + $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder + FROM {role_assignments} ra, {role} r + WHERE r.id = ra.roleid + AND ra.contextid $contextlist + ORDER BY r.sortorder ASC"; + + return $DB->get_records_sql($sql, $params); +} + +/** + * This function is used to print roles column in user profile page. + * It is using the CFG->profileroles to limit the list to only interesting roles. + * (The permission tab has full details of user role assignments.) + * + * @param int $userid + * @param int $courseid + * @return string + */ +function get_user_roles_in_course($userid, $courseid) { + global $CFG, $DB; + + if (empty($CFG->profileroles)) { + return ''; + } + + if ($courseid == SITEID) { + $context = context_system::instance(); + } else { + $context = context_course::instance($courseid); + } + + if (empty($CFG->profileroles)) { + return array(); + } + + list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a'); + list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p'); + $params = array_merge($params, $cparams); + + $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder + FROM {role_assignments} ra, {role} r + WHERE r.id = ra.roleid + AND ra.contextid $contextlist + AND r.id $rallowed + AND ra.userid = :userid + ORDER BY r.sortorder ASC"; + $params['userid'] = $userid; + + $rolestring = ''; + + if ($roles = $DB->get_records_sql($sql, $params)) { + foreach ($roles as $userrole) { + $rolenames[$userrole->id] = $userrole->name; + } + + $rolenames = role_fix_names($rolenames, $context); // Substitute aliases + + foreach ($rolenames as $roleid => $rolename) { + $rolenames[$roleid] = ''.$rolename.''; + } + $rolestring = implode(',', $rolenames); + } + + return $rolestring; +} + +/** + * Checks if a user can assign users to a particular role in this context + * + * @param context $context + * @param int $targetroleid - the id of the role you want to assign users to + * @return boolean + */ +function user_can_assign(context $context, $targetroleid) { + global $DB; + + // first check if user has override capability + // if not return false; + if (!has_capability('moodle/role:assign', $context)) { + return false; + } + // pull out all active roles of this user from this context(or above) + if ($userroles = get_user_roles($context)) { + foreach ($userroles as $userrole) { + // if any in the role_allow_override table, then it's ok + if ($DB->get_record('role_allow_assign', array('roleid'=>$userrole->roleid, 'allowassign'=>$targetroleid))) { + return true; + } + } + } + + return false; +} + +/** + * Returns all site roles in correct sort order. + * + * @return array + */ +function get_all_roles() { + global $DB; + return $DB->get_records('role', null, 'sortorder ASC'); +} + +/** + * Returns roles of a specified archetype + * + * @param string $archetype + * @return array of full role records + */ +function get_archetype_roles($archetype) { + global $DB; + return $DB->get_records('role', array('archetype'=>$archetype), 'sortorder ASC'); +} + +/** + * Gets all the user roles assigned in this context, or higher contexts + * this is mainly used when checking if a user can assign a role, or overriding a role + * i.e. we need to know what this user holds, in order to verify against allow_assign and + * allow_override tables + * + * @param context $context + * @param int $userid + * @param bool $checkparentcontexts defaults to true + * @param string $order defaults to 'c.contextlevel DESC, r.sortorder ASC' + * @return array + */ +function get_user_roles(context $context, $userid = 0, $checkparentcontexts = true, $order = 'c.contextlevel DESC, r.sortorder ASC') { + global $USER, $DB; + + if (empty($userid)) { + if (empty($USER->id)) { + return array(); + } + $userid = $USER->id; + } + + if ($checkparentcontexts) { + $contextids = $context->get_parent_context_ids(); + } else { + $contextids = array(); + } + $contextids[] = $context->id; + + list($contextids, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_QM); + + array_unshift($params, $userid); + + $sql = "SELECT ra.*, r.name, r.shortname + FROM {role_assignments} ra, {role} r, {context} c + WHERE ra.userid = ? + AND ra.roleid = r.id + AND ra.contextid = c.id + AND ra.contextid $contextids + ORDER BY $order"; + + return $DB->get_records_sql($sql ,$params); +} + +/** + * Creates a record in the role_allow_override table + * + * @param int $sroleid source roleid + * @param int $troleid target roleid + * @return void + */ +function allow_override($sroleid, $troleid) { + global $DB; + + $record = new stdClass(); + $record->roleid = $sroleid; + $record->allowoverride = $troleid; + $DB->insert_record('role_allow_override', $record); +} + +/** + * Creates a record in the role_allow_assign table + * + * @param int $fromroleid source roleid + * @param int $targetroleid target roleid + * @return void + */ +function allow_assign($fromroleid, $targetroleid) { + global $DB; + + $record = new stdClass(); + $record->roleid = $fromroleid; + $record->allowassign = $targetroleid; + $DB->insert_record('role_allow_assign', $record); +} + +/** + * Creates a record in the role_allow_switch table + * + * @param int $fromroleid source roleid + * @param int $targetroleid target roleid + * @return void + */ +function allow_switch($fromroleid, $targetroleid) { + global $DB; + + $record = new stdClass(); + $record->roleid = $fromroleid; + $record->allowswitch = $targetroleid; + $DB->insert_record('role_allow_switch', $record); +} + +/** + * Gets a list of roles that this user can assign in this context + * + * @param context $context the context. + * @param int $rolenamedisplay the type of role name to display. One of the + * ROLENAME_X constants. Default ROLENAME_ALIAS. + * @param bool $withusercounts if true, count the number of users with each role. + * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user. + * @return array if $withusercounts is false, then an array $roleid => $rolename. + * if $withusercounts is true, returns a list of three arrays, + * $rolenames, $rolecounts, and $nameswithcounts. + */ +function get_assignable_roles(context $context, $rolenamedisplay = ROLENAME_ALIAS, $withusercounts = false, $user = null) { + global $USER, $DB; + + // make sure there is a real user specified + if ($user === null) { + $userid = isset($USER->id) ? $USER->id : 0; + } else { + $userid = is_object($user) ? $user->id : $user; + } + + if (!has_capability('moodle/role:assign', $context, $userid)) { + if ($withusercounts) { + return array(array(), array(), array()); + } else { + return array(); + } + } + + $parents = $context->get_parent_context_ids(true); + $contexts = implode(',' , $parents); + + $params = array(); + $extrafields = ''; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT or $rolenamedisplay == ROLENAME_SHORT) { + $extrafields .= ', r.shortname'; + } + + if ($withusercounts) { + $extrafields = ', (SELECT count(u.id) + FROM {role_assignments} cra JOIN {user} u ON cra.userid = u.id + WHERE cra.roleid = r.id AND cra.contextid = :conid AND u.deleted = 0 + ) AS usercount'; + $params['conid'] = $context->id; + } + + if (is_siteadmin($userid)) { + // show all roles allowed in this context to admins + $assignrestriction = ""; + } else { + $assignrestriction = "JOIN (SELECT DISTINCT raa.allowassign AS id + FROM {role_allow_assign} raa + JOIN {role_assignments} ra ON ra.roleid = raa.roleid + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + ) ar ON ar.id = r.id"; + $params['userid'] = $userid; + } + $params['contextlevel'] = $context->contextlevel; + $sql = "SELECT r.id, r.name $extrafields + FROM {role} r + $assignrestriction + JOIN {role_context_levels} rcl ON r.id = rcl.roleid + WHERE rcl.contextlevel = :contextlevel + ORDER BY r.sortorder ASC"; + $roles = $DB->get_records_sql($sql, $params); + + $rolenames = array(); + foreach ($roles as $role) { + if ($rolenamedisplay == ROLENAME_SHORT) { + $rolenames[$role->id] = $role->shortname; + continue; + } + $rolenames[$role->id] = $role->name; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { + $rolenames[$role->id] .= ' (' . $role->shortname . ')'; + } + } + if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT and $rolenamedisplay != ROLENAME_SHORT) { + $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay); + } + + if (!$withusercounts) { + return $rolenames; + } + + $rolecounts = array(); + $nameswithcounts = array(); + foreach ($roles as $role) { + $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->usercount . ')'; + $rolecounts[$role->id] = $roles[$role->id]->usercount; + } + return array($rolenames, $rolecounts, $nameswithcounts); +} + +/** + * Gets a list of roles that this user can switch to in a context + * + * Gets a list of roles that this user can switch to in a context, for the switchrole menu. + * This function just process the contents of the role_allow_switch table. You also need to + * test the moodle/role:switchroles to see if the user is allowed to switch in the first place. + * + * @param context $context a context. + * @return array an array $roleid => $rolename. + */ +function get_switchable_roles(context $context) { + global $USER, $DB; + + $params = array(); + $extrajoins = ''; + $extrawhere = ''; + if (!is_siteadmin()) { + // Admins are allowed to switch to any role with. + // Others are subject to the additional constraint that the switch-to role must be allowed by + // 'role_allow_switch' for some role they have assigned in this context or any parent. + $parents = $context->get_parent_context_ids(true); + $contexts = implode(',' , $parents); + + $extrajoins = "JOIN {role_allow_switch} ras ON ras.allowswitch = rc.roleid + JOIN {role_assignments} ra ON ra.roleid = ras.roleid"; + $extrawhere = "WHERE ra.userid = :userid AND ra.contextid IN ($contexts)"; + $params['userid'] = $USER->id; + } + + $query = " + SELECT r.id, r.name + FROM (SELECT DISTINCT rc.roleid + FROM {role_capabilities} rc + $extrajoins + $extrawhere) idlist + JOIN {role} r ON r.id = idlist.roleid + ORDER BY r.sortorder"; + + $rolenames = $DB->get_records_sql_menu($query, $params); + return role_fix_names($rolenames, $context, ROLENAME_ALIAS); +} + +/** + * Gets a list of roles that this user can override in this context. + * + * @param context $context the context. + * @param int $rolenamedisplay the type of role name to display. One of the + * ROLENAME_X constants. Default ROLENAME_ALIAS. + * @param bool $withcounts if true, count the number of overrides that are set for each role. + * @return array if $withcounts is false, then an array $roleid => $rolename. + * if $withusercounts is true, returns a list of three arrays, + * $rolenames, $rolecounts, and $nameswithcounts. + */ +function get_overridable_roles(context $context, $rolenamedisplay = ROLENAME_ALIAS, $withcounts = false) { + global $USER, $DB; + + if (!has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context)) { + if ($withcounts) { + return array(array(), array(), array()); + } else { + return array(); + } + } + + $parents = $context->get_parent_context_ids(true); + $contexts = implode(',' , $parents); + + $params = array(); + $extrafields = ''; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { + $extrafields .= ', ro.shortname'; + } + + $params['userid'] = $USER->id; + if ($withcounts) { + $extrafields = ', (SELECT COUNT(rc.id) FROM {role_capabilities} rc + WHERE rc.roleid = ro.id AND rc.contextid = :conid) AS overridecount'; + $params['conid'] = $context->id; + } + + if (is_siteadmin()) { + // show all roles to admins + $roles = $DB->get_records_sql(" + SELECT ro.id, ro.name$extrafields + FROM {role} ro + ORDER BY ro.sortorder ASC", $params); + + } else { + $roles = $DB->get_records_sql(" + SELECT ro.id, ro.name$extrafields + FROM {role} ro + JOIN (SELECT DISTINCT r.id + FROM {role} r + JOIN {role_allow_override} rao ON r.id = rao.allowoverride + JOIN {role_assignments} ra ON rao.roleid = ra.roleid + WHERE ra.userid = :userid AND ra.contextid IN ($contexts) + ) inline_view ON ro.id = inline_view.id + ORDER BY ro.sortorder ASC", $params); + } + + $rolenames = array(); + foreach ($roles as $role) { + $rolenames[$role->id] = $role->name; + if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { + $rolenames[$role->id] .= ' (' . $role->shortname . ')'; + } + } + if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT) { + $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay); + } + + if (!$withcounts) { + return $rolenames; +} + + $rolecounts = array(); + $nameswithcounts = array(); + foreach ($roles as $role) { + $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->overridecount . ')'; + $rolecounts[$role->id] = $roles[$role->id]->overridecount; + } + return array($rolenames, $rolecounts, $nameswithcounts); +} + +/** + * Create a role menu suitable for default role selection in enrol plugins. + * @param context $context + * @param int $addroleid current or default role - always added to list + * @return array roleid=>localised role name + */ +function get_default_enrol_roles(context $context, $addroleid = null) { + global $DB; + + $params = array('contextlevel'=>CONTEXT_COURSE); + if ($addroleid) { + $addrole = "OR r.id = :addroleid"; + $params['addroleid'] = $addroleid; + } else { + $addrole = ""; + } + $sql = "SELECT r.id, r.name + FROM {role} r + LEFT JOIN {role_context_levels} rcl ON (rcl.roleid = r.id AND rcl.contextlevel = :contextlevel) + WHERE rcl.id IS NOT NULL $addrole + ORDER BY sortorder DESC"; + + $roles = $DB->get_records_sql_menu($sql, $params); + $roles = role_fix_names($roles, $context, ROLENAME_BOTH); + + return $roles; +} + +/** + * Return context levels where this role is assignable. + * @param integer $roleid the id of a role. + * @return array list of the context levels at which this role may be assigned. + */ +function get_role_contextlevels($roleid) { + global $DB; + return $DB->get_records_menu('role_context_levels', array('roleid' => $roleid), + 'contextlevel', 'id,contextlevel'); +} + +/** + * Return roles suitable for assignment at the specified context level. + * + * NOTE: this function name looks like a typo, should be probably get_roles_for_contextlevel() + * + * @param integer $contextlevel a contextlevel. + * @return array list of role ids that are assignable at this context level. + */ +function get_roles_for_contextlevels($contextlevel) { + global $DB; + return $DB->get_records_menu('role_context_levels', array('contextlevel' => $contextlevel), + '', 'id,roleid'); +} + +/** + * Returns default context levels where roles can be assigned. + * + * @param string $rolearchetype one of the role archetypes - that is, one of the keys + * from the array returned by get_role_archetypes(); + * @return array list of the context levels at which this type of role may be assigned by default. + */ +function get_default_contextlevels($rolearchetype) { + static $defaults = array( + 'manager' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE), + 'coursecreator' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT), + 'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE), + 'teacher' => array(CONTEXT_COURSE, CONTEXT_MODULE), + 'student' => array(CONTEXT_COURSE, CONTEXT_MODULE), + 'guest' => array(), + 'user' => array(), + 'frontpage' => array()); + + if (isset($defaults[$rolearchetype])) { + return $defaults[$rolearchetype]; + } else { + return array(); + } +} + +/** + * Set the context levels at which a particular role can be assigned. + * Throws exceptions in case of error. + * + * @param integer $roleid the id of a role. + * @param array $contextlevels the context levels at which this role should be assignable, + * duplicate levels are removed. + * @return void + */ +function set_role_contextlevels($roleid, array $contextlevels) { + global $DB; + $DB->delete_records('role_context_levels', array('roleid' => $roleid)); + $rcl = new stdClass(); + $rcl->roleid = $roleid; + $contextlevels = array_unique($contextlevels); + foreach ($contextlevels as $level) { + $rcl->contextlevel = $level; + $DB->insert_record('role_context_levels', $rcl, false, true); + } +} - // get all relevant capability info for all roles - if ($withcapability) { - list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx'); - $cparams['cap'] = $withcapability; +/** + * Who has this capability in this context? + * + * This can be a very expensive call - use sparingly and keep + * the results if you are going to need them again soon. + * + * Note if $fields is empty this function attempts to get u.* + * which can get rather large - and has a serious perf impact + * on some DBs. + * + * @param context $context + * @param string|array $capability - capability name(s) + * @param string $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included. + * @param string $sort - the sort order. Default is lastaccess time. + * @param mixed $limitfrom - number of records to skip (offset) + * @param mixed $limitnum - number of records to fetch + * @param string|array $groups - single group or array of groups - only return + * users who are in one of these group(s). + * @param string|array $exceptions - list of users to exclude, comma separated or array + * @param bool $doanything_ignored not used any more, admin accounts are never returned + * @param bool $view_ignored - use get_enrolled_sql() instead + * @param bool $useviewallgroups if $groups is set the return users who + * have capability both $capability and moodle/site:accessallgroups + * in this context, as well as users who have $capability and who are + * in $groups. + * @return mixed + */ +function get_users_by_capability(context $context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '', + $groups = '', $exceptions = '', $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false) { + global $CFG, $DB; - $defs = array(); - $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path - FROM {role_capabilities} rc - JOIN {context} ctx on rc.contextid = ctx.id - WHERE rc.contextid $incontexts AND rc.capability = :cap"; - $rcs = $DB->get_records_sql($sql, $cparams); - foreach ($rcs as $rc) { - $defs[$rc->path][$rc->roleid] = $rc->permission; + $defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0; + $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0; + + $ctxids = trim($context->path, '/'); + $ctxids = str_replace('/', ',', $ctxids); + + // Context is the frontpage + $iscoursepage = false; // coursepage other than fp + $isfrontpage = false; + if ($context->contextlevel == CONTEXT_COURSE) { + if ($context->instanceid == SITEID) { + $isfrontpage = true; + } else { + $iscoursepage = true; } + } + $isfrontpage = ($isfrontpage || is_inside_frontpage($context)); - $access = array(); - if (!empty($defs)) { - foreach ($contextpaths as $path) { - if (empty($defs[$path])) { + $caps = (array)$capability; + + // construct list of context paths bottom-->top + list($contextids, $paths) = get_context_info_list($context); + + // we need to find out all roles that have these capabilities either in definition or in overrides + $defs = array(); + list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con'); + list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, 'cap'); + $params = array_merge($params, $params2); + $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path + FROM {role_capabilities} rc + JOIN {context} ctx on rc.contextid = ctx.id + WHERE rc.contextid $incontexts AND rc.capability $incaps"; + + $rcs = $DB->get_records_sql($sql, $params); + foreach ($rcs as $rc) { + $defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission; + } + + // go through the permissions bottom-->top direction to evaluate the current permission, + // first one wins (prohibit is an exception that always wins) + $access = array(); + foreach ($caps as $cap) { + foreach ($paths as $path) { + if (empty($defs[$cap][$path])) { + continue; + } + foreach($defs[$cap][$path] as $roleid => $perm) { + if ($perm == CAP_PROHIBIT) { + $access[$cap][$roleid] = CAP_PROHIBIT; continue; } - foreach($defs[$path] as $roleid => $perm) { - if ($perm == CAP_PROHIBIT) { - $access[$roleid] = CAP_PROHIBIT; - continue; - } - if (!isset($access[$roleid])) { - $access[$roleid] = (int)$perm; - } + if (!isset($access[$cap][$roleid])) { + $access[$cap][$roleid] = (int)$perm; } } } + } - unset($defs); - - // make lists of roles that are needed and prohibited - $needed = array(); // one of these is enough - $prohibited = array(); // must not have any of these - foreach ($access as $roleid => $perm) { + // make lists of roles that are needed and prohibited in this context + $needed = array(); // one of these is enough + $prohibited = array(); // must not have any of these + foreach ($caps as $cap) { + if (empty($access[$cap])) { + continue; + } + foreach ($access[$cap] as $roleid => $perm) { if ($perm == CAP_PROHIBIT) { - unset($needed[$roleid]); - $prohibited[$roleid] = true; - } else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) { - $needed[$roleid] = true; + unset($needed[$cap][$roleid]); + $prohibited[$cap][$roleid] = true; + } else if ($perm == CAP_ALLOW and empty($prohibited[$cap][$roleid])) { + $needed[$cap][$roleid] = true; } } + if (empty($needed[$cap]) or !empty($prohibited[$cap][$defaultuserroleid])) { + // easy, nobody has the permission + unset($needed[$cap]); + unset($prohibited[$cap]); + } else if ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) { + // everybody is disqualified on the frontapge + unset($needed[$cap]); + unset($prohibited[$cap]); + } + if (empty($prohibited[$cap])) { + unset($prohibited[$cap]); + } + } - $defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0; - $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0; + if (empty($needed)) { + // there can not be anybody if no roles match this request + return array(); + } + + if (empty($prohibited)) { + // we can compact the needed roles + $n = array(); + foreach ($needed as $cap) { + foreach ($cap as $roleid=>$unused) { + $n[$roleid] = true; + } + } + $needed = array('any'=>$n); + unset($n); + } + + /// ***** Set up default fields ****** + if (empty($fields)) { + if ($iscoursepage) { + $fields = 'u.*, ul.timeaccess AS lastaccess'; + } else { + $fields = 'u.*'; + } + } else { + if (debugging('', DEBUG_DEVELOPER) && strpos($fields, 'u.*') === false && strpos($fields, 'u.id') === false) { + debugging('u.id must be included in the list of fields passed to get_users_by_capability().', DEBUG_DEVELOPER); + } + } + + /// Set up default sort + if (empty($sort)) { // default to course lastaccess or just lastaccess + if ($iscoursepage) { + $sort = 'ul.timeaccess'; + } else { + $sort = 'u.lastaccess'; + } + } + + // Prepare query clauses + $wherecond = array(); + $params = array(); + $joins = array(); + + // User lastaccess JOIN + if ((strpos($sort, 'ul.timeaccess') === false) and (strpos($fields, 'ul.timeaccess') === false)) { + // user_lastaccess is not required MDL-13810 + } else { + if ($iscoursepage) { + $joins[] = "LEFT OUTER JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})"; + } else { + throw new coding_exception('Invalid sort in get_users_by_capability(), ul.timeaccess allowed only for course contexts.'); + } + } + + /// We never return deleted users or guest account. + $wherecond[] = "u.deleted = 0 AND u.id <> :guestid"; + $params['guestid'] = $CFG->siteguest; + + /// Groups + if ($groups) { + $groups = (array)$groups; + list($grouptest, $grpparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grp'); + $grouptest = "u.id IN (SELECT userid FROM {groups_members} gm WHERE gm.groupid $grouptest)"; + $params = array_merge($params, $grpparams); + + if ($useviewallgroups) { + $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions); + if (!empty($viewallgroupsusers)) { + $wherecond[] = "($grouptest OR u.id IN (" . implode(',', array_keys($viewallgroupsusers)) . '))'; + } else { + $wherecond[] = "($grouptest)"; + } + } else { + $wherecond[] = "($grouptest)"; + } + } + + /// User exceptions + if (!empty($exceptions)) { + $exceptions = (array)$exceptions; + list($exsql, $exparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'exc', false); + $params = array_merge($params, $exparams); + $wherecond[] = "u.id $exsql"; + } + + // now add the needed and prohibited roles conditions as joins + if (!empty($needed['any'])) { + // simple case - there are no prohibits involved + if (!empty($needed['any'][$defaultuserroleid]) or ($isfrontpage and !empty($needed['any'][$defaultfrontpageroleid]))) { + // everybody + } else { + $joins[] = "JOIN (SELECT DISTINCT userid + FROM {role_assignments} + WHERE contextid IN ($ctxids) + AND roleid IN (".implode(',', array_keys($needed['any'])) .") + ) ra ON ra.userid = u.id"; + } + } else { + $unions = array(); + $everybody = false; + foreach ($needed as $cap=>$unused) { + if (empty($prohibited[$cap])) { + if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) { + $everybody = true; + break; + } else { + $unions[] = "SELECT userid + FROM {role_assignments} + WHERE contextid IN ($ctxids) + AND roleid IN (".implode(',', array_keys($needed[$cap])) .")"; + } + } else { + if (!empty($prohibited[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid]))) { + // nobody can have this cap because it is prevented in default roles + continue; - $nobody = false; + } else if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) { + // everybody except the prohibitted - hiding does not matter + $unions[] = "SELECT id AS userid + FROM {user} + WHERE id NOT IN (SELECT userid + FROM {role_assignments} + WHERE contextid IN ($ctxids) + AND roleid IN (".implode(',', array_keys($prohibited[$cap])) ."))"; - if ($isfrontpage) { - if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) { - $nobody = true; - } else if (!empty($needed[$defaultuserroleid]) or !empty($needed[$defaultfrontpageroleid])) { - // everybody not having prohibit has the capability - $needed = array(); - } else if (empty($needed)) { - $nobody = true; + } else { + $unions[] = "SELECT userid + FROM {role_assignments} + WHERE contextid IN ($ctxids) + AND roleid IN (".implode(',', array_keys($needed[$cap])) .") + AND roleid NOT IN (".implode(',', array_keys($prohibited[$cap])) .")"; + } } - } else { - if (!empty($prohibited[$defaultuserroleid])) { - $nobody = true; - } else if (!empty($needed[$defaultuserroleid])) { - // everybody not having prohibit has the capability - $needed = array(); - } else if (empty($needed)) { - $nobody = true; + } + if (!$everybody) { + if ($unions) { + $joins[] = "JOIN (SELECT DISTINCT userid FROM ( ".implode(' UNION ', $unions)." ) us) ra ON ra.userid = u.id"; + } else { + // only prohibits found - nobody can be matched + $wherecond[] = "1 = 2"; } } + } - if ($nobody) { - // nobody can match so return some SQL that does not return any results - $wheres[] = "1 = 2"; - - } else { + // Collect WHERE conditions and needed joins + $where = implode(' AND ', $wherecond); + if ($where !== '') { + $where = 'WHERE ' . $where; + } + $joins = implode("\n", $joins); - if ($needed) { - $ctxids = implode(',', $contextids); - $roleids = implode(',', array_keys($needed)); - $joins[] = "JOIN {role_assignments} {$prefix}ra3 ON ({$prefix}ra3.userid = {$prefix}u.id AND {$prefix}ra3.roleid IN ($roleids) AND {$prefix}ra3.contextid IN ($ctxids))"; - } + /// Ok, let's get the users! + $sql = "SELECT $fields + FROM {user} u + $joins + $where + ORDER BY $sort"; - if ($prohibited) { - $ctxids = implode(',', $contextids); - $roleids = implode(',', array_keys($prohibited)); - $joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4 ON ({$prefix}ra4.userid = {$prefix}u.id AND {$prefix}ra4.roleid IN ($roleids) AND {$prefix}ra4.contextid IN ($ctxids))"; - $wheres[] = "{$prefix}ra4.id IS NULL"; - } + return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); +} - if ($groupid) { - $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)"; - $params["{$prefix}gmid"] = $groupid; - } - } +/** + * Re-sort a users array based on a sorting policy + * + * Will re-sort a $users results array (from get_users_by_capability(), usually) + * based on a sorting policy. This is to support the odd practice of + * sorting teachers by 'authority', where authority was "lowest id of the role + * assignment". + * + * Will execute 1 database query. Only suitable for small numbers of users, as it + * uses an u.id IN() clause. + * + * Notes about the sorting criteria. + * + * As a default, we cannot rely on role.sortorder because then + * admins/coursecreators will always win. That is why the sane + * rule "is locality matters most", with sortorder as 2nd + * consideration. + * + * If you want role.sortorder, use the 'sortorder' policy, and + * name explicitly what roles you want to cover. It's probably + * a good idea to see what roles have the capabilities you want + * (array_diff() them against roiles that have 'can-do-anything' + * to weed out admin-ish roles. Or fetch a list of roles from + * variables like $CFG->coursecontact . + * + * @param array $users Users array, keyed on userid + * @param context $context + * @param array $roles ids of the roles to include, optional + * @param string $sortpolicy defaults to locality, more about + * @return array sorted copy of the array + */ +function sort_by_roleassignment_authority($users, context $context, $roles = array(), $sortpolicy = 'locality') { + global $DB; + $userswhere = ' ra.userid IN (' . implode(',',array_keys($users)) . ')'; + $contextwhere = 'AND ra.contextid IN ('.str_replace('/', ',',substr($context->path, 1)).')'; + if (empty($roles)) { + $roleswhere = ''; } else { - if ($groupid) { - $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.groupid = :{$prefix}gmid)"; - $params["{$prefix}gmid"] = $groupid; - } + $roleswhere = ' AND ra.roleid IN ('.implode(',',$roles).')'; } - $wheres[] = "{$prefix}u.deleted = 0 AND {$prefix}u.id <> :{$prefix}guestid"; - $params["{$prefix}guestid"] = $CFG->siteguest; - - if ($isfrontpage) { - // all users are "enrolled" on the frontpage - } else { - $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id"; - $joins[] = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)"; - $params[$prefix.'courseid'] = $coursecontext->instanceid; + $sql = "SELECT ra.userid + FROM {role_assignments} ra + JOIN {role} r + ON ra.roleid=r.id + JOIN {context} ctx + ON ra.contextid=ctx.id + WHERE $userswhere + $contextwhere + $roleswhere"; - if ($onlyactive) { - $wheres[] = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled"; - $wheres[] = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)"; - $now = round(time(), -2); // rounding helps caching in DB - $params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED, - $prefix.'active'=>ENROL_USER_ACTIVE, - $prefix.'now1'=>$now, $prefix.'now2'=>$now)); - } + // Default 'locality' policy -- read PHPDoc notes + // about sort policies... + $orderby = 'ORDER BY ' + .'ctx.depth DESC, ' /* locality wins */ + .'r.sortorder ASC, ' /* rolesorting 2nd criteria */ + .'ra.id'; /* role assignment order tie-breaker */ + if ($sortpolicy === 'sortorder') { + $orderby = 'ORDER BY ' + .'r.sortorder ASC, ' /* rolesorting 2nd criteria */ + .'ra.id'; /* role assignment order tie-breaker */ } - $joins = implode("\n", $joins); - $wheres = "WHERE ".implode(" AND ", $wheres); + $sortedids = $DB->get_fieldset_sql($sql . $orderby); + $sortedusers = array(); + $seen = array(); - $sql = "SELECT DISTINCT {$prefix}u.id - FROM {user} {$prefix}u - $joins - $wheres"; + foreach ($sortedids as $id) { + // Avoid duplicates + if (isset($seen[$id])) { + continue; + } + $seen[$id] = true; - return array($sql, $params); + // assign + $sortedusers[$id] = $users[$id]; + } + return $sortedusers; } /** - * Returns list of users enrolled into course. - * @param object $context - * @param string $withcapability - * @param int $groupid 0 means ignore groups, any other value limits the result by group id - * @param string $userfields requested user record fields - * @param string $orderby - * @param int $limitfrom return a subset of records, starting at this point (optional, required if $limitnum is set). - * @param int $limitnum return a subset comprising this many records (optional, required if $limitfrom is set). - * @return array of user records + * Gets all the users assigned this role in this context or higher + * + * @param int $roleid (can also be an array of ints!) + * @param context $context + * @param bool $parent if true, get list of users assigned in higher context too + * @param string $fields fields from user (u.) , role assignment (ra) or role (r.) + * @param string $sort sort from user (u.) , role assignment (ra) or role (r.) + * @param bool $gethidden_ignored use enrolments instead + * @param string $group defaults to '' + * @param mixed $limitfrom defaults to '' + * @param mixed $limitnum defaults to '' + * @param string $extrawheretest defaults to '' + * @param string|array $whereparams defaults to '' + * @return array */ -function get_enrolled_users($context, $withcapability = '', $groupid = 0, $userfields = 'u.*', $orderby = '', $limitfrom = 0, $limitnum = 0) { +function get_role_users($roleid, context $context, $parent = false, $fields = '', + $sort = 'u.lastname, u.firstname', $gethidden_ignored = null, $group = '', + $limitfrom = '', $limitnum = '', $extrawheretest = '', $whereparams = array()) { global $DB; - list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid); - $sql = "SELECT $userfields - FROM {user} u - JOIN ($esql) je ON je.id = u.id - WHERE u.deleted = 0"; + if (empty($fields)) { + $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '. + 'u.maildisplay, u.mailformat, u.maildigest, u.email, u.city, '. + 'u.country, u.picture, u.idnumber, u.department, u.institution, '. + 'u.lang, u.timezone, u.lastaccess, u.mnethostid, r.name AS rolename, r.sortorder'; + } - if ($orderby) { - $sql = "$sql ORDER BY $orderby"; + $parentcontexts = ''; + if ($parent) { + $parentcontexts = substr($context->path, 1); // kill leading slash + $parentcontexts = str_replace('/', ',', $parentcontexts); + if ($parentcontexts !== '') { + $parentcontexts = ' OR ra.contextid IN ('.$parentcontexts.' )'; + } + } + + if ($roleid) { + list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM); + $roleselect = "AND ra.roleid $rids"; } else { - $sql = "$sql ORDER BY u.lastname ASC, u.firstname ASC"; + $params = array(); + $roleselect = ''; } - return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); -} + if ($group) { + $groupjoin = "JOIN {groups_members} gm ON gm.userid = u.id"; + $groupselect = " AND gm.groupid = ? "; + $params[] = $group; + } else { + $groupjoin = ''; + $groupselect = ''; + } -/** - * Counts list of users enrolled into course (as per above function) - * @param object $context - * @param string $withcapability - * @param int $groupid 0 means ignore groups, any other value limits the result by group id - * @return array of user records - */ -function count_enrolled_users($context, $withcapability = '', $groupid = 0) { - global $DB; + array_unshift($params, $context->id); - list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid); - $sql = "SELECT count(u.id) - FROM {user} u - JOIN ($esql) je ON je.id = u.id - WHERE u.deleted = 0"; + if ($extrawheretest) { + $extrawheretest = ' AND ' . $extrawheretest; + $params = array_merge($params, $whereparams); + } + + $sql = "SELECT DISTINCT $fields, ra.roleid + FROM {role_assignments} ra + JOIN {user} u ON u.id = ra.userid + JOIN {role} r ON ra.roleid = r.id + $groupjoin + WHERE (ra.contextid = ? $parentcontexts) + $roleselect + $groupselect + $extrawheretest + ORDER BY $sort"; // join now so that we can just use fullname() later - return $DB->count_records_sql($sql, $params); + return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); } - /** - * Loads the capability definitions for the component (from file). - * - * Loads the capability definitions for the component (from file). If no - * capabilities are defined for the component, we simply return an empty array. + * Counts all the users assigned this role in this context or higher * - * @param string $component full plugin name, examples: 'moodle', 'mod_forum' - * @return array array of capabilities + * @param int|array $roleid either int or an array of ints + * @param context $context + * @param bool $parent if true, get list of users assigned in higher context too + * @return int Returns the result count */ -function load_capability_def($component) { - $defpath = get_component_directory($component).'/db/access.php'; +function count_role_users($roleid, context $context, $parent = false) { + global $DB; - $capabilities = array(); - if (file_exists($defpath)) { - require($defpath); - if (!empty(${$component.'_capabilities'})) { - // BC capability array name - // since 2.0 we prefer $capabilities instead - it is easier to use and matches db/* files - debugging('componentname_capabilities array is deprecated, please use capabilities array only in access.php files'); - $capabilities = ${$component.'_capabilities'}; + if ($parent) { + if ($contexts = $context->get_parent_context_ids()) { + $parentcontexts = ' OR r.contextid IN ('.implode(',', $contexts).')'; + } else { + $parentcontexts = ''; } + } else { + $parentcontexts = ''; } - return $capabilities; -} + if ($roleid) { + list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM); + $roleselect = "AND r.roleid $rids"; + } else { + $params = array(); + $roleselect = ''; + } + array_unshift($params, $context->id); -/** - * Gets the capabilities that have been cached in the database for this component. - * @param string $component - examples: 'moodle', 'mod_forum' - * @return array array of capabilities - */ -function get_cached_capabilities($component = 'moodle') { - global $DB; - return $DB->get_records('capabilities', array('component'=>$component)); + $sql = "SELECT COUNT(u.id) + FROM {role_assignments} r + JOIN {user} u ON u.id = r.userid + WHERE (r.contextid = ? $parentcontexts) + $roleselect + AND u.deleted = 0"; + + return $DB->count_records_sql($sql, $params); } /** - * Returns default capabilities for given role archetype. - * @param string $archetype role archetype - * @return array + * This function gets the list of courses that this user has a particular capability in. + * It is still not very efficient. + * + * @param string $capability Capability in question + * @param int $userid User ID or null for current user + * @param bool $doanything True if 'doanything' is permitted (default) + * @param string $fieldsexceptid Leave blank if you only need 'id' in the course records; + * otherwise use a comma-separated list of the fields you require, not including id + * @param string $orderby If set, use a comma-separated list of fields from course + * table with sql modifiers (DESC) if needed + * @return array Array of courses, may have zero entries. Or false if query failed. */ -function get_default_capabilities($archetype) { +function get_user_capability_course($capability, $userid = null, $doanything = true, $fieldsexceptid = '', $orderby = '') { global $DB; - if (!$archetype) { - return array(); - } - - $alldefs = array(); - $defaults = array(); - $components = array(); - $allcaps = $DB->get_records('capabilities'); - - foreach ($allcaps as $cap) { - if (!in_array($cap->component, $components)) { - $components[] = $cap->component; - $alldefs = array_merge($alldefs, load_capability_def($cap->component)); + // Convert fields list and ordering + $fieldlist = ''; + if ($fieldsexceptid) { + $fields = explode(',', $fieldsexceptid); + foreach($fields as $field) { + $fieldlist .= ',c.'.$field; } } - foreach($alldefs as $name=>$def) { - // Use array 'archetypes if available. Only if not specified, use 'legacy'. - if (isset($def['archetypes'])) { - if (isset($def['archetypes'][$archetype])) { - $defaults[$name] = $def['archetypes'][$archetype]; - } - // 'legacy' is for backward compatibility with 1.9 access.php - } else { - if (isset($def['legacy'][$archetype])) { - $defaults[$name] = $def['legacy'][$archetype]; + if ($orderby) { + $fields = explode(',', $orderby); + $orderby = ''; + foreach($fields as $field) { + if ($orderby) { + $orderby .= ','; } + $orderby .= 'c.'.$field; } + $orderby = 'ORDER BY '.$orderby; } - return $defaults; + // Obtain a list of everything relevant about all courses including context. + // Note the result can be used directly as a context (we are going to), the course + // fields are just appended. + + $courses = array(); + $rs = $DB->get_recordset_sql("SELECT x.*, c.id AS courseid $fieldlist + FROM {course} c + INNER JOIN {context} x + ON (c.id=x.instanceid AND x.contextlevel=".CONTEXT_COURSE.") + $orderby"); + // Check capability for each course in turn + foreach ($rs as $coursecontext) { + if (has_capability($capability, $coursecontext, $userid, $doanything)) { + // We've got the capability. Make the record look like a course record + // and store it + $coursecontext->id = $coursecontext->courseid; + unset($coursecontext->courseid); + unset($coursecontext->contextlevel); + unset($coursecontext->instanceid); + $courses[] = $coursecontext; + } + } + $rs->close(); + return empty($courses) ? false : $courses; } /** - * Reset role capabilities to default according to selected role archetype. - * If no archetype selected, removes all capabilities. - * @param int $roleid - * @return void + * This function finds the roles assigned directly to this context only + * i.e. no roles in parent contexts + * + * @param context $context + * @return array */ -function reset_role_capabilities($roleid) { +function get_roles_on_exact_context(context $context) { global $DB; - $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST); - $defaultcaps = get_default_capabilities($role->archetype); - - $sitecontext = get_context_instance(CONTEXT_SYSTEM); - - $DB->delete_records('role_capabilities', array('roleid'=>$roleid)); + return $DB->get_records_sql("SELECT r.* + FROM {role_assignments} ra, {role} r + WHERE ra.roleid = r.id AND ra.contextid = ?", + array($context->id)); - foreach($defaultcaps as $cap=>$permission) { - assign_capability($cap, $permission, $roleid, $sitecontext->id); - } } /** - * Updates the capabilities table with the component capability definitions. - * If no parameters are given, the function updates the core moodle - * capabilities. + * Switches the current user to another role for the current session and only + * in the given context. * - * Note that the absence of the db/access.php capabilities definition file - * will cause any stored capabilities for the component to be removed from - * the database. + * The caller *must* check + * - that this op is allowed + * - that the requested role can be switched to in this context (use get_switchable_roles) + * - that the requested role is NOT $CFG->defaultuserroleid * - * @param string $component examples: 'moodle', 'mod/forum', 'block/quiz_results' - * @return boolean true if success, exception in case of any problems + * To "unswitch" pass 0 as the roleid. + * + * This function *will* modify $USER->access - beware + * + * @param integer $roleid the role to switch to. + * @param context $context the context in which to perform the switch. + * @return bool success or failure. */ -function update_capabilities($component = 'moodle') { - global $DB, $OUTPUT, $ACCESSLIB_PRIVATE; - - $storedcaps = array(); - - $filecaps = load_capability_def($component); - foreach($filecaps as $capname=>$unused) { - if (!preg_match('|^[a-z]+/[a-z_0-9]+:[a-z_0-9]+$|', $capname)) { - debugging("Coding problem: Invalid capability name '$capname', use 'clonepermissionsfrom' field for migration."); - } - } - - $cachedcaps = get_cached_capabilities($component); - if ($cachedcaps) { - foreach ($cachedcaps as $cachedcap) { - array_push($storedcaps, $cachedcap->name); - // update risk bitmasks and context levels in existing capabilities if needed - if (array_key_exists($cachedcap->name, $filecaps)) { - if (!array_key_exists('riskbitmask', $filecaps[$cachedcap->name])) { - $filecaps[$cachedcap->name]['riskbitmask'] = 0; // no risk if not specified - } - if ($cachedcap->captype != $filecaps[$cachedcap->name]['captype']) { - $updatecap = new stdClass(); - $updatecap->id = $cachedcap->id; - $updatecap->captype = $filecaps[$cachedcap->name]['captype']; - $DB->update_record('capabilities', $updatecap); - } - if ($cachedcap->riskbitmask != $filecaps[$cachedcap->name]['riskbitmask']) { - $updatecap = new stdClass(); - $updatecap->id = $cachedcap->id; - $updatecap->riskbitmask = $filecaps[$cachedcap->name]['riskbitmask']; - $DB->update_record('capabilities', $updatecap); - } - - if (!array_key_exists('contextlevel', $filecaps[$cachedcap->name])) { - $filecaps[$cachedcap->name]['contextlevel'] = 0; // no context level defined - } - if ($cachedcap->contextlevel != $filecaps[$cachedcap->name]['contextlevel']) { - $updatecap = new stdClass(); - $updatecap->id = $cachedcap->id; - $updatecap->contextlevel = $filecaps[$cachedcap->name]['contextlevel']; - $DB->update_record('capabilities', $updatecap); - } - } - } - } - - // Are there new capabilities in the file definition? - $newcaps = array(); +function role_switch($roleid, context $context) { + global $USER; - foreach ($filecaps as $filecap => $def) { - if (!$storedcaps || - ($storedcaps && in_array($filecap, $storedcaps) === false)) { - if (!array_key_exists('riskbitmask', $def)) { - $def['riskbitmask'] = 0; // no risk if not specified - } - $newcaps[$filecap] = $def; - } - } - // Add new capabilities to the stored definition. - foreach ($newcaps as $capname => $capdef) { - $capability = new stdClass(); - $capability->name = $capname; - $capability->captype = $capdef['captype']; - $capability->contextlevel = $capdef['contextlevel']; - $capability->component = $component; - $capability->riskbitmask = $capdef['riskbitmask']; + // + // Plan of action + // + // - Add the ghost RA to $USER->access + // as $USER->access['rsw'][$path] = $roleid + // + // - Make sure $USER->access['rdef'] has the roledefs + // it needs to honour the switcherole + // + // Roledefs will get loaded "deep" here - down to the last child + // context. Note that + // + // - When visiting subcontexts, our selective accessdata loading + // will still work fine - though those ra/rdefs will be ignored + // appropriately while the switch is in place + // + // - If a switcherole happens at a category with tons of courses + // (that have many overrides for switched-to role), the session + // will get... quite large. Sometimes you just can't win. + // + // To un-switch just unset($USER->access['rsw'][$path]) + // + // Note: it is not possible to switch to roles that do not have course:view - $DB->insert_record('capabilities', $capability, false); + // Add the switch RA + if (!isset($USER->access['rsw'])) { + $USER->access['rsw'] = array(); + } - if (isset($capdef['clonepermissionsfrom']) && in_array($capdef['clonepermissionsfrom'], $storedcaps)){ - if ($rolecapabilities = $DB->get_records('role_capabilities', array('capability'=>$capdef['clonepermissionsfrom']))){ - foreach ($rolecapabilities as $rolecapability){ - //assign_capability will update rather than insert if capability exists - if (!assign_capability($capname, $rolecapability->permission, - $rolecapability->roleid, $rolecapability->contextid, true)){ - echo $OUTPUT->notification('Could not clone capabilities for '.$capname); - } - } - } - // we ignore archetype key if we have cloned permissions - } else if (isset($capdef['archetypes']) && is_array($capdef['archetypes'])) { - assign_legacy_capabilities($capname, $capdef['archetypes']); - // 'legacy' is for backward compatibility with 1.9 access.php - } else if (isset($capdef['legacy']) && is_array($capdef['legacy'])) { - assign_legacy_capabilities($capname, $capdef['legacy']); + if ($roleid == 0) { + unset($USER->access['rsw'][$context->path]); + if (empty($USER->access['rsw'])) { + unset($USER->access['rsw']); } + return true; } - // Are there any capabilities that have been removed from the file - // definition that we need to delete from the stored capabilities and - // role assignments? - capabilities_cleanup($component, $filecaps); - // reset static caches - $ACCESSLIB_PRIVATE->capabilities = null; + $USER->access['rsw'][$context->path] = $roleid; + + // Load roledefs + load_role_access_by_context($roleid, $context, $USER->access); return true; } +/** + * Checks if the user has switched roles within the given course. + * + * Note: You can only switch roles within the course, hence it takes a courseid + * rather than a context. On that note Petr volunteered to implement this across + * all other contexts, all requests for this should be forwarded to him ;) + * + * @param int $courseid The id of the course to check + * @return bool True if the user has switched roles within the course. + */ +function is_role_switched($courseid) { + global $USER; + $context = context_course::instance($courseid, MUST_EXIST); + return (!empty($USER->access['rsw'][$context->path])); +} /** - * Deletes cached capabilities that are no longer needed by the component. - * Also unassigns these capabilities from any roles that have them. + * Get any role that has an override on exact context * - * @param string $component examples: 'moodle', 'mod_forum', 'block_quiz_results' - * @param array $newcapdef array of the new capability definitions that will be - * compared with the cached capabilities - * @return int number of deprecated capabilities that have been removed + * @param context $context The context to check + * @return array An array of roles */ -function capabilities_cleanup($component, $newcapdef = null) { +function get_roles_with_override_on_context(context $context) { global $DB; - $removedcount = 0; + return $DB->get_records_sql("SELECT r.* + FROM {role_capabilities} rc, {role} r + WHERE rc.roleid = r.id AND rc.contextid = ?", + array($context->id)); +} - if ($cachedcaps = get_cached_capabilities($component)) { - foreach ($cachedcaps as $cachedcap) { - if (empty($newcapdef) || - array_key_exists($cachedcap->name, $newcapdef) === false) { +/** + * Get all capabilities for this role on this context (overrides) + * + * @param stdClass $role + * @param context $context + * @return array + */ +function get_capabilities_from_role_on_context($role, context $context) { + global $DB; - // Remove from capabilities cache. - $DB->delete_records('capabilities', array('name'=>$cachedcap->name)); - $removedcount++; - // Delete from roles. - if ($roles = get_roles_with_capability($cachedcap->name)) { - foreach($roles as $role) { - if (!unassign_capability($cachedcap->name, $role->id)) { - print_error('cannotunassigncap', 'error', '', (object)array('cap'=>$cachedcap->name, 'role'=>$role->name)); - } - } - } - } // End if. - } - } - return $removedcount; + return $DB->get_records_sql("SELECT * + FROM {role_capabilities} + WHERE contextid = ? AND roleid = ?", + array($context->id, $role->id)); } +/** + * Find out which roles has assignment on this context + * + * @param context $context + * @return array + * + */ +function get_roles_with_assignment_on_context(context $context) { + global $DB; + + return $DB->get_records_sql("SELECT r.* + FROM {role_assignments} ra, {role} r + WHERE ra.roleid = r.id AND ra.contextid = ?", + array($context->id)); +} +/** + * Find all user assignment of users for this role, on this context + * + * @param stdClass $role + * @param context $context + * @return array + */ +function get_users_from_role_on_context($role, context $context) { + global $DB; -////////////////// -// UI FUNCTIONS // -////////////////// + return $DB->get_records_sql("SELECT * + FROM {role_assignments} + WHERE contextid = ? AND roleid = ?", + array($context->id, $role->id)); +} /** - * @param integer $contextlevel $context->context level. One of the CONTEXT_... constants. - * @return string the name for this type of context. + * Simple function returning a boolean true if user has roles + * in context or parent contexts, otherwise false. + * + * @param int $userid + * @param int $roleid + * @param int $contextid empty means any context + * @return bool */ -function get_contextlevel_name($contextlevel) { - static $strcontextlevels = null; - if (is_null($strcontextlevels)) { - $strcontextlevels = array( - CONTEXT_SYSTEM => get_string('coresystem'), - CONTEXT_USER => get_string('user'), - CONTEXT_COURSECAT => get_string('category'), - CONTEXT_COURSE => get_string('course'), - CONTEXT_MODULE => get_string('activitymodule'), - CONTEXT_BLOCK => get_string('block') - ); +function user_has_role_assignment($userid, $roleid, $contextid = 0) { + global $DB; + + if ($contextid) { + if (!$context = context::instance_by_id($contextid, IGNORE_MISSING)) { + return false; + } + $parents = $context->get_parent_context_ids(true); + list($contexts, $params) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED, 'r'); + $params['userid'] = $userid; + $params['roleid'] = $roleid; + + $sql = "SELECT COUNT(ra.id) + FROM {role_assignments} ra + WHERE ra.userid = :userid AND ra.roleid = :roleid AND ra.contextid $contexts"; + + $count = $DB->get_field_sql($sql, $params); + return ($count > 0); + + } else { + return $DB->record_exists('role_assignments', array('userid'=>$userid, 'roleid'=>$roleid)); } - return $strcontextlevels[$contextlevel]; } /** - * Prints human readable context identifier. + * Get role name or alias if exists and format the text. * - * @param object $context the context. - * @param boolean $withprefix whether to prefix the name of the context with the - * type of context, e.g. User, Course, Forum, etc. - * @param boolean $short whether to user the short name of the thing. Only applies - * to course contexts - * @return string the human readable context name. + * @param stdClass $role role object + * @param context_course $coursecontext + * @return string name of role in course context */ -function print_context_name($context, $withprefix = true, $short = false) { +function role_get_name($role, context_course $coursecontext) { global $DB; - $name = ''; - switch ($context->contextlevel) { - - case CONTEXT_SYSTEM: - $name = get_string('coresystem'); - break; + if ($r = $DB->get_record('role_names', array('roleid'=>$role->id, 'contextid'=>$coursecontext->id))) { + return strip_tags(format_string($r->name)); + } else { + return strip_tags(format_string($role->name)); + } +} - case CONTEXT_USER: - if ($user = $DB->get_record('user', array('id'=>$context->instanceid))) { - if ($withprefix){ - $name = get_string('user').': '; - } - $name .= fullname($user); - } - break; +/** + * Prepare list of roles for display, apply aliases and format text + * + * @param array $roleoptions array roleid => rolename or roleid => roleobject + * @param context $context a context + * @param int $rolenamedisplay + * @return array Array of context-specific role names, or role objexts with a ->localname field added. + */ +function role_fix_names($roleoptions, context $context, $rolenamedisplay = ROLENAME_ALIAS) { + global $DB; - case CONTEXT_COURSECAT: - if ($category = $DB->get_record('course_categories', array('id'=>$context->instanceid))) { - if ($withprefix){ - $name = get_string('category').': '; - } - $name .= format_string($category->name, true, array('context' => $context)); - } - break; + // Make sure we have a course context. + $coursecontext = $context->get_course_context(false); - case CONTEXT_COURSE: - if ($context->instanceid == SITEID) { - $name = get_string('frontpage', 'admin'); + // Make sure we are working with an array roleid => name. Normally we + // want to use the unlocalised name if the localised one is not present. + $newnames = array(); + foreach ($roleoptions as $rid => $roleorname) { + if ($rolenamedisplay != ROLENAME_ALIAS_RAW) { + if (is_object($roleorname)) { + $newnames[$rid] = $roleorname->name; } else { - if ($course = $DB->get_record('course', array('id'=>$context->instanceid))) { - if ($withprefix){ - $name = get_string('course').': '; - } - if ($short){ - $name .= format_string($course->shortname, true, array('context' => $context)); - } else { - $name .= format_string($course->fullname); - } - } + $newnames[$rid] = $roleorname; } - break; - - case CONTEXT_MODULE: - if ($cm = $DB->get_record_sql('SELECT cm.*, md.name AS modname FROM {course_modules} cm ' . - 'JOIN {modules} md ON md.id = cm.module WHERE cm.id = ?', array($context->instanceid))) { - if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) { - if ($withprefix){ - $name = get_string('modulename', $cm->modname).': '; - } - $name .= $mod->name; - } - } - break; + } else { + $newnames[$rid] = ''; + } + } - case CONTEXT_BLOCK: - if ($blockinstance = $DB->get_record('block_instances', array('id'=>$context->instanceid))) { - global $CFG; - require_once("$CFG->dirroot/blocks/moodleblock.class.php"); - require_once("$CFG->dirroot/blocks/$blockinstance->blockname/block_$blockinstance->blockname.php"); - $blockname = "block_$blockinstance->blockname"; - if ($blockobject = new $blockname()) { - if ($withprefix){ - $name = get_string('block').': '; - } - $name .= $blockobject->title; + // If necessary, get the localised names. + if ($rolenamedisplay != ROLENAME_ORIGINAL && !empty($coursecontext->id)) { + // The get the relevant renames, and use them. + $aliasnames = $DB->get_records('role_names', array('contextid'=>$coursecontext->id)); + foreach ($aliasnames as $alias) { + if (isset($newnames[$alias->roleid])) { + if ($rolenamedisplay == ROLENAME_ALIAS || $rolenamedisplay == ROLENAME_ALIAS_RAW) { + $newnames[$alias->roleid] = $alias->name; + } else if ($rolenamedisplay == ROLENAME_BOTH) { + $newnames[$alias->roleid] = $alias->name . ' (' . $roleoptions[$alias->roleid] . ')'; } } - break; - - default: - print_error('unknowncontext'); - return false; + } } - return $name; + // Finally, apply format_string and put the result in the right place. + foreach ($roleoptions as $rid => $roleorname) { + if ($rolenamedisplay != ROLENAME_ALIAS_RAW) { + $newnames[$rid] = strip_tags(format_string($newnames[$rid])); + } + if (is_object($roleorname)) { + $roleoptions[$rid]->localname = $newnames[$rid]; + } else { + $roleoptions[$rid] = $newnames[$rid]; + } + } + return $roleoptions; } /** - * Get a URL for a context, if there is a natural one. For example, for - * CONTEXT_COURSE, this is the course page. For CONTEXT_USER it is the - * user profile page. + * Aids in detecting if a new line is required when reading a new capability * - * @param object $context the context. - * @return moodle_url - */ -function get_context_url($context) { - global $COURSE, $DB; - - switch ($context->contextlevel) { - case CONTEXT_USER: - if ($COURSE->id == SITEID) { - $url = new moodle_url('/user/profile.php', array('id'=>$context->instanceid)); - } else { - $url = new moodle_url('/user/view.php', array('id'=>$context->instanceid, 'courseid'=>$COURSE->id)); - } - return $url;; + * This function helps admin/roles/manage.php etc to detect if a new line should be printed + * when we read in a new capability. + * Most of the time, if the 2 components are different we should print a new line, (e.g. course system->rss client) + * but when we are in grade, all reports/import/export capabilities should be together + * + * @param string $cap component string a + * @param string $comp component string b + * @param int $contextlevel + * @return bool whether 2 component are in different "sections" + */ +function component_level_changed($cap, $comp, $contextlevel) { - case CONTEXT_COURSECAT: // Coursecat -> coursecat or site - return new moodle_url('/course/category.php', array('id'=>$context->instanceid)); + if (strstr($cap->component, '/') && strstr($comp, '/')) { + $compsa = explode('/', $cap->component); + $compsb = explode('/', $comp); - case CONTEXT_COURSE: // 1 to 1 to course cat - if ($context->instanceid != SITEID) { - return new moodle_url('/course/view.php', array('id'=>$context->instanceid)); - } - break; + // list of system reports + if (($compsa[0] == 'report') && ($compsb[0] == 'report')) { + return false; + } - case CONTEXT_MODULE: // 1 to 1 to course - if ($modname = $DB->get_field_sql('SELECT md.name AS modname FROM {course_modules} cm ' . - 'JOIN {modules} md ON md.id = cm.module WHERE cm.id = ?', array($context->instanceid))) { - return new moodle_url('/mod/' . $modname . '/view.php', array('id'=>$context->instanceid)); - } - break; + // we are in gradebook, still + if (($compsa[0] == 'gradeexport' || $compsa[0] == 'gradeimport' || $compsa[0] == 'gradereport') && + ($compsb[0] == 'gradeexport' || $compsb[0] == 'gradeimport' || $compsb[0] == 'gradereport')) { + return false; + } - case CONTEXT_BLOCK: - $parentcontexts = get_parent_contexts($context, false); - $parent = reset($parentcontexts); - $parent = get_context_instance_by_id($parent); - return get_context_url($parent); + if (($compsa[0] == 'coursereport') && ($compsb[0] == 'coursereport')) { + return false; + } } - return new moodle_url('/'); + return ($cap->component != $comp || $cap->contextlevel != $contextlevel); } /** - * Returns an array of all the known types of risk - * The array keys can be used, for example as CSS class names, or in calls to - * print_risk_icon. The values are the corresponding RISK_ constants. + * Fix the roles.sortorder field in the database, so it contains sequential integers, + * and return an array of roleids in order. * - * @return array all the known types of risk. + * @param array $allroles array of roles, as returned by get_all_roles(); + * @return array $role->sortorder =-> $role->id with the keys in ascending order. */ -function get_all_risks() { - return array( - 'riskmanagetrust' => RISK_MANAGETRUST, - 'riskconfig' => RISK_CONFIG, - 'riskxss' => RISK_XSS, - 'riskpersonal' => RISK_PERSONAL, - 'riskspam' => RISK_SPAM, - 'riskdataloss' => RISK_DATALOSS, - ); +function fix_role_sortorder($allroles) { + global $DB; + + $rolesort = array(); + $i = 0; + foreach ($allroles as $role) { + $rolesort[$i] = $role->id; + if ($role->sortorder != $i) { + $r = new stdClass(); + $r->id = $role->id; + $r->sortorder = $i; + $DB->update_record('role', $r); + $allroles[$role->id]->sortorder = $i; + } + $i++; + } + return $rolesort; } /** - * Return a link to moodle docs for a given capability name + * Switch the sort order of two roles (used in admin/roles/manage.php). * - * @param object $capability a capability - a row from the mdl_capabilities table. - * @return string the human-readable capability name as a link to Moodle Docs. + * @param object $first The first role. Actually, only ->sortorder is used. + * @param object $second The second role. Actually, only ->sortorder is used. + * @return boolean success or failure */ -function get_capability_docs_link($capability) { - global $CFG; - $url = get_docs_url('Capabilities/' . $capability->name); - return '' . get_capability_string($capability->name) . ''; +function switch_roles($first, $second) { + global $DB; + $temp = $DB->get_field('role', 'MAX(sortorder) + 1', array()); + $result = $DB->set_field('role', 'sortorder', $temp, array('sortorder' => $first->sortorder)); + $result = $result && $DB->set_field('role', 'sortorder', $first->sortorder, array('sortorder' => $second->sortorder)); + $result = $result && $DB->set_field('role', 'sortorder', $second->sortorder, array('sortorder' => $temp)); + return $result; } /** - * Extracts the relevant capabilities given a contextid. - * All case based, example an instance of forum context. - * Will fetch all forum related capabilities, while course contexts - * Will fetch all capabilities - * - * capabilities - * `name` varchar(150) NOT NULL, - * `captype` varchar(50) NOT NULL, - * `contextlevel` int(10) NOT NULL, - * `component` varchar(100) NOT NULL, + * Duplicates all the base definitions of a role * - * @param object context - * @return array + * @param object $sourcerole role to copy from + * @param int $targetrole id of role to copy to */ -function fetch_context_capabilities($context) { - global $DB, $CFG; - - $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display - - $params = array(); - - switch ($context->contextlevel) { - - case CONTEXT_SYSTEM: // all - $SQL = "SELECT * - FROM {capabilities}"; - break; - - case CONTEXT_USER: - $extracaps = array('moodle/grade:viewall'); - list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap'); - $SQL = "SELECT * - FROM {capabilities} - WHERE contextlevel = ".CONTEXT_USER." - OR name $extra"; - break; - - case CONTEXT_COURSECAT: // course category context and bellow - $SQL = "SELECT * - FROM {capabilities} - WHERE contextlevel IN (".CONTEXT_COURSECAT.",".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")"; - break; - - case CONTEXT_COURSE: // course context and bellow - $SQL = "SELECT * - FROM {capabilities} - WHERE contextlevel IN (".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")"; - break; - - case CONTEXT_MODULE: // mod caps - $cm = $DB->get_record('course_modules', array('id'=>$context->instanceid)); - $module = $DB->get_record('modules', array('id'=>$cm->module)); - - $subcaps = array(); - $subpluginsfile = "$CFG->dirroot/mod/$module->name/db/subplugins.php"; - if (file_exists($subpluginsfile)) { - $subplugins = array(); // should be redefined in the file - include($subpluginsfile); - if (!empty($subplugins)) { - foreach (array_keys($subplugins) as $subplugintype) { - foreach (array_keys(get_plugin_list($subplugintype)) as $subpluginname) { - $subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname))); - } - } - } - } - - $modfile = "$CFG->dirroot/mod/$module->name/lib.php"; - if (file_exists($modfile)) { - include_once($modfile); - $modfunction = $module->name.'_get_extra_capabilities'; - if (function_exists($modfunction)) { - $extracaps = $modfunction(); - } - } - if (empty($extracaps)) { - $extracaps = array(); - } - - $extracaps = array_merge($subcaps, $extracaps); - - // All modules allow viewhiddenactivities. This is so you can hide - // the module then override to allow specific roles to see it. - // The actual check is in course page so not module-specific - $extracaps[]="moodle/course:viewhiddenactivities"; - list($extra, $params) = $DB->get_in_or_equal( - $extracaps, SQL_PARAMS_NAMED, 'cap0'); - $extra = "OR name $extra"; - - $SQL = "SELECT * - FROM {capabilities} - WHERE (contextlevel = ".CONTEXT_MODULE." - AND component = :component) - $extra"; - $params['component'] = "mod_$module->name"; - break; - - case CONTEXT_BLOCK: // block caps - $bi = $DB->get_record('block_instances', array('id' => $context->instanceid)); - - $extra = ''; - $extracaps = block_method_result($bi->blockname, 'get_extra_capabilities'); - if ($extracaps) { - list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap'); - $extra = "OR name $extra"; - } - - $SQL = "SELECT * - FROM {capabilities} - WHERE (contextlevel = ".CONTEXT_BLOCK." - AND component = :component) - $extra"; - $params['component'] = 'block_' . $bi->blockname; - break; - - default: - return false; - } +function role_cap_duplicate($sourcerole, $targetrole) { + global $DB; - if (!$records = $DB->get_records_sql($SQL.' '.$sort, $params)) { - $records = array(); + $systemcontext = context_system::instance(); + $caps = $DB->get_records_sql("SELECT * + FROM {role_capabilities} + WHERE roleid = ? AND contextid = ?", + array($sourcerole->id, $systemcontext->id)); + // adding capabilities + foreach ($caps as $cap) { + unset($cap->id); + $cap->roleid = $targetrole; + $DB->insert_record('role_capabilities', $cap); } - - return $records; } - /** - * This function pulls out all the resolved capabilities (overrides and - * defaults) of a role used in capability overrides in contexts at a given - * context. + * Returns two lists, this can be used to find out if user has capability. + * Having any needed role and no forbidden role in this context means + * user has this capability in this context. + * Use get_role_names_with_cap_in_context() if you need role names to display in the UI * - * @param obj $context - * @param int $roleid - * @param string $cap capability, optional, defaults to '' - * @return array of capabilities + * @param object $context + * @param string $capability + * @return array($neededroles, $forbiddenroles) */ -function role_context_capabilities($roleid, $context, $cap = '') { +function get_roles_with_cap_in_context($context, $capability) { global $DB; - $contexts = get_parent_contexts($context); - $contexts[] = $context->id; - $contexts = '('.implode(',', $contexts).')'; + $ctxids = trim($context->path, '/'); // kill leading slash + $ctxids = str_replace('/', ',', $ctxids); - $params = array($roleid); + $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.depth + FROM {role_capabilities} rc + JOIN {context} ctx ON ctx.id = rc.contextid + WHERE rc.capability = :cap AND ctx.id IN ($ctxids) + ORDER BY rc.roleid ASC, ctx.depth DESC"; + $params = array('cap'=>$capability); - if ($cap) { - $search = " AND rc.capability = ? "; - $params[] = $cap; - } else { - $search = ''; + if (!$capdefs = $DB->get_records_sql($sql, $params)) { + // no cap definitions --> no capability + return array(array(), array()); } - $sql = "SELECT rc.* - FROM {role_capabilities} rc, {context} c - WHERE rc.contextid in $contexts - AND rc.roleid = ? - AND rc.contextid = c.id $search - ORDER BY c.contextlevel DESC, rc.capability DESC"; - - $capabilities = array(); - - if ($records = $DB->get_records_sql($sql, $params)) { - // We are traversing via reverse order. - foreach ($records as $record) { - // If not set yet (i.e. inherit or not set at all), or currently we have a prohibit - if (!isset($capabilities[$record->capability]) || $record->permission<-500) { - $capabilities[$record->capability] = $record->permission; + $forbidden = array(); + $needed = array(); + foreach($capdefs as $def) { + if (isset($forbidden[$def->roleid])) { + continue; + } + if ($def->permission == CAP_PROHIBIT) { + $forbidden[$def->roleid] = $def->roleid; + unset($needed[$def->roleid]); + continue; + } + if (!isset($needed[$def->roleid])) { + if ($def->permission == CAP_ALLOW) { + $needed[$def->roleid] = true; + } else if ($def->permission == CAP_PREVENT) { + $needed[$def->roleid] = false; } } } - return $capabilities; -} - -/** - * Recursive function which, given a context, find all parent context ids, - * and return the array in reverse order, i.e. parent first, then grand - * parent, etc. - * - * @param object $context - * @param bool $capability optional, defaults to false - * @return array - */ -function get_parent_contexts($context, $includeself = false) { - - if ($context->path == '') { - return array(); - } + unset($capdefs); - $parentcontexts = substr($context->path, 1); // kill leading slash - $parentcontexts = explode('/', $parentcontexts); - if (!$includeself) { - array_pop($parentcontexts); // and remove its own id + // remove all those roles not allowing + foreach($needed as $key=>$value) { + if (!$value) { + unset($needed[$key]); + } else { + $needed[$key] = $key; + } } - return array_reverse($parentcontexts); + return array($needed, $forbidden); } /** - * Return the id of the parent of this context, or false if there is no parent (only happens if this - * is the site context.) + * Returns an array of role IDs that have ALL of the the supplied capabilities + * Uses get_roles_with_cap_in_context(). Returns $allowed minus $forbidden * * @param object $context - * @return integer the id of the parent context. + * @param array $capabilities An array of capabilities + * @return array of roles with all of the required capabilities */ -function get_parent_contextid($context) { - $parentcontexts = get_parent_contexts($context); - if (count($parentcontexts) == 0) { - return false; +function get_roles_with_caps_in_context($context, $capabilities) { + $neededarr = array(); + $forbiddenarr = array(); + foreach($capabilities as $caprequired) { + list($neededarr[], $forbiddenarr[]) = get_roles_with_cap_in_context($context, $caprequired); } - return array_shift($parentcontexts); -} -/** - * Constructs array with contextids as first parameter and context paths, - * in both cases bottom top including self. - * - * @param object $context - * @return array - */ -function get_context_info_list($context) { - $contextids = explode('/', ltrim($context->path, '/')); - $contextpaths = array(); - $contextids2 = $contextids; - while ($contextids2) { - $contextpaths[] = '/' . implode('/', $contextids2); - array_pop($contextids2); + $rolesthatcanrate = array(); + if (!empty($neededarr)) { + foreach ($neededarr as $needed) { + if (empty($rolesthatcanrate)) { + $rolesthatcanrate = $needed; + } else { + //only want roles that have all caps + $rolesthatcanrate = array_intersect_key($rolesthatcanrate,$needed); + } + } } - return array($contextids, $contextpaths); + if (!empty($forbiddenarr) && !empty($rolesthatcanrate)) { + foreach ($forbiddenarr as $forbidden) { + //remove any roles that are forbidden any of the caps + $rolesthatcanrate = array_diff($rolesthatcanrate, $forbidden); + } + } + return $rolesthatcanrate; } /** - * Find course context - * @param object $context - course or lower context - * @return object context of the enclosing course, throws exception when related course context can not be found + * Returns an array of role names that have ALL of the the supplied capabilities + * Uses get_roles_with_caps_in_context(). Returns $allowed minus $forbidden + * + * @param object $context + * @param array $capabilities An array of capabilities + * @return array of roles with all of the required capabilities */ -function get_course_context($context) { - if (empty($context->contextlevel)) { - throw new coding_exception('Invalid context parameter.'); - - } if ($context->contextlevel == CONTEXT_COURSE) { - return $context; +function get_role_names_with_caps_in_context($context, $capabilities) { + global $DB; - } else if ($context->contextlevel == CONTEXT_MODULE) { - return get_context_instance_by_id(get_parent_contextid($context, MUST_EXIST)); + $rolesthatcanrate = get_roles_with_caps_in_context($context, $capabilities); - } else if ($context->contextlevel == CONTEXT_BLOCK) { - $parentcontext = get_context_instance_by_id(get_parent_contextid($context, MUST_EXIST)); - if ($parentcontext->contextlevel == CONTEXT_COURSE) { - return $parentcontext; - } else if ($parentcontext->contextlevel == CONTEXT_MODULE) { - return get_context_instance_by_id(get_parent_contextid($parentcontext, MUST_EXIST)); - } else { - throw new coding_exception('Invalid level of block context parameter.'); - } + $allroles = array(); + $roles = $DB->get_records('role', null, 'sortorder DESC'); + foreach ($roles as $roleid=>$role) { + $allroles[$roleid] = $role->name; } - throw new coding_exception('Invalid context level of parameter.'); + $rolenames = array(); + foreach ($rolesthatcanrate as $r) { + $rolenames[$r] = $allroles[$r]; + } + $rolenames = role_fix_names($rolenames, $context); + return $rolenames; } /** - * Check if context is the front page context or a context inside it - * - * Returns true if this context is the front page context, or a context inside it, - * otherwise false. + * This function verifies the prohibit comes from this context + * and there are no more prohibits in parent contexts. * - * @param object $context a context object. + * @param int $roleid + * @param context $context + * @param string $capability name * @return bool */ -function is_inside_frontpage($context) { - $frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID); - return strpos($context->path . '/', $frontpagecontext->path . '/') === 0; +function prohibit_is_removable($roleid, context $context, $capability) { + global $DB; + + $ctxids = trim($context->path, '/'); // kill leading slash + $ctxids = str_replace('/', ',', $ctxids); + + $params = array('roleid'=>$roleid, 'cap'=>$capability, 'prohibit'=>CAP_PROHIBIT); + + $sql = "SELECT ctx.id + FROM {role_capabilities} rc + JOIN {context} ctx ON ctx.id = rc.contextid + WHERE rc.roleid = :roleid AND rc.permission = :prohibit AND rc.capability = :cap AND ctx.id IN ($ctxids) + ORDER BY ctx.depth DESC"; + + if (!$prohibits = $DB->get_records_sql($sql, $params)) { + // no prohibits == nothing to remove + return true; + } + + if (count($prohibits) > 1) { + // more prohibints can not be removed + return false; + } + + return !empty($prohibits[$context->id]); } /** - * Runs get_records select on context table and returns the result - * Does get_records_select on the context table, and returns the results ordered - * by contextlevel, and then the natural sort order within each level. - * for the purpose of $select, you need to know that the context table has been - * aliased to ctx, so for example, you can call get_sorted_contexts('ctx.depth = 3'); - * - * @param string $select the contents of the WHERE clause. Remember to do ctx.fieldname. - * @param array $params any parameters required by $select. - * @return array the requested context records. + * More user friendly role permission changing, + * it should produce as few overrides as possible. + * @param int $roleid + * @param object $context + * @param string $capname capability name + * @param int $permission + * @return void */ -function get_sorted_contexts($select, $params = array()) { +function role_change_permission($roleid, $context, $capname, $permission) { global $DB; - if ($select) { - $select = 'WHERE ' . $select; + + if ($permission == CAP_INHERIT) { + unassign_capability($capname, $roleid, $context->id); + $context->mark_dirty(); + return; } - return $DB->get_records_sql(" - SELECT ctx.* - FROM {context} ctx - LEFT JOIN {user} u ON ctx.contextlevel = " . CONTEXT_USER . " AND u.id = ctx.instanceid - LEFT JOIN {course_categories} cat ON ctx.contextlevel = " . CONTEXT_COURSECAT . " AND cat.id = ctx.instanceid - LEFT JOIN {course} c ON ctx.contextlevel = " . CONTEXT_COURSE . " AND c.id = ctx.instanceid - LEFT JOIN {course_modules} cm ON ctx.contextlevel = " . CONTEXT_MODULE . " AND cm.id = ctx.instanceid - LEFT JOIN {block_instances} bi ON ctx.contextlevel = " . CONTEXT_BLOCK . " AND bi.id = ctx.instanceid - $select - ORDER BY ctx.contextlevel, bi.defaultregion, COALESCE(cat.sortorder, c.sortorder, cm.section, bi.defaultweight), u.lastname, u.firstname, cm.id - ", $params); -} -/** - * Recursive function which, given a context, find all its children context ids. - * - * When called for a course context, it will return the modules and blocks - * displayed in the course page. - * - * For course category contexts it will return categories and courses. It will - * NOT recurse into courses, nor return blocks on the category pages. If you - * want to do that, call it on the returned courses. - * - * If called on a course context it _will_ populate the cache with the appropriate - * contexts ;-) - * - * @param object $context. - * @return array Array of child records - */ -function get_child_contexts($context) { + $ctxids = trim($context->path, '/'); // kill leading slash + $ctxids = str_replace('/', ',', $ctxids); - global $DB, $ACCESSLIB_PRIVATE; + $params = array('roleid'=>$roleid, 'cap'=>$capname); - // We *MUST* populate the context_cache as the callers - // will probably ask for the full record anyway soon after - // soon after calling us ;-) - - $array = array(); - $cache = $ACCESSLIB_PRIVATE->contexcache; - - switch ($context->contextlevel) { - - case CONTEXT_BLOCK: - // No children. - break; - - case CONTEXT_MODULE: - // Find - // - blocks under this context path. - $sql = " SELECT ctx.* - FROM {context} ctx - WHERE ctx.path LIKE ? - AND ctx.contextlevel = ".CONTEXT_BLOCK; - $params = array("{$context->path}/%", $context->instanceid); - $records = $DB->get_recordset_sql($sql, $params); - foreach ($records as $rec) { - $cache->add($rec); - $array[$rec->id] = $rec; - } - break; + $sql = "SELECT ctx.id, rc.permission, ctx.depth + FROM {role_capabilities} rc + JOIN {context} ctx ON ctx.id = rc.contextid + WHERE rc.roleid = :roleid AND rc.capability = :cap AND ctx.id IN ($ctxids) + ORDER BY ctx.depth DESC"; - case CONTEXT_COURSE: - // Find - // - modules and blocks under this context path. - $sql = " SELECT ctx.* - FROM {context} ctx - WHERE ctx.path LIKE ? - AND ctx.contextlevel IN (".CONTEXT_MODULE.",".CONTEXT_BLOCK.")"; - $params = array("{$context->path}/%", $context->instanceid); - $records = $DB->get_recordset_sql($sql, $params); - foreach ($records as $rec) { - $cache->add($rec); - $array[$rec->id] = $rec; - } - break; - - case CONTEXT_COURSECAT: - // Find - // - categories - // - courses - $sql = " SELECT ctx.* - FROM {context} ctx - WHERE ctx.path LIKE ? - AND ctx.contextlevel IN (".CONTEXT_COURSECAT.",".CONTEXT_COURSE.")"; - $params = array("{$context->path}/%"); - $records = $DB->get_recordset_sql($sql, $params); - foreach ($records as $rec) { - $cache->add($rec); - $array[$rec->id] = $rec; + if ($existing = $DB->get_records_sql($sql, $params)) { + foreach($existing as $e) { + if ($e->permission == CAP_PROHIBIT) { + // prohibit can not be overridden, no point in changing anything + return; } - break; - - case CONTEXT_USER: - // Find - // - blocks under this context path. - $sql = " SELECT ctx.* - FROM {context} ctx - WHERE ctx.path LIKE ? - AND ctx.contextlevel = ".CONTEXT_BLOCK; - $params = array("{$context->path}/%", $context->instanceid); - $records = $DB->get_recordset_sql($sql, $params); - foreach ($records as $rec) { - $cache->add($rec); - $array[$rec->id] = $rec; + } + $lowest = array_shift($existing); + if ($lowest->permission == $permission) { + // permission already set in this context or parent - nothing to do + return; + } + if ($existing) { + $parent = array_shift($existing); + if ($parent->permission == $permission) { + // permission already set in parent context or parent - just unset in this context + // we do this because we want as few overrides as possible for performance reasons + unassign_capability($capname, $roleid, $context->id); + $context->mark_dirty(); + return; } - break; + } - case CONTEXT_SYSTEM: - // Just get all the contexts except for CONTEXT_SYSTEM level - // and hope we don't OOM in the process - don't cache - $sql = "SELECT c.* - FROM {context} c - WHERE contextlevel != ".CONTEXT_SYSTEM; + } else { + if ($permission == CAP_PREVENT) { + // nothing means role does not have permission + return; + } + } - $records = $DB->get_records_sql($sql); - foreach ($records as $rec) { - $array[$rec->id] = $rec; - } - break; + // assign the needed capability + assign_capability($capname, $permission, $roleid, $context->id, true); - default: - print_error('unknowcontext', '', '', $context->contextlevel); - return false; - } - return $array; + // force cap reloading + $context->mark_dirty(); } /** - * Gets a string for sql calls, searching for stuff in this context or above + * Context maintenance and helper methods. * - * @param object $context - * @return string - */ -function get_related_contexts_string($context) { - if ($parents = get_parent_contexts($context)) { - return (' IN ('.$context->id.','.implode(',', $parents).')'); - } else { - return (' ='.$context->id); - } -} - -/** - * Returns capability information (cached) + * This is "extends context" is a bloody hack that tires to work around the deficiencies + * in the "protected" keyword in PHP, this helps us to hide all the internals of context + * level implementation from the rest of code, the code completion returns what developers need. * - * @param string $capabilityname - * @return object or null if capability not found + * Thank you Tim Hunt for helping me with this nasty trick. + * + * @author Petr Skoda */ -function get_capability_info($capabilityname) { - global $ACCESSLIB_PRIVATE, $DB; // one request per page only +class context_helper extends context { - // TODO: cache this in shared memory if available, use new $CFG->roledefrev for version check + private static $alllevels = array( + CONTEXT_SYSTEM => 'context_system', + CONTEXT_USER => 'context_user', + CONTEXT_COURSECAT => 'context_coursecat', + CONTEXT_COURSE => 'context_course', + CONTEXT_MODULE => 'context_module', + CONTEXT_BLOCK => 'context_block', + ); - if (empty($ACCESSLIB_PRIVATE->capabilities)) { - $ACCESSLIB_PRIVATE->capabilities = array(); - $caps = $DB->get_records('capabilities', array(), 'id, name, captype, riskbitmask'); - foreach ($caps as $cap) { - $capname = $cap->name; - unset($cap->id); - unset($cap->name); - $ACCESSLIB_PRIVATE->capabilities[$capname] = $cap; + /** + * Instance does not make sense here, only static use + */ + protected function __construct() { + } + + /** + * Returns a class name of the context level class + * + * @static + * @param int $contextlevel (CONTEXT_SYSTEM, etc.) + * @return string class name of the context class + */ + public static function get_class_for_level($contextlevel) { + if (isset(self::$alllevels[$contextlevel])) { + return self::$alllevels[$contextlevel]; + } else { + throw new coding_exception('Invalid context level specified'); } } - return isset($ACCESSLIB_PRIVATE->capabilities[$capabilityname]) ? $ACCESSLIB_PRIVATE->capabilities[$capabilityname] : null; -} + /** + * Returns a list of all context levels + * + * @static + * @return array int=>string (level=>level class name) + */ + public static function get_all_levels() { + return self::$alllevels; + } -/** - * Returns the human-readable, translated version of the capability. - * Basically a big switch statement. - * - * @param string $capabilityname e.g. mod/choice:readresponses - * @return string - */ -function get_capability_string($capabilityname) { + /** + * Remove stale contexts that belonged to deleted instances. + * Ideally all code should cleanup contexts properly, unfortunately accidents happen... + * + * @static + * @return void + */ + public static function cleanup_instances() { + global $DB; + $sqls = array(); + foreach (self::$alllevels as $level=>$classname) { + $sqls[] = $classname::get_cleanup_sql(); + } - // Typical capability name is 'plugintype/pluginname:capabilityname' - list($type, $name, $capname) = preg_split('|[/:]|', $capabilityname); + $sql = implode(" UNION ", $sqls); - if ($type === 'moodle') { - $component = 'core_role'; - } else if ($type === 'quizreport') { - //ugly hack!! - $component = 'quiz_'.$name; - } else { - $component = $type.'_'.$name; + // it is probably better to use transactions, it might be faster too + $transaction = $DB->start_delegated_transaction(); + + $rs = $DB->get_recordset_sql($sql); + foreach ($rs as $record) { + $context = context::create_instance_from_record($record); + $context->delete(); + } + $rs->close(); + + $transaction->allow_commit(); } - $stringname = $name.':'.$capname; + /** + * Create all context instances at the given level and above. + * + * @static + * @param int $contextlevel null means all levels + * @param bool $buildpaths + * @return void + */ + public static function create_instances($contextlevel = null, $buildpaths = true) { + foreach (self::$alllevels as $level=>$classname) { + if ($contextlevel and $level > $contextlevel) { + // skip potential sub-contexts + continue; + } + $classname::create_level_instances(); + if ($buildpaths) { + $classname::build_paths(false); + } + } + } - if ($component === 'core_role' or get_string_manager()->string_exists($stringname, $component)) { - return get_string($stringname, $component); + /** + * Rebuild paths and depths in all context levels. + * + * @static + * @param bool $force false means add missing only + * @return void + */ + public static function build_all_paths($force = false) { + foreach (self::$alllevels as $classname) { + $classname::build_paths($force); + } + + // reset static course cache - it might have incorrect cached data + accesslib_clear_all_caches(true); } - $dir = get_component_directory($component); - if (!file_exists($dir)) { - // plugin broken or does not exist, do not bother with printing of debug message - return $capabilityname.' ???'; + /** + * Resets the cache to remove all data. + * @static + */ + public static function reset_caches() { + context::reset_caches(); } - // something is wrong in plugin, better print debug - return get_string($stringname, $component); -} + /** + * Returns all fields necessary for context preloading from user $rec. + * + * This helps with performance when dealing with hundreds of contexts. + * + * @static + * @param string $tablealias context table alias in the query + * @return array (table.column=>alias, ...) + */ + public static function get_preload_record_columns($tablealias) { + return array("$tablealias.id"=>"ctxid", "$tablealias.path"=>"ctxpath", "$tablealias.depth"=>"ctxdepth", "$tablealias.contextlevel"=>"ctxlevel", "$tablealias.instanceid"=>"ctxinstance"); + } + + /** + * Returns all fields necessary for context preloading from user $rec. + * + * This helps with performance when dealing with hundreds of contexts. + * + * @static + * @param string $tablealias context table alias in the query + * @return string + */ + public static function get_preload_record_columns_sql($tablealias) { + return "$tablealias.id AS ctxid, $tablealias.path AS ctxpath, $tablealias.depth AS ctxdepth, $tablealias.contextlevel AS ctxlevel, $tablealias.instanceid AS ctxinstance"; + } + + /** + * Preloads context information from db record and strips the cached info. + * + * The db request has to contain all columns from context_helper::get_preload_record_columns(). + * + * @static + * @param stdClass $rec + * @return void (modifies $rec) + */ + public static function preload_from_record(stdClass $rec) { + context::preload_from_record($rec); + } + /** + * Preload all contexts instances from course. + * + * To be used if you expect multiple queries for course activities... + * + * @static + * @param $courseid + */ + public static function preload_course($courseid) { + // Users can call this multiple times without doing any harm + if (isset(context::$cache_preloaded[$courseid])) { + return; + } + $coursecontext = context_course::instance($courseid); + $coursecontext->get_child_contexts(); -/** - * This gets the mod/block/course/core etc strings. - * - * @param string $component - * @param int $contextlevel - * @return string|bool String is success, false if failed - */ -function get_component_string($component, $contextlevel) { + context::$cache_preloaded[$courseid] = true; + } - if ($component === 'moodle' or $component === 'core') { - switch ($contextlevel) { - case CONTEXT_SYSTEM: return get_string('coresystem'); - case CONTEXT_USER: return get_string('users'); - case CONTEXT_COURSECAT: return get_string('categories'); - case CONTEXT_COURSE: return get_string('course'); - case CONTEXT_MODULE: return get_string('activities'); - case CONTEXT_BLOCK: return get_string('block'); - default: print_error('unknowncontext'); + /** + * Delete context instance + * + * @static + * @param int $contextlevel + * @param int $instanceid + * @return void + */ + public static function delete_instance($contextlevel, $instanceid) { + global $DB; + + // double check the context still exists + if ($record = $DB->get_record('context', array('contextlevel'=>$contextlevel, 'instanceid'=>$instanceid))) { + $context = context::create_instance_from_record($record); + $context->delete(); + } else { + // we should try to purge the cache anyway } } - list($type, $name) = normalize_component($component); - $dir = get_plugin_directory($type, $name); - if (!file_exists($dir)) { - // plugin not installed, bad luck, there is no way to find the name - return $component.' ???'; + /** + * Returns the name of specified context level + * + * @static + * @param int $contextlevel + * @return string name of the context level + */ + public static function get_level_name($contextlevel) { + $classname = context_helper::get_class_for_level($contextlevel); + return $classname::get_level_name(); } - switch ($type) { - // TODO this is really hacky - case 'quiz': return get_string($name.':componentname', $component);// insane hack!!! - case 'repository': return get_string('repository', 'repository').': '.get_string('pluginname', $component); - case 'gradeimport': return get_string('gradeimport', 'grades').': '.get_string('pluginname', $component); - case 'gradeexport': return get_string('gradeexport', 'grades').': '.get_string('pluginname', $component); - case 'gradereport': return get_string('gradereport', 'grades').': '.get_string('pluginname', $component); - case 'webservice': return get_string('webservice', 'webservice').': '.get_string('pluginname', $component); - case 'block': return get_string('block').': '.get_string('pluginname', basename($component)); - case 'mod': - if (get_string_manager()->string_exists('pluginname', $component)) { - return get_string('activity').': '.get_string('pluginname', $component); - } else { - return get_string('activity').': '.get_string('modulename', $component); - } - default: return get_string('pluginname', $component); + /** + * not used + */ + public function get_url() { + } + + /** + * not used + */ + public function get_capabilities() { } } + /** - * Gets the list of roles assigned to this context and up (parents) - * from the list of roles that are visible on user profile page - * and participants page. + * Basic moodle context abstraction class. * - * @param object $context - * @return array + * @author Petr Skoda + * + * @property-read int $id context id + * @property-read int $contextlevel CONTEXT_SYSTEM, CONTEXT_COURSE, etc. + * @property-read int $instanceid id of related instance in each context + * @property-read string $path path to context, starts with system context + * @property-read dept $depth */ -function get_profile_roles($context) { - global $CFG, $DB; +abstract class context extends stdClass { - if (empty($CFG->profileroles)) { - return array(); - } + /* + * Google confirms that no other important framework is using "context" class, + * we could use something else like mcontext or moodle_context, but we need to type + * this very often which would be annoying and it would take too much space... + * + * This class is derived from stdClass for backwards compatibility with + * odl $context record that was returned from DML $DB->get_record() + */ - $allowed = explode(',', $CFG->profileroles); - list($rallowed, $params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED); + protected $_id; + protected $_contextlevel; + protected $_instanceid; + protected $_path; + protected $_depth; - $contextlist = get_related_contexts_string($context); + /* context caching info */ - $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder - FROM {role_assignments} ra, {role} r - WHERE r.id = ra.roleid - AND ra.contextid $contextlist - AND r.id $rallowed - ORDER BY r.sortorder ASC"; + private static $cache_contextsbyid = array(); + private static $cache_contexts = array(); + protected static $cache_count = 0; // why do we do count contexts? Because count($array) is horribly slow for large arrays - return $DB->get_records_sql($sql, $params); -} + protected static $cache_preloaded = array(); + protected static $systemcontext = null; -/** - * Gets the list of roles assigned to this context and up (parents) - * - * @param object $context - * @return array - */ -function get_roles_used_in_context($context) { - global $DB; + /** + * Resets the cache to remove all data. + */ + protected static function reset_caches() { + self::$cache_contextsbyid = array(); + self::$cache_contexts = array(); + self::$cache_count = 0; + self::$cache_preloaded = array(); - $contextlist = get_related_contexts_string($context); + self::$systemcontext = null; + } - $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder - FROM {role_assignments} ra, {role} r - WHERE r.id = ra.roleid - AND ra.contextid $contextlist - ORDER BY r.sortorder ASC"; + /** + * Adds a context to the cache. If the cache is full, discards a batch of + * older entries. + * + * @static + * @param context $context New context to add + * @return void + */ + protected static function cache_add(context $context) { + if (isset(self::$cache_contextsbyid[$context->id])) { + // already cached, no need to do anything - this is relatively cheap, we do all this because count() is slow + return; + } - return $DB->get_records_sql($sql); -} + if (self::$cache_count >= CONTEXT_CACHE_MAX_SIZE) { + $i = 0; + foreach(self::$cache_contextsbyid as $ctx) { + $i++; + if ($i <= 100) { + // we want to keep the first contexts to be loaded on this page, hopefully they will be needed again later + continue; + } + if ($i > (CONTEXT_CACHE_MAX_SIZE / 3)) { + // we remove oldest third of the contexts to make room for more contexts + break; + } + unset(self::$cache_contextsbyid[$ctx->id]); + unset(self::$cache_contexts[$ctx->contextlevel][$ctx->instanceid]); + self::$cache_count--; + } + } -/** - * This function is used to print roles column in user profile page. - * It is using the CFG->profileroles to limit the list to only interesting roles. - * (The permission tab has full details of user role assignments.) - * - * @param int $userid - * @param int $courseid - * @return string - */ -function get_user_roles_in_course($userid, $courseid) { - global $CFG, $DB,$USER; + self::$cache_contexts[$context->contextlevel][$context->instanceid] = $context; + self::$cache_contextsbyid[$context->id] = $context; + self::$cache_count++; + } - if (empty($CFG->profileroles)) { - return ''; + /** + * Removes a context from the cache. + * + * @static + * @param context $context Context object to remove + * @return void + */ + protected static function cache_remove(context $context) { + if (!isset(self::$cache_contextsbyid[$context->id])) { + // not cached, no need to do anything - this is relatively cheap, we do all this because count() is slow + return; + } + unset(self::$cache_contexts[$context->contextlevel][$context->instanceid]); + unset(self::$cache_contextsbyid[$context->id]); + + self::$cache_count--; + + if (self::$cache_count < 0) { + self::$cache_count = 0; + } } - if ($courseid == SITEID) { - $context = get_context_instance(CONTEXT_SYSTEM); - } else { - $context = get_context_instance(CONTEXT_COURSE, $courseid); + /** + * Gets a context from the cache. + * + * @static + * @param int $contextlevel Context level + * @param int $instance Instance ID + * @return context|bool Context or false if not in cache + */ + protected static function cache_get($contextlevel, $instance) { + if (isset(self::$cache_contexts[$contextlevel][$instance])) { + return self::$cache_contexts[$contextlevel][$instance]; + } + return false; } - if (empty($CFG->profileroles)) { - return array(); + /** + * Gets a context from the cache based on its id. + * + * @static + * @param int $id Context ID + * @return context|bool Context or false if not in cache + */ + protected static function cache_get_by_id($id) { + if (isset(self::$cache_contextsbyid[$id])) { + return self::$cache_contextsbyid[$id]; + } + return false; } - $allowed = explode(',', $CFG->profileroles); - list($rallowed, $params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED); + /** + * Preloads context information from db record and strips the cached info. + * + * @static + * @param stdClass $rec + * @return void (modifies $rec) + */ + protected static function preload_from_record(stdClass $rec) { + if (empty($rec->ctxid) or empty($rec->ctxlevel) or empty($rec->ctxinstance) or empty($rec->ctxpath) or empty($rec->ctxdepth)) { + // $rec does not have enough data, passed here repeatedly or context does not exist yet + return; + } - $contextlist = get_related_contexts_string($context); + // note: in PHP5 the objects are passed by reference, no need to return $rec + $record = new stdClass(); + $record->id = $rec->ctxid; unset($rec->ctxid); + $record->contextlevel = $rec->ctxlevel; unset($rec->ctxlevel); + $record->instanceid = $rec->ctxinstance; unset($rec->ctxinstance); + $record->path = $rec->ctxpath; unset($rec->ctxpath); + $record->depth = $rec->ctxdepth; unset($rec->ctxdepth); - $sql = "SELECT DISTINCT r.id, r.name, r.shortname, r.sortorder - FROM {role_assignments} ra, {role} r - WHERE r.id = ra.roleid - AND ra.contextid $contextlist - AND r.id $rallowed - AND ra.userid = :userid - ORDER BY r.sortorder ASC"; - $params['userid'] = $userid; + return context::create_instance_from_record($record); + } - $rolestring = ''; - if ($roles = $DB->get_records_sql($sql, $params)) { - foreach ($roles as $userrole) { - $rolenames[$userrole->id] = $userrole->name; - } + // ====== magic methods ======= - $rolenames = role_fix_names($rolenames, $context); // Substitute aliases + /** + * Magic setter method, we do not want anybody to modify properties from the outside + * @param string $name + * @param mixed @value + */ + public function __set($name, $value) { + debugging('Can not change context instance properties!'); + } - foreach ($rolenames as $roleid => $rolename) { - $rolenames[$roleid] = ''.$rolename.''; + /** + * Magic method getter, redirects to read only values. + * @param string $name + * @return mixed + */ + public function __get($name) { + switch ($name) { + case 'id': return $this->_id; + case 'contextlevel': return $this->_contextlevel; + case 'instanceid': return $this->_instanceid; + case 'path': return $this->_path; + case 'depth': return $this->_depth; + + default: + debugging('Invalid context property accessed! '.$name); + return null; } - $rolestring = implode(',', $rolenames); } - return $rolestring; -} + /** + * Full support for isset on our magic read only properties. + * @param $name + * @return bool + */ + public function __isset($name) { + switch ($name) { + case 'id': return isset($this->_id); + case 'contextlevel': return isset($this->_contextlevel); + case 'instanceid': return isset($this->_instanceid); + case 'path': return isset($this->_path); + case 'depth': return isset($this->_depth); -/** - * Checks if a user can assign users to a particular role in this context - * - * @param object $context - * @param int $targetroleid - the id of the role you want to assign users to - * @return boolean - */ -function user_can_assign($context, $targetroleid) { - global $DB; + default: return false; + } - // first check if user has override capability - // if not return false; - if (!has_capability('moodle/role:assign', $context)) { - return false; } - // pull out all active roles of this user from this context(or above) - if ($userroles = get_user_roles($context)) { - foreach ($userroles as $userrole) { - // if any in the role_allow_override table, then it's ok - if ($DB->get_record('role_allow_assign', array('roleid'=>$userrole->roleid, 'allowassign'=>$targetroleid))) { - return true; - } - } + + /** + * ALl properties are read only, sorry. + * @param string $name + */ + public function __unset($name) { + debugging('Can not unset context instance properties!'); } - return false; -} + // ====== general context methods ====== -/** - * Returns all site roles in correct sort order. - * - * @return array - */ -function get_all_roles() { - global $DB; - return $DB->get_records('role', null, 'sortorder ASC'); -} + /** + * Constructor is protected so that devs are forced to + * use context_xxx::instance() or context::instance_by_id(). + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + $this->_id = $record->id; + $this->_contextlevel = (int)$record->contextlevel; + $this->_instanceid = $record->instanceid; + $this->_path = $record->path; + $this->_depth = $record->depth; + } -/** - * Returns roles of a specified archetype - * @param string $archetype - * @return array of full role records - */ -function get_archetype_roles($archetype) { - global $DB; - return $DB->get_records('role', array('archetype'=>$archetype), 'sortorder ASC'); -} + /** + * This function is also used to work around 'protected' keyword problems in context_helper. + * @param stdClass $record + * @return context instance + */ + protected static function create_instance_from_record(stdClass $record) { + $classname = context_helper::get_class_for_level($record->contextlevel); -/** - * Gets all the user roles assigned in this context, or higher contexts - * this is mainly used when checking if a user can assign a role, or overriding a role - * i.e. we need to know what this user holds, in order to verify against allow_assign and - * allow_override tables - * - * @param object $context - * @param int $userid - * @param bool $checkparentcontexts defaults to true - * @param string $order defaults to 'c.contextlevel DESC, r.sortorder ASC' - * @return array - */ -function get_user_roles($context, $userid = 0, $checkparentcontexts = true, $order = 'c.contextlevel DESC, r.sortorder ASC') { - global $USER, $DB; + if ($context = context::cache_get_by_id($record->id)) { + return $context; + } - if (empty($userid)) { - if (empty($USER->id)) { - return array(); + $context = new $classname($record); + context::cache_add($context); + + return $context; + } + + /** + * Copy prepared new contexts from temp table to context table, + * we do this in db specific way for perf reasons only. + * @static + */ + protected static function merge_context_temp_table() { + global $DB; + + /* MDL-11347: + * - mysql does not allow to use FROM in UPDATE statements + * - using two tables after UPDATE works in mysql, but might give unexpected + * results in pg 8 (depends on configuration) + * - using table alias in UPDATE does not work in pg < 8.2 + * + * Different code for each database - mostly for performance reasons + */ + + $dbfamily = $DB->get_dbfamily(); + if ($dbfamily == 'mysql') { + $updatesql = "UPDATE {context} ct, {context_temp} temp + SET ct.path = temp.path, + ct.depth = temp.depth + WHERE ct.id = temp.id"; + } else if ($dbfamily == 'oracle') { + $updatesql = "UPDATE {context} ct + SET (ct.path, ct.depth) = + (SELECT temp.path, temp.depth + FROM {context_temp} temp + WHERE temp.id=ct.id) + WHERE EXISTS (SELECT 'x' + FROM {context_temp} temp + WHERE temp.id = ct.id)"; + } else if ($dbfamily == 'postgres' or $dbfamily == 'mssql') { + $updatesql = "UPDATE {context} + SET path = temp.path, + depth = temp.depth + FROM {context_temp} temp + WHERE temp.id={context}.id"; + } else { + // sqlite and others + $updatesql = "UPDATE {context} + SET path = (SELECT path FROM {context_temp} WHERE id = {context}.id), + depth = (SELECT depth FROM {context_temp} WHERE id = {context}.id) + WHERE id IN (SELECT id FROM {context_temp})"; } - $userid = $USER->id; - } - if ($checkparentcontexts) { - $contextids = get_parent_contexts($context); - } else { - $contextids = array(); + $DB->execute($updatesql); } - $contextids[] = $context->id; - list($contextids, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_QM); + /** + * Get a context instance as an object, from a given context id. + * + * @static + * @param int $id context id + * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found; + * MUST_EXIST means throw exception if no record found + * @return context|bool the context object or false if not found + */ + public static function instance_by_id($id, $strictness = MUST_EXIST) { + global $DB; - array_unshift($params, $userid); + if (get_called_class() !== 'context' and get_called_class() !== 'context_helper') { + // some devs might confuse context->id and instanceid, better prevent these mistakes completely + throw new coding_exception('use only context::instance_by_id() for real context levels use ::instance() methods'); + } - $sql = "SELECT ra.*, r.name, r.shortname - FROM {role_assignments} ra, {role} r, {context} c - WHERE ra.userid = ? - AND ra.roleid = r.id - AND ra.contextid = c.id - AND ra.contextid $contextids - ORDER BY $order"; + if ($id == SYSCONTEXTID) { + return context_system::instance(0, $strictness); + } - return $DB->get_records_sql($sql ,$params); -} + if (is_array($id) or is_object($id) or empty($id)) { + throw new coding_exception('Invalid context id specified context::instance_by_id()'); + } -/** - * Creates a record in the role_allow_override table - * - * @param int $sroleid source roleid - * @param int $troleid target roleid - * @return void - */ -function allow_override($sroleid, $troleid) { - global $DB; + if ($context = context::cache_get_by_id($id)) { + return $context; + } - $record = new stdClass(); - $record->roleid = $sroleid; - $record->allowoverride = $troleid; - $DB->insert_record('role_allow_override', $record); -} + if ($record = $DB->get_record('context', array('id'=>$id), '*', $strictness)) { + return context::create_instance_from_record($record); + } -/** - * Creates a record in the role_allow_assign table - * - * @param int $sroleid source roleid - * @param int $troleid target roleid - * @return void - */ -function allow_assign($fromroleid, $targetroleid) { - global $DB; + return false; + } - $record = new stdClass(); - $record->roleid = $fromroleid; - $record->allowassign = $targetroleid; - $DB->insert_record('role_allow_assign', $record); -} + /** + * Update context info after moving context in the tree structure. + * + * @param context $newparent + * @return void + */ + public function update_moved(context $newparent) { + global $DB; -/** - * Creates a record in the role_allow_switch table - * - * @param int $sroleid source roleid - * @param int $troleid target roleid - * @return void - */ -function allow_switch($fromroleid, $targetroleid) { - global $DB; + $frompath = $this->_path; + $newpath = $newparent->path . '/' . $this->_id; - $record = new stdClass(); - $record->roleid = $fromroleid; - $record->allowswitch = $targetroleid; - $DB->insert_record('role_allow_switch', $record); -} + $trans = $DB->start_delegated_transaction(); -/** - * Gets a list of roles that this user can assign in this context - * - * @param object $context the context. - * @param int $rolenamedisplay the type of role name to display. One of the - * ROLENAME_X constants. Default ROLENAME_ALIAS. - * @param bool $withusercounts if true, count the number of users with each role. - * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user. - * @return array if $withusercounts is false, then an array $roleid => $rolename. - * if $withusercounts is true, returns a list of three arrays, - * $rolenames, $rolecounts, and $nameswithcounts. - */ -function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $withusercounts = false, $user = null) { - global $USER, $DB; + $this->mark_dirty(); - // make sure there is a real user specified - if ($user === null) { - $userid = isset($USER->id) ? $USER->id : 0; - } else { - $userid = is_object($user) ? $user->id : $user; + $setdepth = ''; + if (($newparent->depth +1) != $this->_depth) { + $diff = $newparent->depth - $this->_depth + 1; + $setdepth = ", depth = depth + $diff"; + } + $sql = "UPDATE {context} + SET path = ? + $setdepth + WHERE id = ?"; + $params = array($newpath, $this->_id); + $DB->execute($sql, $params); + + $this->_path = $newpath; + $this->_depth = $newparent->depth + 1; + + $sql = "UPDATE {context} + SET path = ".$DB->sql_concat("?", $DB->sql_substr("path", strlen($frompath)+1))." + $setdepth + WHERE path LIKE ?"; + $params = array($newpath, "{$frompath}/%"); + $DB->execute($sql, $params); + + $this->mark_dirty(); + + context::reset_caches(); + + $trans->allow_commit(); } - if (!has_capability('moodle/role:assign', $context, $userid)) { - if ($withusercounts) { - return array(array(), array(), array()); - } else { - return array(); + /** + * Remove all context path info and optionally rebuild it. + * + * @param bool $rebuild + * @return void + */ + public function reset_paths($rebuild = true) { + global $DB; + + if ($this->_path) { + $this->mark_dirty(); + } + $DB->set_field_select('context', 'depth', 0, "path LIKE '%/$this->_id/%'"); + $DB->set_field_select('context', 'path', NULL, "path LIKE '%/$this->_id/%'"); + if ($this->_contextlevel != CONTEXT_SYSTEM) { + $DB->set_field('context', 'depth', 0, array('id'=>$this->_id)); + $DB->set_field('context', 'path', NULL, array('id'=>$this->_id)); + $this->_depth = 0; + $this->_path = null; } - } - $parents = get_parent_contexts($context, true); - $contexts = implode(',' , $parents); + if ($rebuild) { + context_helper::build_all_paths(false); + } - $params = array(); - $extrafields = ''; - if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT or $rolenamedisplay == ROLENAME_SHORT) { - $extrafields .= ', r.shortname'; + context::reset_caches(); } - if ($withusercounts) { - $extrafields = ', (SELECT count(u.id) - FROM {role_assignments} cra JOIN {user} u ON cra.userid = u.id - WHERE cra.roleid = r.id AND cra.contextid = :conid AND u.deleted = 0 - ) AS usercount'; - $params['conid'] = $context->id; - } + /** + * Delete all data linked to content, do not delete the context record itself + */ + public function delete_content() { + global $CFG, $DB; - if (is_siteadmin($userid)) { - // show all roles allowed in this context to admins - $assignrestriction = ""; - } else { - $assignrestriction = "JOIN (SELECT DISTINCT raa.allowassign AS id - FROM {role_allow_assign} raa - JOIN {role_assignments} ra ON ra.roleid = raa.roleid - WHERE ra.userid = :userid AND ra.contextid IN ($contexts) - ) ar ON ar.id = r.id"; - $params['userid'] = $userid; + blocks_delete_all_for_context($this->_id); + filter_delete_all_for_context($this->_id); + + require_once($CFG->dirroot . '/comment/lib.php'); + comment::delete_comments(array('contextid'=>$this->_id)); + + require_once($CFG->dirroot.'/rating/lib.php'); + $delopt = new stdclass(); + $delopt->contextid = $this->_id; + $rm = new rating_manager(); + $rm->delete_ratings($delopt); + + // delete all files attached to this context + $fs = get_file_storage(); + $fs->delete_area_files($this->_id); + + // now delete stuff from role related tables, role_unassign_all + // and unenrol should be called earlier to do proper cleanup + $DB->delete_records('role_assignments', array('contextid'=>$this->_id)); + $DB->delete_records('role_capabilities', array('contextid'=>$this->_id)); + $DB->delete_records('role_names', array('contextid'=>$this->_id)); } - $params['contextlevel'] = $context->contextlevel; - $sql = "SELECT r.id, r.name $extrafields - FROM {role} r - $assignrestriction - JOIN {role_context_levels} rcl ON r.id = rcl.roleid - WHERE rcl.contextlevel = :contextlevel - ORDER BY r.sortorder ASC"; - $roles = $DB->get_records_sql($sql, $params); - $rolenames = array(); - foreach ($roles as $role) { - if ($rolenamedisplay == ROLENAME_SHORT) { - $rolenames[$role->id] = $role->shortname; - continue; + /** + * Delete the context content and the context record itself + */ + public function delete() { + global $DB; + + // double check the context still exists + if (!$DB->record_exists('context', array('id'=>$this->_id))) { + context::cache_remove($this); + return; } - $rolenames[$role->id] = $role->name; - if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { - $rolenames[$role->id] .= ' (' . $role->shortname . ')'; + + $this->delete_content(); + $DB->delete_records('context', array('id'=>$this->_id)); + // purge static context cache if entry present + context::cache_remove($this); + + // do not mark dirty contexts if parents unknown + if (!is_null($this->_path) and $this->_depth > 0) { + $this->mark_dirty(); } } - if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT and $rolenamedisplay != ROLENAME_SHORT) { - $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay); - } - if (!$withusercounts) { - return $rolenames; + // ====== context level related methods ====== + + /** + * Utility method for context creation + * + * @static + * @param int $contextlevel + * @param int $instanceid + * @param string $parentpath + * @return stdClass context record + */ + protected static function insert_context_record($contextlevel, $instanceid, $parentpath) { + global $DB; + + $record = new stdClass(); + $record->contextlevel = $contextlevel; + $record->instanceid = $instanceid; + $record->depth = 0; + $record->path = null; //not known before insert + + $record->id = $DB->insert_record('context', $record); + + // now add path if known - it can be added later + if (!is_null($parentpath)) { + $record->path = $parentpath.'/'.$record->id; + $record->depth = substr_count($record->path, '/'); + $DB->update_record('context', $record); + } + + return $record; } - $rolecounts = array(); - $nameswithcounts = array(); - foreach ($roles as $role) { - $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->usercount . ')'; - $rolecounts[$role->id] = $roles[$role->id]->usercount; + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + // must be implemented in all context levels + throw new coding_exception('can not get level name of abstract context'); } - return array($rolenames, $rolecounts, $nameswithcounts); -} -/** - * Gets a list of roles that this user can switch to in a context - * - * Gets a list of roles that this user can switch to in a context, for the switchrole menu. - * This function just process the contents of the role_allow_switch table. You also need to - * test the moodle/role:switchroles to see if the user is allowed to switch in the first place. - * - * @param object $context a context. - * @return array an array $roleid => $rolename. - */ -function get_switchable_roles($context) { - global $USER, $DB; + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with the + * type of context, e.g. User, Course, Forum, etc. + * @param boolean $short whether to use the short name of the thing. Only applies + * to course contexts + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + // must be implemented in all context levels + throw new coding_exception('can not get name of abstract context'); + } - $systemcontext = get_context_instance(CONTEXT_SYSTEM); + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public abstract function get_url(); - $params = array(); - $extrajoins = ''; - $extrawhere = ''; - if (!is_siteadmin()) { - // Admins are allowed to switch to any role with. - // Others are subject to the additional constraint that the switch-to role must be allowed by - // 'role_allow_switch' for some role they have assigned in this context or any parent. - $parents = get_parent_contexts($context); - $parents[] = $context->id; - $contexts = implode(',' , $parents); + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public abstract function get_capabilities(); - $extrajoins = "JOIN {role_allow_switch} ras ON ras.allowswitch = rc.roleid - JOIN {role_assignments} ra ON ra.roleid = ras.roleid"; - $extrawhere = "WHERE ra.userid = :userid AND ra.contextid IN ($contexts)"; - $params['userid'] = $USER->id; - } + /** + * Recursive function which, given a context, find all its children context ids. + * + * For course category contexts it will return immediate children and all subcategory contexts. + * It will NOT recurse into courses or subcategories categories. + * If you want to do that, call it on the returned courses/categories. + * + * When called for a course context, it will return the modules and blocks + * displayed in the course page and blocks displayed on the module pages. + * + * If called on a user/course/module context it _will_ populate the cache with the appropriate + * contexts ;-) + * + * @return array Array of child records + */ + public function get_child_contexts() { + global $DB; - $query = " - SELECT r.id, r.name - FROM (SELECT DISTINCT rc.roleid - FROM {role_capabilities} rc - $extrajoins - $extrawhere) idlist - JOIN {role} r ON r.id = idlist.roleid - ORDER BY r.sortorder"; + $sql = "SELECT ctx.* + FROM {context} ctx + WHERE ctx.path LIKE ?"; + $params = array($this->_path.'/%'); + $records = $DB->get_records_sql($sql, $params); - $rolenames = $DB->get_records_sql_menu($query, $params); - return role_fix_names($rolenames, $context, ROLENAME_ALIAS); -} + $result = array(); + foreach ($records as $record) { + $result[$record->id] = context::create_instance_from_record($record); + } -/** - * Gets a list of roles that this user can override in this context. - * - * @param object $context the context. - * @param int $rolenamedisplay the type of role name to display. One of the - * ROLENAME_X constants. Default ROLENAME_ALIAS. - * @param bool $withcounts if true, count the number of overrides that are set for each role. - * @return array if $withcounts is false, then an array $roleid => $rolename. - * if $withusercounts is true, returns a list of three arrays, - * $rolenames, $rolecounts, and $nameswithcounts. - */ -function get_overridable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $withcounts = false) { - global $USER, $DB; + return $result; + } - if (!has_any_capability(array('moodle/role:safeoverride', 'moodle/role:override'), $context)) { - if ($withcounts) { - return array(array(), array(), array()); - } else { + /** + * Returns parent contexts of this context in reversed order, i.e. parent first, + * then grand parent, etc. + * + * @param bool $includeself tre means include self too + * @return array of context instances + */ + public function get_parent_contexts($includeself = false) { + if (!$contextids = $this->get_parent_context_ids($includeself)) { return array(); } - } - $parents = get_parent_contexts($context); - $parents[] = $context->id; - $contexts = implode(',' , $parents); + $result = array(); + foreach ($contextids as $contextid) { + $parent = context::instance_by_id($contextid, MUST_EXIST); + $result[$parent->id] = $parent; + } - $params = array(); - $extrafields = ''; - if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { - $extrafields .= ', ro.shortname'; + return $result; } - $params['userid'] = $USER->id; - if ($withcounts) { - $extrafields = ', (SELECT count(rc.id) FROM {role_capabilities} rc - WHERE rc.roleid = ro.id AND rc.contextid = :conid) AS overridecount'; - $params['conid'] = $context->id; + /** + * Returns parent contexts of this context in reversed order, i.e. parent first, + * then grand parent, etc. + * + * @param bool $includeself tre means include self too + * @return array of context ids + */ + public function get_parent_context_ids($includeself = false) { + if (empty($this->_path)) { + return array(); + } + + $parentcontexts = trim($this->_path, '/'); // kill leading slash + $parentcontexts = explode('/', $parentcontexts); + if (!$includeself) { + array_pop($parentcontexts); // and remove its own id + } + + return array_reverse($parentcontexts); } - if (is_siteadmin()) { - // show all roles to admins - $roles = $DB->get_records_sql(" - SELECT ro.id, ro.name$extrafields - FROM {role} ro - ORDER BY ro.sortorder ASC", $params); + /** + * Returns parent context + * + * @return context + */ + public function get_parent_context() { + if (empty($this->_path) or $this->_id == SYSCONTEXTID) { + return false; + } - } else { - $roles = $DB->get_records_sql(" - SELECT ro.id, ro.name$extrafields - FROM {role} ro - JOIN (SELECT DISTINCT r.id - FROM {role} r - JOIN {role_allow_override} rao ON r.id = rao.allowoverride - JOIN {role_assignments} ra ON rao.roleid = ra.roleid - WHERE ra.userid = :userid AND ra.contextid IN ($contexts) - ) inline_view ON ro.id = inline_view.id - ORDER BY ro.sortorder ASC", $params); + $parentcontexts = trim($this->_path, '/'); // kill leading slash + $parentcontexts = explode('/', $parentcontexts); + array_pop($parentcontexts); // self + $contextid = array_pop($parentcontexts); // immediate parent + + return context::instance_by_id($contextid, MUST_EXIST); } - $rolenames = array(); - foreach ($roles as $role) { - $rolenames[$role->id] = $role->name; - if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) { - $rolenames[$role->id] .= ' (' . $role->shortname . ')'; + /** + * Is this context part of any course? If yes return course context. + * + * @param bool $strict true means throw exception if not found, false means return false if not found + * @return course_context context of the enclosing course, null if not found or exception + */ + public function get_course_context($strict = true) { + if ($strict) { + throw new coding_exception('Context does not belong to any course.'); + } else { + return false; } } - if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT) { - $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay); + + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + throw new coding_exception('get_cleanup_sql() method must be implemented in all context levels'); } - if (!$withcounts) { - return $rolenames; -} + /** + * Rebuild context paths and depths at context level. + * + * @static + * @param $force + * @return void + */ + protected static function build_paths($force) { + throw new coding_exception('build_paths() method must be implemented in all context levels'); + } - $rolecounts = array(); - $nameswithcounts = array(); - foreach ($roles as $role) { - $nameswithcounts[$role->id] = $rolenames[$role->id] . ' (' . $roles[$role->id]->overridecount . ')'; - $rolecounts[$role->id] = $roles[$role->id]->overridecount; + /** + * Create missing context instances at given level + * + * @static + * @return void + */ + protected static function create_level_instances() { + throw new coding_exception('create_level_instances() method must be implemented in all context levels'); } - return array($rolenames, $rolecounts, $nameswithcounts); -} -/** - * Create a role menu suitable for default role selection in enrol plugins. - * @param object $context - * @param int $addroleid current or default role - always added to list - * @return array roleid=>localised role name - */ -function get_default_enrol_roles($context, $addroleid = null) { - global $DB; + /** + * Reset all cached permissions and definitions if the necessary. + * @return void + */ + public function reload_if_dirty() { + global $ACCESSLIB_PRIVATE, $USER; - $params = array('contextlevel'=>CONTEXT_COURSE); - if ($addroleid) { - $addrole = "OR r.id = :addroleid"; - $params['addroleid'] = $addroleid; - } else { - $addrole = ""; + // Load dirty contexts list if needed + if (CLI_SCRIPT) { + if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) { + // we do not load dirty flags in CLI and cron + $ACCESSLIB_PRIVATE->dirtycontexts = array(); + } + } else { + if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) { + if (!isset($USER->access['time'])) { + // nothing was loaded yet, we do not need to check dirty contexts now + return; + } + // no idea why -2 is there, server cluster time difference maybe... (skodak) + $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2); + } + } + + foreach ($ACCESSLIB_PRIVATE->dirtycontexts as $path=>$unused) { + if ($path === $this->_path or strpos($this->_path, $path.'/') === 0) { + // reload all capabilities of USER and others - preserving loginas, roleswitches, etc + // and then cleanup any marks of dirtyness... at least from our short term memory! :-) + reload_all_capabilities(); + break; + } + } } - $sql = "SELECT r.id, r.name - FROM {role} r - LEFT JOIN {role_context_levels} rcl ON (rcl.roleid = r.id AND rcl.contextlevel = :contextlevel) - WHERE rcl.id IS NOT NULL $addrole - ORDER BY sortorder DESC"; - $roles = $DB->get_records_sql_menu($sql, $params); - $roles = role_fix_names($roles, $context, ROLENAME_BOTH); + /** + * Mark a context as dirty (with timestamp) so as to force reloading of the context. + */ + public function mark_dirty() { + global $CFG, $USER, $ACCESSLIB_PRIVATE; - return $roles; -} + if (during_initial_install()) { + return; + } -/** - * @param integer $roleid the id of a role. - * @return array list of the context levels at which this role may be assigned. - */ -function get_role_contextlevels($roleid) { - global $DB; - return $DB->get_records_menu('role_context_levels', array('roleid' => $roleid), - 'contextlevel', 'id,contextlevel'); + // only if it is a non-empty string + if (is_string($this->_path) && $this->_path !== '') { + set_cache_flag('accesslib/dirtycontexts', $this->_path, 1, time()+$CFG->sessiontimeout); + if (isset($ACCESSLIB_PRIVATE->dirtycontexts)) { + $ACCESSLIB_PRIVATE->dirtycontexts[$this->_path] = 1; + } else { + if (CLI_SCRIPT) { + $ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1); + } else { + if (isset($USER->access['time'])) { + $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2); + } else { + $ACCESSLIB_PRIVATE->dirtycontexts = array($this->_path => 1); + } + // flags not loaded yet, it will be done later in $context->reload_if_dirty() + } + } + } + } } -/** - * @param integer $contextlevel a contextlevel. - * @return array list of role ids that are assignable at this context level. - */ -function get_roles_for_contextlevels($contextlevel) { - global $DB; - return $DB->get_records_menu('role_context_levels', array('contextlevel' => $contextlevel), - '', 'id,roleid'); -} /** - * @param string $rolearchetype one of the role archetypes - that is, one of the keys - * from the array returned by get_role_archetypes(); - * @return array list of the context levels at which this type of role may be assigned by default. + * Basic context class + * @author Petr Skoda (http://skodak.org) */ -function get_default_contextlevels($rolearchetype) { - static $defaults = array( - 'manager' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE), - 'coursecreator' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT), - 'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE), - 'teacher' => array(CONTEXT_COURSE, CONTEXT_MODULE), - 'student' => array(CONTEXT_COURSE, CONTEXT_MODULE), - 'guest' => array(), - 'user' => array(), - 'frontpage' => array()); +class context_system extends context { + /** + * Please use context_system::instance() if you need the instance of context. + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_SYSTEM) { + throw new coding_exception('Invalid $record->contextlevel in context_system constructor.'); + } + } - if (isset($defaults[$rolearchetype])) { - return $defaults[$rolearchetype]; - } else { - return array(); + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('coresystem'); } -} -/** - * Set the context levels at which a particular role can be assigned. - * Throws exceptions in case of error. - * - * @param integer $roleid the id of a role. - * @param array $contextlevels the context levels at which this role should be assignable, - * duplicate levels are removed. - * @return void - */ -function set_role_contextlevels($roleid, array $contextlevels) { - global $DB; - $DB->delete_records('role_context_levels', array('roleid' => $roleid)); - $rcl = new stdClass(); - $rcl->roleid = $roleid; - $contextlevels = array_unique($contextlevels); - foreach ($contextlevels as $level) { - $rcl->contextlevel = $level; - $DB->insert_record('role_context_levels', $rcl, false, true); + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix does not apply to system context + * @param boolean $short does not apply to system context + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + return self::get_level_name(); } -} -/** - * Who has this capability in this context? - * - * This can be a very expensive call - use sparingly and keep - * the results if you are going to need them again soon. - * - * Note if $fields is empty this function attempts to get u.* - * which can get rather large - and has a serious perf impact - * on some DBs. - * - * @param object $context - * @param string|array $capability - capability name(s) - * @param string $fields - fields to be pulled. The user table is aliased to 'u'. u.id MUST be included. - * @param string $sort - the sort order. Default is lastaccess time. - * @param mixed $limitfrom - number of records to skip (offset) - * @param mixed $limitnum - number of records to fetch - * @param string|array $groups - single group or array of groups - only return - * users who are in one of these group(s). - * @param string|array $exceptions - list of users to exclude, comma separated or array - * @param bool $doanything_ignored not used any more, admin accounts are never returned - * @param bool $view_ignored - use get_enrolled_sql() instead - * @param bool $useviewallgroups if $groups is set the return users who - * have capability both $capability and moodle/site:accessallgroups - * in this context, as well as users who have $capability and who are - * in $groups. - * @return mixed - */ -function get_users_by_capability($context, $capability, $fields = '', $sort = '', $limitfrom = '', $limitnum = '', - $groups = '', $exceptions = '', $doanything_ignored = null, $view_ignored = null, $useviewallgroups = false) { - global $CFG, $DB; + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + return new moodle_url('/'); + } + + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB; + + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display - if (empty($context->id)) { - throw new coding_exception('Invalid context specified'); + $params = array(); + $sql = "SELECT * + FROM {capabilities}"; + + return $DB->get_records_sql($sql.' '.$sort, $params); + } + + /** + * Create missing context instances at system context + * @static + */ + protected static function create_level_instances() { + // nothing to do here, the system context is created automatically in installer + self::instance(0); } - $defaultuserroleid = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : 0; - $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : 0; + /** + * Returns system context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @param bool $cache + * @return context_system context instance + */ + public static function instance($instanceid = 0, $strictness = MUST_EXIST, $cache = true) { + global $DB; + + if ($instanceid != 0) { + debugging('context_system::instance(): invalid $id parameter detected, should be 0'); + } + + if (defined('SYSCONTEXTID') and $cache) { // dangerous: define this in config.php to eliminate 1 query/page + if (!isset(context::$systemcontext)) { + $record = new stdClass(); + $record->id = SYSCONTEXTID; + $record->contextlevel = CONTEXT_SYSTEM; + $record->instanceid = 0; + $record->path = '/'.SYSCONTEXTID; + $record->depth = 1; + context::$systemcontext = new context_system($record); + } + return context::$systemcontext; + } + + + try { + // we ignore the strictness completely because system context must except except during install + $record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST); + } catch (dml_exception $e) { + //table or record does not exist + if (!during_initial_install()) { + // do not mess with system context after install, it simply must exist + throw $e; + } + $record = null; + } + + if (!$record) { + $record = new stdClass(); + $record->contextlevel = CONTEXT_SYSTEM; + $record->instanceid = 0; + $record->depth = 1; + $record->path = null; //not known before insert + + try { + if ($DB->count_records('context')) { + // contexts already exist, this is very weird, system must be first!!! + return null; + } + if (defined('SYSCONTEXTID')) { + // this would happen only in unittest on sites that went through weird 1.7 upgrade + $record->id = SYSCONTEXTID; + $DB->import_record('context', $record); + $DB->get_manager()->reset_sequence('context'); + } else { + $record->id = $DB->insert_record('context', $record); + } + } catch (dml_exception $e) { + // can not create context - table does not exist yet, sorry + return null; + } + } + + if ($record->instanceid != 0) { + // this is very weird, somebody must be messing with context table + debugging('Invalid system context detected'); + } - $ctxids = trim($context->path, '/'); - $ctxids = str_replace('/', ',', $ctxids); + if ($record->depth != 1 or $record->path != '/'.$record->id) { + // fix path if necessary, initial install or path reset + $record->depth = 1; + $record->path = '/'.$record->id; + $DB->update_record('context', $record); + } - // Context is the frontpage - $iscoursepage = false; // coursepage other than fp - $isfrontpage = false; - if ($context->contextlevel == CONTEXT_COURSE) { - if ($context->instanceid == SITEID) { - $isfrontpage = true; - } else { - $iscoursepage = true; + if (!defined('SYSCONTEXTID')) { + define('SYSCONTEXTID', $record->id); } + + context::$systemcontext = new context_system($record); + return context::$systemcontext; } - $isfrontpage = ($isfrontpage || is_inside_frontpage($context)); - $caps = (array)$capability; + /** + * Returns all site contexts except the system context, DO NOT call on production servers!! + * + * Contexts are not cached. + * + * @return array + */ + public function get_child_contexts() { + global $DB; - // construct list of context paths bottom-->top - list($contextids, $paths) = get_context_info_list($context); + debugging('Fetching of system context child courses is strongly discouraged on production servers (it may eat all available memory)!'); - // we need to find out all roles that have these capabilities either in definition or in overrides - $defs = array(); - list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con'); - list($incaps, $params2) = $DB->get_in_or_equal($caps, SQL_PARAMS_NAMED, 'cap'); - $params = array_merge($params, $params2); - $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path - FROM {role_capabilities} rc - JOIN {context} ctx on rc.contextid = ctx.id - WHERE rc.contextid $incontexts AND rc.capability $incaps"; + // Just get all the contexts except for CONTEXT_SYSTEM level + // and hope we don't OOM in the process - don't cache + $sql = "SELECT c.* + FROM {context} c + WHERE contextlevel > ".CONTEXT_SYSTEM; + $records = $DB->get_records_sql($sql); - $rcs = $DB->get_records_sql($sql, $params); - foreach ($rcs as $rc) { - $defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission; + $result = array(); + foreach ($records as $record) { + $result[$record->id] = context::create_instance_from_record($record); + } + + return $result; } - // go through the permissions bottom-->top direction to evaluate the current permission, - // first one wins (prohibit is an exception that always wins) - $access = array(); - foreach ($caps as $cap) { - foreach ($paths as $path) { - if (empty($defs[$cap][$path])) { - continue; - } - foreach($defs[$cap][$path] as $roleid => $perm) { - if ($perm == CAP_PROHIBIT) { - $access[$cap][$roleid] = CAP_PROHIBIT; - continue; - } - if (!isset($access[$cap][$roleid])) { - $access[$cap][$roleid] = (int)$perm; - } - } - } + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + WHERE 1=2 + "; + + return $sql; } - // make lists of roles that are needed and prohibited in this context - $needed = array(); // one of these is enough - $prohibited = array(); // must not have any of these - foreach ($caps as $cap) { - if (empty($access[$cap])) { - continue; + /** + * Rebuild context paths and depths at system context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; + + /* note: ignore $force here, we always do full test of system context */ + + // exactly one record must exist + $record = $DB->get_record('context', array('contextlevel'=>CONTEXT_SYSTEM), '*', MUST_EXIST); + + if ($record->instanceid != 0) { + debugging('Invalid system context detected'); } - foreach ($access[$cap] as $roleid => $perm) { - if ($perm == CAP_PROHIBIT) { - unset($needed[$cap][$roleid]); - $prohibited[$cap][$roleid] = true; - } else if ($perm == CAP_ALLOW and empty($prohibited[$cap][$roleid])) { - $needed[$cap][$roleid] = true; - } + + if (defined('SYSCONTEXTID') and $record->id != SYSCONTEXTID) { + debugging('Invalid SYSCONTEXTID detected'); } - if (empty($needed[$cap]) or !empty($prohibited[$cap][$defaultuserroleid])) { - // easy, nobody has the permission - unset($needed[$cap]); - unset($prohibited[$cap]); - } else if ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid])) { - // everybody is disqualified on the frontapge - unset($needed[$cap]); - unset($prohibited[$cap]); + + if ($record->depth != 1 or $record->path != '/'.$record->id) { + // fix path if necessary, initial install or path reset + $record->depth = 1; + $record->path = '/'.$record->id; + $DB->update_record('context', $record); } - if (empty($prohibited[$cap])) { - unset($prohibited[$cap]); + } +} + + +/** + * User context class + * @author Petr Skoda (http://skodak.org) + */ +class context_user extends context { + /** + * Please use context_user::instance($userid) if you need the instance of context. + * Alternatively if you know only the context id use context::instance_by_id($contextid) + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_USER) { + throw new coding_exception('Invalid $record->contextlevel in context_user constructor.'); } } - if (empty($needed)) { - // there can not be anybody if no roles match this request - return array(); + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('user'); } - if (empty($prohibited)) { - // we can compact the needed roles - $n = array(); - foreach ($needed as $cap) { - foreach ($cap as $roleid=>$unused) { - $n[$roleid] = true; + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with User + * @param boolean $short does not apply to user context + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + global $DB; + + $name = ''; + if ($user = $DB->get_record('user', array('id'=>$this->_instanceid, 'deleted'=>0))) { + if ($withprefix){ + $name = get_string('user').': '; } + $name .= fullname($user); } - $needed = array('any'=>$n); - unset($n); + return $name; } - /// ***** Set up default fields ****** - if (empty($fields)) { - if ($iscoursepage) { - $fields = 'u.*, ul.timeaccess AS lastaccess'; - } else { - $fields = 'u.*'; - } - } else { - if (debugging('', DEBUG_DEVELOPER) && strpos($fields, 'u.*') === false && strpos($fields, 'u.id') === false) { - debugging('u.id must be included in the list of fields passed to get_users_by_capability().', DEBUG_DEVELOPER); - } - } + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + global $COURSE; - /// Set up default sort - if (empty($sort)) { // default to course lastaccess or just lastaccess - if ($iscoursepage) { - $sort = 'ul.timeaccess'; + if ($COURSE->id == SITEID) { + $url = new moodle_url('/user/profile.php', array('id'=>$this->_instanceid)); } else { - $sort = 'u.lastaccess'; + $url = new moodle_url('/user/view.php', array('id'=>$this->_instanceid, 'courseid'=>$COURSE->id)); } + return $url; } - $sortby = "ORDER BY $sort"; - // Prepare query clauses - $wherecond = array(); - $params = array(); - $joins = array(); + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB; - // User lastaccess JOIN - if ((strpos($sort, 'ul.timeaccess') === false) and (strpos($fields, 'ul.timeaccess') === false)) { - // user_lastaccess is not required MDL-13810 - } else { - if ($iscoursepage) { - $joins[] = "LEFT OUTER JOIN {user_lastaccess} ul ON (ul.userid = u.id AND ul.courseid = {$context->instanceid})"; - } else { - throw new coding_exception('Invalid sort in get_users_by_capability(), ul.timeaccess allowed only for course contexts.'); - } + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display + + $extracaps = array('moodle/grade:viewall'); + list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap'); + $sql = "SELECT * + FROM {capabilities} + WHERE contextlevel = ".CONTEXT_USER." + OR name $extra"; + + return $records = $DB->get_records_sql($sql.' '.$sort, $params); } - /// We never return deleted users or guest account. - $wherecond[] = "u.deleted = 0 AND u.id <> :guestid"; - $params['guestid'] = $CFG->siteguest; + /** + * Returns user context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @return context_user context instance + */ + public static function instance($instanceid, $strictness = MUST_EXIST) { + global $DB; - /// Groups - if ($groups) { - $groups = (array)$groups; - list($grouptest, $grpparams) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grp'); - $grouptest = "u.id IN (SELECT userid FROM {groups_members} gm WHERE gm.groupid $grouptest)"; - $params = array_merge($params, $grpparams); + if ($context = context::cache_get(CONTEXT_USER, $instanceid)) { + return $context; + } - if ($useviewallgroups) { - $viewallgroupsusers = get_users_by_capability($context, 'moodle/site:accessallgroups', 'u.id, u.id', '', '', '', '', $exceptions); - if (!empty($viewallgroupsusers)) { - $wherecond[] = "($grouptest OR u.id IN (" . implode(',', array_keys($viewallgroupsusers)) . '))'; - } else { - $wherecond[] = "($grouptest)"; + if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_USER, 'instanceid'=>$instanceid))) { + if ($user = $DB->get_record('user', array('id'=>$instanceid, 'deleted'=>0), 'id', $strictness)) { + $record = context::insert_context_record(CONTEXT_USER, $user->id, '/'.SYSCONTEXTID, 0); } - } else { - $wherecond[] = "($grouptest)"; } + + if ($record) { + $context = new context_user($record); + context::cache_add($context); + return $context; + } + + return false; } - /// User exceptions - if (!empty($exceptions)) { - $exceptions = (array)$exceptions; - list($exsql, $exparams) = $DB->get_in_or_equal($exceptions, SQL_PARAMS_NAMED, 'exc', false); - $params = array_merge($params, $exparams); - $wherecond[] = "u.id $exsql"; + /** + * Create missing context instances at user context level + * @static + */ + protected static function create_level_instances() { + global $DB; + + $sql = "INSERT INTO {context} (contextlevel, instanceid) + SELECT ".CONTEXT_USER.", u.id + FROM {user} u + WHERE u.deleted = 0 + AND NOT EXISTS (SELECT 'x' + FROM {context} cx + WHERE u.id = cx.instanceid AND cx.contextlevel=".CONTEXT_USER.")"; + $DB->execute($sql); } - // now add the needed and prohibited roles conditions as joins - if (!empty($needed['any'])) { - // simple case - there are no prohibits involved - if (!empty($needed['any'][$defaultuserroleid]) or ($isfrontpage and !empty($needed['any'][$defaultfrontpageroleid]))) { - // everybody - } else { - $joins[] = "JOIN (SELECT DISTINCT userid - FROM {role_assignments} - WHERE contextid IN ($ctxids) - AND roleid IN (".implode(',', array_keys($needed['any'])) .") - ) ra ON ra.userid = u.id"; + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + LEFT OUTER JOIN {user} u ON (c.instanceid = u.id AND u.deleted = 0) + WHERE u.id IS NULL AND c.contextlevel = ".CONTEXT_USER." + "; + + return $sql; + } + + /** + * Rebuild context paths and depths at user context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; + + // first update normal users + $sql = "UPDATE {context} + SET depth = 2, + path = ".$DB->sql_concat("'/".SYSCONTEXTID."/'", 'id')." + WHERE contextlevel=".CONTEXT_USER; + $DB->execute($sql); + } +} + + +/** + * Course category context class + * @author Petr Skoda (http://skodak.org) + */ +class context_coursecat extends context { + /** + * Please use context_coursecat::instance($coursecatid) if you need the instance of context. + * Alternatively if you know only the context id use context::instance_by_id($contextid) + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_COURSECAT) { + throw new coding_exception('Invalid $record->contextlevel in context_coursecat constructor.'); } - } else { - $unions = array(); - $everybody = false; - foreach ($needed as $cap=>$unused) { - if (empty($prohibited[$cap])) { - if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) { - $everybody = true; - break; - } else { - $unions[] = "SELECT userid - FROM {role_assignments} - WHERE contextid IN ($ctxids) - AND roleid IN (".implode(',', array_keys($needed[$cap])) .")"; - } - } else { - if (!empty($prohibited[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($prohibited[$cap][$defaultfrontpageroleid]))) { - // nobody can have this cap because it is prevented in default roles - continue; + } + + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('category'); + } + + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with Category + * @param boolean $short does not apply to course categories + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + global $DB; - } else if (!empty($needed[$cap][$defaultuserroleid]) or ($isfrontpage and !empty($needed[$cap][$defaultfrontpageroleid]))) { - // everybody except the prohibitted - hiding does not matter - $unions[] = "SELECT id AS userid - FROM {user} - WHERE id NOT IN (SELECT userid - FROM {role_assignments} - WHERE contextid IN ($ctxids) - AND roleid IN (".implode(',', array_keys($prohibited[$cap])) ."))"; + $name = ''; + if ($category = $DB->get_record('course_categories', array('id'=>$this->_instanceid))) { + if ($withprefix){ + $name = get_string('category').': '; + } + $name .= format_string($category->name, true, array('context' => $this)); + } + return $name; + } + + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + return new moodle_url('/course/category.php', array('id'=>$this->_instanceid)); + } + + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB; + + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display + + $params = array(); + $sql = "SELECT * + FROM {capabilities} + WHERE contextlevel IN (".CONTEXT_COURSECAT.",".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")"; + + return $DB->get_records_sql($sql.' '.$sort, $params); + } + + /** + * Returns course category context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @return context_coursecat context instance + */ + public static function instance($instanceid, $strictness = MUST_EXIST) { + global $DB; + + if ($context = context::cache_get(CONTEXT_COURSECAT, $instanceid)) { + return $context; + } + if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSECAT, 'instanceid'=>$instanceid))) { + if ($category = $DB->get_record('course_categories', array('id'=>$instanceid), 'id,parent', $strictness)) { + if ($category->parent) { + $parentcontext = context_coursecat::instance($category->parent); + $record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, $parentcontext->path); } else { - $unions[] = "SELECT userid - FROM {role_assignments} - WHERE contextid IN ($ctxids) - AND roleid IN (".implode(',', array_keys($needed[$cap])) .") - AND roleid NOT IN (".implode(',', array_keys($prohibited[$cap])) .")"; + $record = context::insert_context_record(CONTEXT_COURSECAT, $category->id, '/'.SYSCONTEXTID, 0); } } } - if (!$everybody) { - if ($unions) { - $joins[] = "JOIN (SELECT DISTINCT userid FROM ( ".implode(' UNION ', $unions)." ) us) ra ON ra.userid = u.id"; - } else { - // only prohibits found - nobody can be matched - $wherecond[] = "1 = 2"; - } + + if ($record) { + $context = new context_coursecat($record); + context::cache_add($context); + return $context; } - } - // Collect WHERE conditions and needed joins - $where = implode(' AND ', $wherecond); - if ($where !== '') { - $where = 'WHERE ' . $where; + return false; } - $joins = implode("\n", $joins); - /// Ok, let's get the users! - $sql = "SELECT $fields - FROM {user} u - $joins - $where - ORDER BY $sort"; + /** + * Returns immediate child contexts of category and all subcategories, + * children of subcategories and courses are not returned. + * + * @return array + */ + public function get_child_contexts() { + global $DB; - return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); -} + $sql = "SELECT ctx.* + FROM {context} ctx + WHERE ctx.path LIKE ? AND (ctx.depth = ? OR ctx.contextlevel = ?)"; + $params = array($this->_path.'/%', $this->depth+1, CONTEXT_COURSECAT); + $records = $DB->get_records_sql($sql, $params); -/** - * Re-sort a users array based on a sorting policy - * - * Will re-sort a $users results array (from get_users_by_capability(), usually) - * based on a sorting policy. This is to support the odd practice of - * sorting teachers by 'authority', where authority was "lowest id of the role - * assignment". - * - * Will execute 1 database query. Only suitable for small numbers of users, as it - * uses an u.id IN() clause. - * - * Notes about the sorting criteria. - * - * As a default, we cannot rely on role.sortorder because then - * admins/coursecreators will always win. That is why the sane - * rule "is locality matters most", with sortorder as 2nd - * consideration. - * - * If you want role.sortorder, use the 'sortorder' policy, and - * name explicitly what roles you want to cover. It's probably - * a good idea to see what roles have the capabilities you want - * (array_diff() them against roiles that have 'can-do-anything' - * to weed out admin-ish roles. Or fetch a list of roles from - * variables like $CFG->coursecontact . - * - * @param array $users Users array, keyed on userid - * @param object $context - * @param array $roles ids of the roles to include, optional - * @param string $policy defaults to locality, more about - * @return array sorted copy of the array - */ -function sort_by_roleassignment_authority($users, $context, $roles = array(), $sortpolicy = 'locality') { - global $DB; + $result = array(); + foreach ($records as $record) { + $result[$record->id] = context::create_instance_from_record($record); + } - $userswhere = ' ra.userid IN (' . implode(',',array_keys($users)) . ')'; - $contextwhere = 'AND ra.contextid IN ('.str_replace('/', ',',substr($context->path, 1)).')'; - if (empty($roles)) { - $roleswhere = ''; - } else { - $roleswhere = ' AND ra.roleid IN ('.implode(',',$roles).')'; + return $result; } - $sql = "SELECT ra.userid - FROM {role_assignments} ra - JOIN {role} r - ON ra.roleid=r.id - JOIN {context} ctx - ON ra.contextid=ctx.id - WHERE $userswhere - $contextwhere - $roleswhere"; + /** + * Create missing context instances at course category context level + * @static + */ + protected static function create_level_instances() { + global $DB; - // Default 'locality' policy -- read PHPDoc notes - // about sort policies... - $orderby = 'ORDER BY ' - .'ctx.depth DESC, ' /* locality wins */ - .'r.sortorder ASC, ' /* rolesorting 2nd criteria */ - .'ra.id'; /* role assignment order tie-breaker */ - if ($sortpolicy === 'sortorder') { - $orderby = 'ORDER BY ' - .'r.sortorder ASC, ' /* rolesorting 2nd criteria */ - .'ra.id'; /* role assignment order tie-breaker */ + $sql = "INSERT INTO {context} (contextlevel, instanceid) + SELECT ".CONTEXT_COURSECAT.", cc.id + FROM {course_categories} cc + WHERE NOT EXISTS (SELECT 'x' + FROM {context} cx + WHERE cc.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSECAT.")"; + $DB->execute($sql); } - $sortedids = $DB->get_fieldset_sql($sql . $orderby); - $sortedusers = array(); - $seen = array(); + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + LEFT OUTER JOIN {course_categories} cc ON c.instanceid = cc.id + WHERE cc.id IS NULL AND c.contextlevel = ".CONTEXT_COURSECAT." + "; - foreach ($sortedids as $id) { - // Avoid duplicates - if (isset($seen[$id])) { - continue; - } - $seen[$id] = true; + return $sql; + } - // assign - $sortedusers[$id] = $users[$id]; + /** + * Rebuild context paths and depths at course category context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; + + if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSECAT." AND (depth = 0 OR path IS NULL)")) { + if ($force) { + $ctxemptyclause = $emptyclause = ''; + } else { + $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)"; + $emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)"; + } + + $base = '/'.SYSCONTEXTID; + + // Normal top level categories + $sql = "UPDATE {context} + SET depth=2, + path=".$DB->sql_concat("'$base/'", 'id')." + WHERE contextlevel=".CONTEXT_COURSECAT." + AND EXISTS (SELECT 'x' + FROM {course_categories} cc + WHERE cc.id = {context}.instanceid AND cc.depth=1) + $emptyclause"; + $DB->execute($sql); + + // Deeper categories - one query per depthlevel + $maxdepth = $DB->get_field_sql("SELECT MAX(depth) FROM {course_categories}"); + for ($n=2; $n<=$maxdepth; $n++) { + $sql = "INSERT INTO {context_temp} (id, path, depth) + SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 + FROM {context} ctx + JOIN {course_categories} cc ON (cc.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_COURSECAT." AND cc.depth = $n) + JOIN {context} pctx ON (pctx.instanceid = cc.parent AND pctx.contextlevel = ".CONTEXT_COURSECAT.") + WHERE pctx.path IS NOT NULL AND pctx.depth > 0 + $ctxemptyclause"; + $trans = $DB->start_delegated_transaction(); + $DB->delete_records('context_temp'); + $DB->execute($sql); + context::merge_context_temp_table(); + $DB->delete_records('context_temp'); + $trans->allow_commit(); + + } + } } - return $sortedusers; } + /** - * Gets all the users assigned this role in this context or higher - * - * @param int $roleid (can also be an array of ints!) - * @param stdClass $context - * @param bool $parent if true, get list of users assigned in higher context too - * @param string $fields fields from user (u.) , role assignment (ra) or role (r.) - * @param string $sort sort from user (u.) , role assignment (ra) or role (r.) - * @param bool $gethidden_ignored use enrolments instead - * @param string $group defaults to '' - * @param mixed $limitfrom defaults to '' - * @param mixed $limitnum defaults to '' - * @param string $extrawheretest defaults to '' - * @param string|array $whereparams defaults to '' - * @return array + * Course context class + * @author Petr Skoda (http://skodak.org) */ -function get_role_users($roleid, $context, $parent = false, $fields = '', - $sort = 'u.lastname, u.firstname', $gethidden_ignored = null, $group = '', - $limitfrom = '', $limitnum = '', $extrawheretest = '', $whereparams = array()) { - global $DB; +class context_course extends context { + /** + * Please use context_course::instance($courseid) if you need the instance of context. + * Alternatively if you know only the context id use context::instance_by_id($contextid) + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_COURSE) { + throw new coding_exception('Invalid $record->contextlevel in context_course constructor.'); + } + } - if (empty($fields)) { - $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '. - 'u.maildisplay, u.mailformat, u.maildigest, u.email, u.city, '. - 'u.country, u.picture, u.idnumber, u.department, u.institution, '. - 'u.lang, u.timezone, u.lastaccess, u.mnethostid, r.name AS rolename, r.sortorder'; + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('course'); } - $parentcontexts = ''; - if ($parent) { - $parentcontexts = substr($context->path, 1); // kill leading slash - $parentcontexts = str_replace('/', ',', $parentcontexts); - if ($parentcontexts !== '') { - $parentcontexts = ' OR ra.contextid IN ('.$parentcontexts.' )'; + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with Course + * @param boolean $short whether to use the short name of the thing. + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + global $DB; + + $name = ''; + if ($this->_instanceid == SITEID) { + $name = get_string('frontpage', 'admin'); + } else { + if ($course = $DB->get_record('course', array('id'=>$this->_instanceid))) { + if ($withprefix){ + $name = get_string('course').': '; + } + if ($short){ + $name .= format_string($course->shortname, true, array('context' => $this)); + } else { + $name .= format_string($course->fullname); + } + } } + return $name; } - if ($roleid) { - list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM); - $roleselect = "AND ra.roleid $rids"; - } else { + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + if ($this->_instanceid != SITEID) { + return new moodle_url('/course/view.php', array('id'=>$this->_instanceid)); + } + + return new moodle_url('/'); + } + + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB; + + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display + $params = array(); - $roleselect = ''; + $sql = "SELECT * + FROM {capabilities} + WHERE contextlevel IN (".CONTEXT_COURSE.",".CONTEXT_MODULE.",".CONTEXT_BLOCK.")"; + + return $DB->get_records_sql($sql.' '.$sort, $params); } - if ($group) { - $groupjoin = "JOIN {groups_members} gm ON gm.userid = u.id"; - $groupselect = " AND gm.groupid = ? "; - $params[] = $group; - } else { - $groupjoin = ''; - $groupselect = ''; + /** + * Is this context part of any course? If yes return course context. + * + * @param bool $strict true means throw exception if not found, false means return false if not found + * @return course_context context of the enclosing course, null if not found or exception + */ + public function get_course_context($strict = true) { + return $this; + } + + /** + * Returns course context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @return context_course context instance + */ + public static function instance($instanceid, $strictness = MUST_EXIST) { + global $DB; + + if ($context = context::cache_get(CONTEXT_COURSE, $instanceid)) { + return $context; + } + + if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$instanceid))) { + if ($course = $DB->get_record('course', array('id'=>$instanceid), 'id,category', $strictness)) { + if ($course->category) { + $parentcontext = context_coursecat::instance($course->category); + $record = context::insert_context_record(CONTEXT_COURSE, $course->id, $parentcontext->path); + } else { + $record = context::insert_context_record(CONTEXT_COURSE, $course->id, '/'.SYSCONTEXTID, 0); + } + } + } + + if ($record) { + $context = new context_course($record); + context::cache_add($context); + return $context; + } + + return false; + } + + /** + * Create missing context instances at course context level + * @static + */ + protected static function create_level_instances() { + global $DB; + + $sql = "INSERT INTO {context} (contextlevel, instanceid) + SELECT ".CONTEXT_COURSE.", c.id + FROM {course} c + WHERE NOT EXISTS (SELECT 'x' + FROM {context} cx + WHERE c.id = cx.instanceid AND cx.contextlevel=".CONTEXT_COURSE.")"; + $DB->execute($sql); } - array_unshift($params, $context->id); + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + LEFT OUTER JOIN {course} co ON c.instanceid = co.id + WHERE co.id IS NULL AND c.contextlevel = ".CONTEXT_COURSE." + "; - if ($extrawheretest) { - $extrawheretest = ' AND ' . $extrawheretest; - $params = array_merge($params, $whereparams); + return $sql; } - $sql = "SELECT DISTINCT $fields, ra.roleid - FROM {role_assignments} ra - JOIN {user} u ON u.id = ra.userid - JOIN {role} r ON ra.roleid = r.id - $groupjoin - WHERE (ra.contextid = ? $parentcontexts) - $roleselect - $groupselect - $extrawheretest - ORDER BY $sort"; // join now so that we can just use fullname() later + /** + * Rebuild context paths and depths at course context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; - return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); + if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_COURSE." AND (depth = 0 OR path IS NULL)")) { + if ($force) { + $ctxemptyclause = $emptyclause = ''; + } else { + $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)"; + $emptyclause = "AND ({context}.path IS NULL OR {context}.depth = 0)"; + } + + $base = '/'.SYSCONTEXTID; + + // Standard frontpage + $sql = "UPDATE {context} + SET depth = 2, + path = ".$DB->sql_concat("'$base/'", 'id')." + WHERE contextlevel = ".CONTEXT_COURSE." + AND EXISTS (SELECT 'x' + FROM {course} c + WHERE c.id = {context}.instanceid AND c.category = 0) + $emptyclause"; + $DB->execute($sql); + + // standard courses + $sql = "INSERT INTO {context_temp} (id, path, depth) + SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 + FROM {context} ctx + JOIN {course} c ON (c.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_COURSE." AND c.category <> 0) + JOIN {context} pctx ON (pctx.instanceid = c.category AND pctx.contextlevel = ".CONTEXT_COURSECAT.") + WHERE pctx.path IS NOT NULL AND pctx.depth > 0 + $ctxemptyclause"; + $trans = $DB->start_delegated_transaction(); + $DB->delete_records('context_temp'); + $DB->execute($sql); + context::merge_context_temp_table(); + $DB->delete_records('context_temp'); + $trans->allow_commit(); + } + } } + /** - * Counts all the users assigned this role in this context or higher - * - * @param mixed $roleid either int or an array of ints - * @param object $context - * @param bool $parent if true, get list of users assigned in higher context too - * @return int Returns the result count + * Course module context class + * @author Petr Skoda (http://skodak.org) */ -function count_role_users($roleid, $context, $parent = false) { - global $DB; - - if ($parent) { - if ($contexts = get_parent_contexts($context)) { - $parentcontexts = ' OR r.contextid IN ('.implode(',', $contexts).')'; - } else { - $parentcontexts = ''; +class context_module extends context { + /** + * Please use context_module::instance($cmid) if you need the instance of context. + * Alternatively if you know only the context id use context::instance_by_id($contextid) + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_MODULE) { + throw new coding_exception('Invalid $record->contextlevel in context_module constructor.'); } - } else { - $parentcontexts = ''; } - if ($roleid) { - list($rids, $params) = $DB->get_in_or_equal($roleid, SQL_PARAMS_QM); - $roleselect = "AND r.roleid $rids"; - } else { - $params = array(); - $roleselect = ''; + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('activitymodule'); } - array_unshift($params, $context->id); + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with the + * module name, e.g. Forum, Glossary, etc. + * @param boolean $short does not apply to module context + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + global $DB; + + $name = ''; + if ($cm = $DB->get_record_sql("SELECT cm.*, md.name AS modname + FROM {course_modules} cm + JOIN {modules} md ON md.id = cm.module + WHERE cm.id = ?", array($this->_instanceid))) { + if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) { + if ($withprefix){ + $name = get_string('modulename', $cm->modname).': '; + } + $name .= $mod->name; + } + } + return $name; + } - $sql = "SELECT count(u.id) - FROM {role_assignments} r - JOIN {user} u ON u.id = r.userid - WHERE (r.contextid = ? $parentcontexts) - $roleselect - AND u.deleted = 0"; + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + global $DB; - return $DB->count_records_sql($sql, $params); -} + if ($modname = $DB->get_field_sql("SELECT md.name AS modname + FROM {course_modules} cm + JOIN {modules} md ON md.id = cm.module + WHERE cm.id = ?", array($this->_instanceid))) { + return new moodle_url('/mod/' . $modname . '/view.php', array('id'=>$this->_instanceid)); + } -/** - * This function gets the list of courses that this user has a particular capability in. - * It is still not very efficient. - * - * @param string $capability Capability in question - * @param int $userid User ID or null for current user - * @param bool $doanything True if 'doanything' is permitted (default) - * @param string $fieldsexceptid Leave blank if you only need 'id' in the course records; - * otherwise use a comma-separated list of the fields you require, not including id - * @param string $orderby If set, use a comma-separated list of fields from course - * table with sql modifiers (DESC) if needed - * @return array Array of courses, may have zero entries. Or false if query failed. - */ -function get_user_capability_course($capability, $userid = null, $doanything = true, $fieldsexceptid = '', $orderby = '') { - global $DB; + return new moodle_url('/'); + } - // Convert fields list and ordering - $fieldlist = ''; - if ($fieldsexceptid) { - $fields = explode(',', $fieldsexceptid); - foreach($fields as $field) { - $fieldlist .= ',c.'.$field; + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB, $CFG; + + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display + + $cm = $DB->get_record('course_modules', array('id'=>$this->_instanceid)); + $module = $DB->get_record('modules', array('id'=>$cm->module)); + + $subcaps = array(); + $subpluginsfile = "$CFG->dirroot/mod/$module->name/db/subplugins.php"; + if (file_exists($subpluginsfile)) { + $subplugins = array(); // should be redefined in the file + include($subpluginsfile); + if (!empty($subplugins)) { + foreach (array_keys($subplugins) as $subplugintype) { + foreach (array_keys(get_plugin_list($subplugintype)) as $subpluginname) { + $subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname))); + } + } + } } - } - if ($orderby) { - $fields = explode(',', $orderby); - $orderby = ''; - foreach($fields as $field) { - if ($orderby) { - $orderby .= ','; + + $modfile = "$CFG->dirroot/mod/$module->name/lib.php"; + if (file_exists($modfile)) { + include_once($modfile); + $modfunction = $module->name.'_get_extra_capabilities'; + if (function_exists($modfunction)) { + $extracaps = $modfunction(); } - $orderby .= 'c.'.$field; } - $orderby = 'ORDER BY '.$orderby; - } + if (empty($extracaps)) { + $extracaps = array(); + } - // Obtain a list of everything relevant about all courses including context. - // Note the result can be used directly as a context (we are going to), the course - // fields are just appended. + $extracaps = array_merge($subcaps, $extracaps); - $courses = array(); - $rs = $DB->get_recordset_sql("SELECT x.*, c.id AS courseid $fieldlist - FROM {course} c - INNER JOIN {context} x - ON (c.id=x.instanceid AND x.contextlevel=".CONTEXT_COURSE.") - $orderby"); - // Check capability for each course in turn - foreach ($rs as $coursecontext) { - if (has_capability($capability, $coursecontext, $userid, $doanything)) { - // We've got the capability. Make the record look like a course record - // and store it - $coursecontext->id = $coursecontext->courseid; - unset($coursecontext->courseid); - unset($coursecontext->contextlevel); - unset($coursecontext->instanceid); - $courses[] = $coursecontext; - } + // All modules allow viewhiddenactivities. This is so you can hide + // the module then override to allow specific roles to see it. + // The actual check is in course page so not module-specific + $extracaps[] = "moodle/course:viewhiddenactivities"; + list($extra, $params) = $DB->get_in_or_equal( + $extracaps, SQL_PARAMS_NAMED, 'cap0'); + $extra = "OR name $extra"; + + $sql = "SELECT * + FROM {capabilities} + WHERE (contextlevel = ".CONTEXT_MODULE." + AND component = :component) + $extra"; + $params['component'] = "mod_$module->name"; + + return $DB->get_records_sql($sql.' '.$sort, $params); } - $rs->close(); - return empty($courses) ? false : $courses; -} -/** - * This function finds the roles assigned directly to this context only - * i.e. no parents role - * - * @param object $context - * @return array - */ -function get_roles_on_exact_context($context) { - global $DB; + /** + * Is this context part of any course? If yes return course context. + * + * @param bool $strict true means throw exception if not found, false means return false if not found + * @return course_context context of the enclosing course, null if not found or exception + */ + public function get_course_context($strict = true) { + return $this->get_parent_context(); + } - return $DB->get_records_sql("SELECT r.* - FROM {role_assignments} ra, {role} r - WHERE ra.roleid = r.id AND ra.contextid = ?", - array($context->id)); + /** + * Returns module context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @return context_module context instance + */ + public static function instance($instanceid, $strictness = MUST_EXIST) { + global $DB; -} + if ($context = context::cache_get(CONTEXT_MODULE, $instanceid)) { + return $context; + } -/** - * Switches the current user to another role for the current session and only - * in the given context. - * - * The caller *must* check - * - that this op is allowed - * - that the requested role can be switched to in this context (use get_switchable_roles) - * - that the requested role is NOT $CFG->defaultuserroleid - * - * To "unswitch" pass 0 as the roleid. - * - * This function *will* modify $USER->access - beware - * - * @param integer $roleid the role to switch to. - * @param object $context the context in which to perform the switch. - * @return bool success or failure. - */ -function role_switch($roleid, $context) { - global $USER; + if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$instanceid))) { + if ($cm = $DB->get_record('course_modules', array('id'=>$instanceid), 'id,course', $strictness)) { + $parentcontext = context_course::instance($cm->course); + $record = context::insert_context_record(CONTEXT_MODULE, $cm->id, $parentcontext->path); + } + } - // - // Plan of action - // - // - Add the ghost RA to $USER->access - // as $USER->access['rsw'][$path] = $roleid - // - // - Make sure $USER->access['rdef'] has the roledefs - // it needs to honour the switcherole - // - // Roledefs will get loaded "deep" here - down to the last child - // context. Note that - // - // - When visiting subcontexts, our selective accessdata loading - // will still work fine - though those ra/rdefs will be ignored - // appropriately while the switch is in place - // - // - If a switcherole happens at a category with tons of courses - // (that have many overrides for switched-to role), the session - // will get... quite large. Sometimes you just can't win. - // - // To un-switch just unset($USER->access['rsw'][$path]) - // - // Note: it is not possible to switch to roles that do not have course:view + if ($record) { + $context = new context_module($record); + context::cache_add($context); + return $context; + } - // Add the switch RA - if (!isset($USER->access['rsw'])) { - $USER->access['rsw'] = array(); + return false; } - if ($roleid == 0) { - unset($USER->access['rsw'][$context->path]); - if (empty($USER->access['rsw'])) { - unset($USER->access['rsw']); - } - return true; + /** + * Create missing context instances at module context level + * @static + */ + protected static function create_level_instances() { + global $DB; + + $sql = "INSERT INTO {context} (contextlevel, instanceid) + SELECT ".CONTEXT_MODULE.", cm.id + FROM {course_modules} cm + WHERE NOT EXISTS (SELECT 'x' + FROM {context} cx + WHERE cm.id = cx.instanceid AND cx.contextlevel=".CONTEXT_MODULE.")"; + $DB->execute($sql); } - $USER->access['rsw'][$context->path]=$roleid; + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id + WHERE cm.id IS NULL AND c.contextlevel = ".CONTEXT_MODULE." + "; - // Load roledefs - $USER->access = get_role_access_bycontext($roleid, $context, - $USER->access); + return $sql; + } - return true; -} + /** + * Rebuild context paths and depths at module context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; -/** - * Checks if the user has switched roles within the given course. - * - * Note: You can only switch roles within the course, hence it takes a courseid - * rather than a context. On that note Petr volunteered to implement this across - * all other contexts, all requests for this should be forwarded to him ;) - * - * @param int $courseid The id of the course to check - * @return bool True if the user has switched roles within the course. - */ -function is_role_switched($courseid) { - global $USER; - $context = get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST); - return (!empty($USER->access['rsw'][$context->path])); + if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_MODULE." AND (depth = 0 OR path IS NULL)")) { + if ($force) { + $ctxemptyclause = ''; + } else { + $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)"; + } + + $sql = "INSERT INTO {context_temp} (id, path, depth) + SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 + FROM {context} ctx + JOIN {course_modules} cm ON (cm.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_MODULE.") + JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = ".CONTEXT_COURSE.") + WHERE pctx.path IS NOT NULL AND pctx.depth > 0 + $ctxemptyclause"; + $trans = $DB->start_delegated_transaction(); + $DB->delete_records('context_temp'); + $DB->execute($sql); + context::merge_context_temp_table(); + $DB->delete_records('context_temp'); + $trans->allow_commit(); + } + } } + /** - * Get any role that has an override on exact context - * - * @global moodle_database - * @param stdClass $context The context to check - * @return array An array of roles + * Block context class + * @author Petr Skoda (http://skodak.org) */ -function get_roles_with_override_on_context($context) { - global $DB; +class context_block extends context { + /** + * Please use context_block::instance($blockinstanceid) if you need the instance of context. + * Alternatively if you know only the context id use context::instance_by_id($contextid) + * + * @param stdClass $record + */ + protected function __construct(stdClass $record) { + parent::__construct($record); + if ($record->contextlevel != CONTEXT_BLOCK) { + throw new coding_exception('Invalid $record->contextlevel in context_block constructor'); + } + } - return $DB->get_records_sql("SELECT r.* - FROM {role_capabilities} rc, {role} r - WHERE rc.roleid = r.id AND rc.contextid = ?", - array($context->id)); -} + /** + * Returns human readable context level name. + * + * @static + * @return string the human readable context level name. + */ + protected static function get_level_name() { + return get_string('block'); + } -/** - * Get all capabilities for this role on this context (overrides) - * - * @param object $role - * @param object $context - * @return array - */ -function get_capabilities_from_role_on_context($role, $context) { - global $DB; + /** + * Returns human readable context identifier. + * + * @param boolean $withprefix whether to prefix the name of the context with Block + * @param boolean $short does not apply to block context + * @return string the human readable context name. + */ + public function get_context_name($withprefix = true, $short = false) { + global $DB, $CFG; + + $name = ''; + if ($blockinstance = $DB->get_record('block_instances', array('id'=>$this->_instanceid))) { + global $CFG; + require_once("$CFG->dirroot/blocks/moodleblock.class.php"); + require_once("$CFG->dirroot/blocks/$blockinstance->blockname/block_$blockinstance->blockname.php"); + $blockname = "block_$blockinstance->blockname"; + if ($blockobject = new $blockname()) { + if ($withprefix){ + $name = get_string('block').': '; + } + $name .= $blockobject->title; + } + } - return $DB->get_records_sql("SELECT * - FROM {role_capabilities} - WHERE contextid = ? AND roleid = ?", - array($context->id, $role->id)); -} + return $name; + } -/** - * Find out which roles has assignment on this context - * - * @param object $context - * @return array - * - */ -function get_roles_with_assignment_on_context($context) { - global $DB; + /** + * Returns the most relevant URL for this context. + * + * @return moodle_url + */ + public function get_url() { + $parentcontexts = $this->get_parent_context(); + return $parentcontexts->get_url(); + } - return $DB->get_records_sql("SELECT r.* - FROM {role_assignments} ra, {role} r - WHERE ra.roleid = r.id AND ra.contextid = ?", - array($context->id)); -} + /** + * Returns array of relevant context capability records. + * + * @return array + */ + public function get_capabilities() { + global $DB; + $sort = 'ORDER BY contextlevel,component,name'; // To group them sensibly for display + $params = array(); + $bi = $DB->get_record('block_instances', array('id' => $this->_instanceid)); -/** - * Find all user assignment of users for this role, on this context - * - * @param object $role - * @param object $context - * @return array - */ -function get_users_from_role_on_context($role, $context) { - global $DB; + $extra = ''; + $extracaps = block_method_result($bi->blockname, 'get_extra_capabilities'); + if ($extracaps) { + list($extra, $params) = $DB->get_in_or_equal($extracaps, SQL_PARAMS_NAMED, 'cap'); + $extra = "OR name $extra"; + } - return $DB->get_records_sql("SELECT * - FROM {role_assignments} - WHERE contextid = ? AND roleid = ?", - array($context->id, $role->id)); -} + $sql = "SELECT * + FROM {capabilities} + WHERE (contextlevel = ".CONTEXT_BLOCK." + AND component = :component) + $extra"; + $params['component'] = 'block_' . $bi->blockname; -/** - * Simple function returning a boolean true if user has roles - * in context or parent contexts, otherwise false. - * - * @param int $userid - * @param int $roleid - * @param int $contextid empty means any context - * @return bool - */ -function user_has_role_assignment($userid, $roleid, $contextid = 0) { - global $DB; + return $DB->get_records_sql($sql.' '.$sort, $params); + } - if ($contextid) { - if (!$context = get_context_instance_by_id($contextid)) { - return false; + /** + * Is this context part of any course? If yes return course context. + * + * @param bool $strict true means throw exception if not found, false means return false if not found + * @return course_context context of the enclosing course, null if not found or exception + */ + public function get_course_context($strict = true) { + $parentcontext = $this->get_parent_context(); + return $parentcontext->get_course_context($strict); + } + + /** + * Returns block context instance. + * + * @static + * @param int $instanceid + * @param int $strictness + * @return context_block context instance + */ + public static function instance($instanceid, $strictness = MUST_EXIST) { + global $DB; + + if ($context = context::cache_get(CONTEXT_BLOCK, $instanceid)) { + return $context; } - $parents = get_parent_contexts($context, true); - list($contexts, $params) = $DB->get_in_or_equal($parents, SQL_PARAMS_NAMED, 'r'); - $params['userid'] = $userid; - $params['roleid'] = $roleid; - $sql = "SELECT COUNT(ra.id) - FROM {role_assignments} ra - WHERE ra.userid = :userid AND ra.roleid = :roleid AND ra.contextid $contexts"; + if (!$record = $DB->get_record('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$instanceid))) { + if ($bi = $DB->get_record('block_instances', array('id'=>$instanceid), 'id,parentcontextid', $strictness)) { + $parentcontext = context::instance_by_id($bi->parentcontextid); + $record = context::insert_context_record(CONTEXT_BLOCK, $bi->id, $parentcontext->path); + } + } - $count = $DB->get_field_sql($sql, $params); - return ($count > 0); + if ($record) { + $context = new context_block($record); + context::cache_add($context); + return $context; + } - } else { - return $DB->record_exists('role_assignments', array('userid'=>$userid, 'roleid'=>$roleid)); + return false; } -} -/** - * Get role name or alias if exists and format the text. - * - * @param object $role role object - * @param object $coursecontext - * @return string name of role in course context - */ -function role_get_name($role, $coursecontext) { - global $DB; + /** + * Block do not have child contexts... + * @return array + */ + public function get_child_contexts() { + return array(); + } - if ($r = $DB->get_record('role_names', array('roleid'=>$role->id, 'contextid'=>$coursecontext->id))) { - return strip_tags(format_string($r->name)); - } else { - return strip_tags(format_string($role->name)); + /** + * Create missing context instances at block context level + * @static + */ + protected static function create_level_instances() { + global $DB; + + $sql = "INSERT INTO {context} (contextlevel, instanceid) + SELECT ".CONTEXT_BLOCK.", bi.id + FROM {block_instances} bi + WHERE NOT EXISTS (SELECT 'x' + FROM {context} cx + WHERE bi.id = cx.instanceid AND cx.contextlevel=".CONTEXT_BLOCK.")"; + $DB->execute($sql); } -} -/** - * Prepare list of roles for display, apply aliases and format text - * - * @param array $roleoptions array roleid => rolename or roleid => roleobject - * @param object $context a context - * @return array Array of context-specific role names, or role objexts with a ->localname field added. - */ -function role_fix_names($roleoptions, $context, $rolenamedisplay = ROLENAME_ALIAS) { - global $DB; + /** + * Returns sql necessary for purging of stale context instances. + * + * @static + * @return string cleanup SQL + */ + protected static function get_cleanup_sql() { + $sql = " + SELECT c.* + FROM {context} c + LEFT OUTER JOIN {block_instances} bi ON c.instanceid = bi.id + WHERE bi.id IS NULL AND c.contextlevel = ".CONTEXT_BLOCK." + "; - // Make sure we are working with an array roleid => name. Normally we - // want to use the unlocalised name if the localised one is not present. - $newnames = array(); - foreach ($roleoptions as $rid => $roleorname) { - if ($rolenamedisplay != ROLENAME_ALIAS_RAW) { - if (is_object($roleorname)) { - $newnames[$rid] = $roleorname->name; - } else { - $newnames[$rid] = $roleorname; - } - } else { - $newnames[$rid] = ''; - } + return $sql; } - // If necessary, get the localised names. - if ($rolenamedisplay != ROLENAME_ORIGINAL && !empty($context->id)) { - // Make sure we have a course context. - if ($context->contextlevel == CONTEXT_MODULE) { - if ($parentcontextid = array_shift(get_parent_contexts($context))) { - $context = get_context_instance_by_id($parentcontextid); - } - } else if ($context->contextlevel == CONTEXT_BLOCK) { - do { - if ($parentcontextid = array_shift(get_parent_contexts($context))) { - $context = get_context_instance_by_id($parentcontextid); - } - } while ($parentcontextid && $context->contextlevel != CONTEXT_COURSE); - } + /** + * Rebuild context paths and depths at block context level. + * + * @static + * @param $force + */ + protected static function build_paths($force) { + global $DB; - // The get the relevant renames, and use them. - $aliasnames = $DB->get_records('role_names', array('contextid'=>$context->id)); - foreach ($aliasnames as $alias) { - if (isset($newnames[$alias->roleid])) { - if ($rolenamedisplay == ROLENAME_ALIAS || $rolenamedisplay == ROLENAME_ALIAS_RAW) { - $newnames[$alias->roleid] = $alias->name; - } else if ($rolenamedisplay == ROLENAME_BOTH) { - $newnames[$alias->roleid] = $alias->name . ' (' . $roleoptions[$alias->roleid] . ')'; - } + if ($force or $DB->record_exists_select('context', "contextlevel = ".CONTEXT_BLOCK." AND (depth = 0 OR path IS NULL)")) { + if ($force) { + $ctxemptyclause = ''; + } else { + $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)"; } - } - } - // Finally, apply format_string and put the result in the right place. - foreach ($roleoptions as $rid => $roleorname) { - if ($rolenamedisplay != ROLENAME_ALIAS_RAW) { - $newnames[$rid] = strip_tags(format_string($newnames[$rid])); - } - if (is_object($roleorname)) { - $roleoptions[$rid]->localname = $newnames[$rid]; - } else { - $roleoptions[$rid] = $newnames[$rid]; + // pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent + $sql = "INSERT INTO {context_temp} (id, path, depth) + SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 + FROM {context} ctx + JOIN {block_instances} bi ON (bi.id = ctx.instanceid AND ctx.contextlevel = ".CONTEXT_BLOCK.") + JOIN {context} pctx ON (pctx.id = bi.parentcontextid) + WHERE (pctx.path IS NOT NULL AND pctx.depth > 0) + $ctxemptyclause"; + $trans = $DB->start_delegated_transaction(); + $DB->delete_records('context_temp'); + $DB->execute($sql); + context::merge_context_temp_table(); + $DB->delete_records('context_temp'); + $trans->allow_commit(); } } - return $roleoptions; } + +// ============== DEPRECATED ======================== + + /** - * Aids in detecting if a new line is required when reading a new capability - * - * This function helps admin/roles/manage.php etc to detect if a new line should be printed - * when we read in a new capability. - * Most of the time, if the 2 components are different we should print a new line, (e.g. course system->rss client) - * but when we are in grade, all reports/import/export capabilities should be together - * - * @param string $cap component string a - * @param string $comp component string b - * @param mixed $contextlevel - * @return bool whether 2 component are in different "sections" + * Use load_temp_course_role() instead. + * @deprecated + * @param stdClass $context + * @param int $roleid + * @param array $accessdata + * @return array */ -function component_level_changed($cap, $comp, $contextlevel) { +function load_temp_role($context, $roleid, array $accessdata) { + debugging('load_temp_role() is deprecated, please use load_temp_course_role() instead, temp role not loaded.'); + return $accessdata; +} - if (strstr($cap->component, '/') && strstr($comp, '/')) { - $compsa = explode('/', $cap->component); - $compsb = explode('/', $comp); +/** + * Use remove_temp_course_roles() instead + * @deprecated + * @param object $context + * @param array $accessdata + * @return array access data + */ +function remove_temp_roles($context, array $accessdata) { + debugging('remove_temp_role() is deprecated, please use remove_temp_course_roles() instead.'); + return $accessdata; +} - // list of system reports - if (($compsa[0] == 'report') && ($compsb[0] == 'report')) { - return false; - } +/** + * Returns system context or null if can not be created yet. + * + * @deprecated + * @param bool $cache use caching + * @return context system context (null if context table not created yet) + */ +function get_system_context($cache = true) { + return context_system::instance(0, IGNORE_MISSING, $cache); +} - // we are in gradebook, still - if (($compsa[0] == 'gradeexport' || $compsa[0] == 'gradeimport' || $compsa[0] == 'gradereport') && - ($compsb[0] == 'gradeexport' || $compsb[0] == 'gradeimport' || $compsb[0] == 'gradereport')) { - return false; - } +/** + * Get the context instance as an object. This function will create the + * context instance if it does not exist yet. + * + * @deprecated + * @param integer $contextlevel The context level, for example CONTEXT_COURSE, or CONTEXT_MODULE. + * @param integer $instance The instance id. For $level = CONTEXT_COURSE, this would be $course->id, + * for $level = CONTEXT_MODULE, this would be $cm->id. And so on. Defaults to 0 + * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found; + * MUST_EXIST means throw exception if no record or multiple records found + * @return context The context object. + */ +function get_context_instance($contextlevel, $instance = 0, $strictness = IGNORE_MISSING) { + $instances = (array)$instance; + $contexts = array(); - if (($compsa[0] == 'coursereport') && ($compsb[0] == 'coursereport')) { - return false; - } + $classname = context_helper::get_class_for_level($contextlevel); + + // we do not load multiple contexts any more, PAGE should be responsible for any preloading + foreach ($instances as $inst) { + $contexts[$inst] = $classname::instance($inst, $strictness); } - return ($cap->component != $comp || $cap->contextlevel != $contextlevel); + if (is_array($instance)) { + return $contexts; + } else { + return $contexts[$instance]; + } } /** - * Rebuild all related context depth and path caches + * Get a context instance as an object, from a given context id. * - * @param array $fixcontexts array of contexts, strongtyped + * @deprecated + * @param int $id context id + * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found; + * MUST_EXIST means throw exception if no record or multiple records found + * @return context|bool the context object or false if not found. */ -function rebuild_contexts(array $fixcontexts) { - global $DB; - - foreach ($fixcontexts as $context) { - if ($context->path) { - mark_context_dirty($context->path); - } - $DB->set_field_select('context', 'depth', 0, "path LIKE '%/$context->id/%'"); - $DB->set_field('context', 'depth', 0, array('id'=>$context->id)); - } - build_context_path(false); +function get_context_instance_by_id($id, $strictness = IGNORE_MISSING) { + return context::instance_by_id($id, $strictness); } /** - * Populate context.path and context.depth where missing. + * Recursive function which, given a context, find all parent context ids, + * and return the array in reverse order, i.e. parent first, then grand + * parent, etc. * - * @param bool $force force a complete rebuild of the path and depth fields, defaults to false + * @deprecated + * @param context $context + * @param bool $includeself optional, defaults to false + * @return array */ -function build_context_path($force = false) { - global $CFG, $DB, $ACCESSLIB_PRIVATE; - - // System context - $sitectx = get_system_context(!$force); - $base = '/'.$sitectx->id; - - // Sitecourse - $sitecoursectx = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>SITEID)); - if ($force || $sitecoursectx->path !== "$base/{$sitecoursectx->id}") { - $DB->set_field('context', 'path', "$base/{$sitecoursectx->id}", array('id'=>$sitecoursectx->id)); - $DB->set_field('context', 'depth', 2, array('id'=>$sitecoursectx->id)); - $sitecoursectx = $DB->get_record('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>SITEID)); - } - - $ctxemptyclause = " AND (ctx.path IS NULL - OR ctx.depth=0) "; - $emptyclause = " AND ({context}.path IS NULL - OR {context}.depth=0) "; - if ($force) { - $ctxemptyclause = $emptyclause = ''; - } +function get_parent_contexts(context $context, $includeself = false) { + return $context->get_parent_context_ids($includeself); +} - /* MDL-11347: - * - mysql does not allow to use FROM in UPDATE statements - * - using two tables after UPDATE works in mysql, but might give unexpected - * results in pg 8 (depends on configuration) - * - using table alias in UPDATE does not work in pg < 8.2 - * - * Different code for each database - mostly for performance reasons - */ - $dbfamily = $DB->get_dbfamily(); - if ($dbfamily == 'mysql') { - $updatesql = "UPDATE {context} ct, {context_temp} temp - SET ct.path = temp.path, - ct.depth = temp.depth - WHERE ct.id = temp.id"; - } else if ($dbfamily == 'oracle') { - $updatesql = "UPDATE {context} ct - SET (ct.path, ct.depth) = - (SELECT temp.path, temp.depth - FROM {context_temp} temp - WHERE temp.id=ct.id) - WHERE EXISTS (SELECT 'x' - FROM {context_temp} temp - WHERE temp.id = ct.id)"; - } else if ($dbfamily == 'postgres' or $dbfamily == 'mssql') { - $updatesql = "UPDATE {context} - SET path = temp.path, - depth = temp.depth - FROM {context_temp} temp - WHERE temp.id={context}.id"; +/** + * Return the id of the parent of this context, or false if there is no parent (only happens if this + * is the site context.) + * + * @deprecated + * @param context $context + * @return integer the id of the parent context. + */ +function get_parent_contextid(context $context) { + if ($parent = $context->get_parent_context()) { + return $parent->id; } else { - // sqlite and others - $updatesql = "UPDATE {context} - SET path = (SELECT path FROM {context_temp} WHERE id = {context}.id), - depth = (SELECT depth FROM {context_temp} WHERE id = {context}.id) - WHERE id IN (SELECT id FROM {context_temp})"; - } - - // Top level categories - $sql = "UPDATE {context} - SET depth=2, path=" . $DB->sql_concat("'$base/'", 'id') . " - WHERE contextlevel=".CONTEXT_COURSECAT." - AND EXISTS (SELECT 'x' - FROM {course_categories} cc - WHERE cc.id = {context}.instanceid - AND cc.depth=1) - $emptyclause"; - - $DB->execute($sql); - $DB->delete_records('context_temp'); - - // Deeper categories - one query per depthlevel - $maxdepth = $DB->get_field_sql("SELECT MAX(depth) - FROM {course_categories}"); - for ($n=2; $n<=$maxdepth; $n++) { - $sql = "INSERT INTO {context_temp} (id, path, depth) - SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", $n+1 - FROM {context} ctx - JOIN {course_categories} c ON ctx.instanceid=c.id - JOIN {context} pctx ON c.parent=pctx.instanceid - WHERE ctx.contextlevel=".CONTEXT_COURSECAT." - AND pctx.contextlevel=".CONTEXT_COURSECAT." - AND c.depth=$n - AND NOT EXISTS (SELECT 'x' - FROM {context_temp} temp - WHERE temp.id = ctx.id) - $ctxemptyclause"; - $DB->execute($sql); - - // this is needed after every loop - // MDL-11532 - $DB->execute($updatesql); - $DB->delete_records('context_temp'); + return false; } - - // Courses -- except sitecourse - $sql = "INSERT INTO {context_temp} (id, path, depth) - SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 - FROM {context} ctx - JOIN {course} c ON ctx.instanceid=c.id - JOIN {context} pctx ON c.category=pctx.instanceid - WHERE ctx.contextlevel=".CONTEXT_COURSE." - AND c.id!=".SITEID." - AND pctx.contextlevel=".CONTEXT_COURSECAT." - AND NOT EXISTS (SELECT 'x' - FROM {context_temp} temp - WHERE temp.id = ctx.id) - $ctxemptyclause"; - $DB->execute($sql); - - $DB->execute($updatesql); - $DB->delete_records('context_temp'); - - // Module instances - $sql = "INSERT INTO {context_temp} (id, path, depth) - SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 - FROM {context} ctx - JOIN {course_modules} cm ON ctx.instanceid=cm.id - JOIN {context} pctx ON cm.course=pctx.instanceid - WHERE ctx.contextlevel=".CONTEXT_MODULE." - AND pctx.contextlevel=".CONTEXT_COURSE." - AND NOT EXISTS (SELECT 'x' - FROM {context_temp} temp - WHERE temp.id = ctx.id) - $ctxemptyclause"; - $DB->execute($sql); - - $DB->execute($updatesql); - $DB->delete_records('context_temp'); - - // User - $sql = "UPDATE {context} - SET depth=2, path=".$DB->sql_concat("'$base/'", 'id')." - WHERE contextlevel=".CONTEXT_USER." - AND EXISTS (SELECT 'x' - FROM {user} u - WHERE u.id = {context}.instanceid) - $emptyclause "; - $DB->execute($sql); - - // Blocks - // pctx.path IS NOT NULL prevents fatal problems with broken block instances that point to invalid context parent - $sql = "INSERT INTO {context_temp} (id, path, depth) - SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1 - FROM {context} ctx - JOIN {block_instances} bi ON ctx.instanceid = bi.id - JOIN {context} pctx ON bi.parentcontextid = pctx.id - WHERE ctx.contextlevel=".CONTEXT_BLOCK." - AND pctx.path IS NOT NULL - AND NOT EXISTS (SELECT 'x' - FROM {context_temp} temp - WHERE temp.id = ctx.id) - $ctxemptyclause"; - $DB->execute($sql); - - $DB->execute($updatesql); - $DB->delete_records('context_temp'); - - // reset static course cache - it might have incorrect cached data - $ACCESSLIB_PRIVATE->contexcache->reset(); } /** - * Update the path field of the context and all dep. subcontexts that follow + * Recursive function which, given a context, find all its children context ids. * - * Update the path field of the context and - * all the dependent subcontexts that follow - * the move. + * For course category contexts it will return immediate children only categories and courses. + * It will NOT recurse into courses or child categories. + * If you want to do that, call it on the returned courses/categories. * - * The most important thing here is to be as - * DB efficient as possible. This op can have a - * massive impact in the DB. + * When called for a course context, it will return the modules and blocks + * displayed in the course page. + * + * If called on a user/course/module context it _will_ populate the cache with the appropriate + * contexts ;-) * - * @param stdClass $current context obj - * @param stdClass $newparent new parent obj + * @deprecated + * @param context $context. + * @return array Array of child records + */ +function get_child_contexts(context $context) { + return $context->get_child_contexts(); +} + +/** + * Precreates all contexts including all parents * + * @deprecated + * @param int $contextlevel empty means all + * @param bool $buildpaths update paths and depths + * @return void */ -function context_moved($context, $newparent) { - global $DB; +function create_contexts($contextlevel = null, $buildpaths = true) { + context_helper::create_instances($contextlevel, $buildpaths); +} - $frompath = $context->path; - $newpath = $newparent->path . '/' . $context->id; +/** + * Remove stale context records + * + * @deprecated + * @return bool + */ +function cleanup_contexts() { + context_helper::cleanup_instances(); + return true; +} - $setdepth = ''; - if (($newparent->depth +1) != $context->depth) { - $diff = $newparent->depth - $context->depth + 1; - $setdepth = ", depth = depth + $diff"; - } - $sql = "UPDATE {context} - SET path = ? - $setdepth - WHERE path = ?"; - $params = array($newpath, $frompath); - $DB->execute($sql, $params); +/** + * Populate context.path and context.depth where missing. + * + * @deprecated + * @param bool $force force a complete rebuild of the path and depth fields, defaults to false + * @return void + */ +function build_context_path($force = false) { + context_helper::build_all_paths($force); +} - $sql = "UPDATE {context} - SET path = ".$DB->sql_concat("?", $DB->sql_substr("path", strlen($frompath)+1))." - $setdepth - WHERE path LIKE ?"; - $params = array($newpath, "{$frompath}/%"); - $DB->execute($sql, $params); +/** + * Rebuild all related context depth and path caches + * + * @deprecated + * @param array $fixcontexts array of contexts, strongtyped + * @return void + */ +function rebuild_contexts(array $fixcontexts) { + foreach ($fixcontexts as $fixcontext) { + $fixcontext->reset_paths(false); + } + context_helper::build_all_paths(false); +} - mark_context_dirty($frompath); - mark_context_dirty($newpath); +/** + * Preloads all contexts relating to a course: course, modules. Block contexts + * are no longer loaded here. The contexts for all the blocks on the current + * page are now efficiently loaded by {@link block_manager::load_blocks()}. + * + * @deprecated + * @param int $courseid Course ID + * @return void + */ +function preload_course_contexts($courseid) { + context_helper::preload_course($courseid); } /** * Preloads context information together with instances. - * NOTE: in future this function may return empty strings - * if we implement different caching. + * Use context_instance_preload() to strip the context info from the record and cache the context instance. * + * @deprecated * @param string $joinon for example 'u.id' * @param string $contextlevel context level of instance in $joinon * @param string $tablealias context table alias * @return array with two values - select and join part */ function context_instance_preload_sql($joinon, $contextlevel, $tablealias) { - $select = ", $tablealias.id AS ctxid, $tablealias.path AS ctxpath, $tablealias.depth AS ctxdepth, $tablealias.contextlevel AS ctxlevel, $tablealias.instanceid AS ctxinstance"; + $select = ", ".context_helper::get_preload_record_columns_sql($tablealias); $join = "LEFT JOIN {context} $tablealias ON ($tablealias.instanceid = $joinon AND $tablealias.contextlevel = $contextlevel)"; return array($select, $join); } /** * Preloads context information from db record and strips the cached info. - * The db request has to ontain both the $join and $select from context_instance_preload_sql() + * The db request has to contain both the $join and $select from context_instance_preload_sql() * - * @param object $rec + * @deprecated + * @param stdClass $rec * @return void (modifies $rec) */ function context_instance_preload(stdClass $rec) { - global $ACCESSLIB_PRIVATE; - if (empty($rec->ctxid)) { - // $rec does not have enough data, passed here repeatedly or context does not exist yet - return; - } - - // note: in PHP5 the objects are passed by reference, no need to return $rec - $context = new stdClass(); - $context->id = $rec->ctxid; unset($rec->ctxid); - $context->path = $rec->ctxpath; unset($rec->ctxpath); - $context->depth = $rec->ctxdepth; unset($rec->ctxdepth); - $context->contextlevel = $rec->ctxlevel; unset($rec->ctxlevel); - $context->instanceid = $rec->ctxinstance; unset($rec->ctxinstance); - - $ACCESSLIB_PRIVATE->contexcache->add($context); + context_helper::preload_from_record($rec); } /** - * Fetch recent dirty contexts to know cheaply whether our $USER->access - * is stale and needs to be reloaded. + * Mark a context as dirty (with timestamp) so as to force reloading of the context. * - * Uses cache_flags - * @param int $time - * @return array Array of dirty contexts - */ -function get_dirty_contexts($time) { - return get_cache_flags('accesslib/dirtycontexts', $time-2); -} - -/** - * Mark a context as dirty (with timestamp) - * so as to force reloading of the context. + * NOTE: use $context->mark_dirty() instead * + * @deprecated * @param string $path context path */ function mark_context_dirty($path) { - global $CFG, $ACCESSLIB_PRIVATE; + global $CFG, $USER, $ACCESSLIB_PRIVATE; if (during_initial_install()) { return; @@ -5988,314 +6923,267 @@ function mark_context_dirty($path) { set_cache_flag('accesslib/dirtycontexts', $path, 1, time()+$CFG->sessiontimeout); if (isset($ACCESSLIB_PRIVATE->dirtycontexts)) { $ACCESSLIB_PRIVATE->dirtycontexts[$path] = 1; + } else { + if (CLI_SCRIPT) { + $ACCESSLIB_PRIVATE->dirtycontexts = array($path => 1); + } else { + if (isset($USER->access['time'])) { + $ACCESSLIB_PRIVATE->dirtycontexts = get_cache_flags('accesslib/dirtycontexts', $USER->access['time']-2); + } else { + $ACCESSLIB_PRIVATE->dirtycontexts = array($path => 1); + } + // flags not loaded yet, it will be done later in $context->reload_if_dirty() + } } } } /** - * Will walk the contextpath to answer whether - * the contextpath is dirty - * - * @param array $contexts array of strings - * @param obj|array $dirty Dirty contexts from get_dirty_contexts() - * @return bool - */ -function is_contextpath_dirty($pathcontexts, $dirty) { - $path = ''; - foreach ($pathcontexts as $ctx) { - $path = $path.'/'.$ctx; - if (isset($dirty[$path])) { - return true; - } - } - return false; -} - -/** - * Fix the roles.sortorder field in the database, so it contains sequential integers, - * and return an array of roleids in order. + * Update the path field of the context and all dep. subcontexts that follow * - * @param array $allroles array of roles, as returned by get_all_roles(); - * @return array $role->sortorder =-> $role->id with the keys in ascending order. - */ -function fix_role_sortorder($allroles) { - global $DB; - - $rolesort = array(); - $i = 0; - foreach ($allroles as $role) { - $rolesort[$i] = $role->id; - if ($role->sortorder != $i) { - $r = new stdClass(); - $r->id = $role->id; - $r->sortorder = $i; - $DB->update_record('role', $r); - $allroles[$role->id]->sortorder = $i; - } - $i++; - } - return $rolesort; -} - -/** - * Switch the sort order of two roles (used in admin/roles/manage.php). + * Update the path field of the context and + * all the dependent subcontexts that follow + * the move. * - * @param object $first The first role. Actually, only ->sortorder is used. - * @param object $second The second role. Actually, only ->sortorder is used. - * @return boolean success or failure - */ -function switch_roles($first, $second) { - global $DB; - $temp = $DB->get_field('role', 'MAX(sortorder) + 1', array()); - $result = $DB->set_field('role', 'sortorder', $temp, array('sortorder' => $first->sortorder)); - $result = $result && $DB->set_field('role', 'sortorder', $first->sortorder, array('sortorder' => $second->sortorder)); - $result = $result && $DB->set_field('role', 'sortorder', $second->sortorder, array('sortorder' => $temp)); - return $result; -} - -/** - * duplicates all the base definitions of a role + * The most important thing here is to be as + * DB efficient as possible. This op can have a + * massive impact in the DB. * - * @param object $sourcerole role to copy from - * @param int $targetrole id of role to copy to + * @deprecated + * @param context $context context obj + * @param context $newparent new parent obj + * @return void */ -function role_cap_duplicate($sourcerole, $targetrole) { - global $DB; - - $systemcontext = get_context_instance(CONTEXT_SYSTEM); - $caps = $DB->get_records_sql("SELECT * - FROM {role_capabilities} - WHERE roleid = ? AND contextid = ?", - array($sourcerole->id, $systemcontext->id)); - // adding capabilities - foreach ($caps as $cap) { - unset($cap->id); - $cap->roleid = $targetrole; - $DB->insert_record('role_capabilities', $cap); - } +function context_moved(context $context, context $newparent) { + $context->update_moved($newparent); } - /** - * Returns two lists, this can be used to find out if user has capability. - * Having any needed role and no forbidden role in this context means - * user has this capability in this context. - * Use get_role_names_with_cap_in_context() if you need role names to display in the UI - * - * @param object $context - * @param string $capability - * @return array($neededroles, $forbiddenroles) - */ -function get_roles_with_cap_in_context($context, $capability) { - global $DB; - - $ctxids = trim($context->path, '/'); // kill leading slash - $ctxids = str_replace('/', ',', $ctxids); - - $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.depth - FROM {role_capabilities} rc - JOIN {context} ctx ON ctx.id = rc.contextid - WHERE rc.capability = :cap AND ctx.id IN ($ctxids) - ORDER BY rc.roleid ASC, ctx.depth DESC"; - $params = array('cap'=>$capability); - - if (!$capdefs = $DB->get_records_sql($sql, $params)) { - // no cap definitions --> no capability - return array(array(), array()); - } - - $forbidden = array(); - $needed = array(); - foreach($capdefs as $def) { - if (isset($forbidden[$def->roleid])) { - continue; - } - if ($def->permission == CAP_PROHIBIT) { - $forbidden[$def->roleid] = $def->roleid; - unset($needed[$def->roleid]); - continue; - } - if (!isset($needed[$def->roleid])) { - if ($def->permission == CAP_ALLOW) { - $needed[$def->roleid] = true; - } else if ($def->permission == CAP_PREVENT) { - $needed[$def->roleid] = false; - } - } - } - unset($capdefs); - - // remove all those roles not allowing - foreach($needed as $key=>$value) { - if (!$value) { - unset($needed[$key]); - } else { - $needed[$key] = $key; + * Remove a context record and any dependent entries, + * removes context from static context cache too + * + * @deprecated + * @param int $contextlevel + * @param int $instanceid + * @param bool $deleterecord false means keep record for now + * @return bool returns true or throws an exception + */ +function delete_context($contextlevel, $instanceid, $deleterecord = true) { + if ($deleterecord) { + context_helper::delete_instance($contextlevel, $instanceid); + } else { + $classname = context_helper::get_class_for_level($contextlevel); + if ($context = $classname::instance($instanceid, IGNORE_MISSING)) { + $context->delete_content(); } } - return array($needed, $forbidden); + return true; } /** - * Returns an array of role IDs that have ALL of the the supplied capabilities - * Uses get_roles_with_cap_in_context(). Returns $allowed minus $forbidden * - * @param object $context - * @param array $capabilities An array of capabilities - * @return array of roles with all of the required capabilities + * @deprecated + * @param integer $contextlevel $context->context level. One of the CONTEXT_... constants. + * @return string the name for this type of context. */ -function get_roles_with_caps_in_context($context, $capabilities) { - $neededarr = array(); - $forbiddenarr = array(); - foreach($capabilities as $caprequired) { - list($neededarr[], $forbiddenarr[]) = get_roles_with_cap_in_context($context, $caprequired); - } - - $rolesthatcanrate = array(); - if (!empty($neededarr)) { - foreach ($neededarr as $needed) { - if (empty($rolesthatcanrate)) { - $rolesthatcanrate = $needed; - } else { - //only want roles that have all caps - $rolesthatcanrate = array_intersect_key($rolesthatcanrate,$needed); - } - } - } - if (!empty($forbiddenarr) && !empty($rolesthatcanrate)) { - foreach ($forbiddenarr as $forbidden) { - //remove any roles that are forbidden any of the caps - $rolesthatcanrate = array_diff($rolesthatcanrate, $forbidden); - } - } - return $rolesthatcanrate; +function get_contextlevel_name($contextlevel) { + return context_helper::get_level_name($contextlevel); } /** - * Returns an array of role names that have ALL of the the supplied capabilities - * Uses get_roles_with_caps_in_context(). Returns $allowed minus $forbidden + * Prints human readable context identifier. * - * @param object $context - * @param array $capabilities An array of capabilities - * @return array of roles with all of the required capabilities + * @deprecated + * @param context $context the context. + * @param boolean $withprefix whether to prefix the name of the context with the + * type of context, e.g. User, Course, Forum, etc. + * @param boolean $short whether to user the short name of the thing. Only applies + * to course contexts + * @return string the human readable context name. */ -function get_role_names_with_caps_in_context($context, $capabilities) { - global $DB; +function print_context_name(context $context, $withprefix = true, $short = false) { + return $context->get_context_name($withprefix, $short); +} - $rolesthatcanrate = get_roles_with_caps_in_context($context, $capabilities); +/** + * Get a URL for a context, if there is a natural one. For example, for + * CONTEXT_COURSE, this is the course page. For CONTEXT_USER it is the + * user profile page. + * + * @deprecated + * @param context $context the context. + * @return moodle_url + */ +function get_context_url(context $context) { + return $context->get_url(); +} - $allroles = array(); - $roles = $DB->get_records('role', null, 'sortorder DESC'); - foreach ($roles as $roleid=>$role) { - $allroles[$roleid] = $role->name; - } +/** + * Is this context part of any course? if yes return course context, + * if not return null or throw exception. + * + * @deprecated + * @param context $context + * @return course_context context of the enclosing course, null if not found or exception + */ +function get_course_context(context $context) { + return $context->get_course_context(true); +} - $rolenames = array(); - foreach ($rolesthatcanrate as $r) { - $rolenames[$r] = $allroles[$r]; +/** + * Returns current course id or null if outside of course based on context parameter. + * + * @deprecated + * @param context $context + * @return int|bool related course id or false + */ +function get_courseid_from_context(context $context) { + if ($coursecontext = $context->get_course_context(false)) { + return $coursecontext->instanceid; + } else { + return false; } - $rolenames = role_fix_names($rolenames, $context); - return $rolenames; } /** - * This function verifies the prohibit comes from this context - * and there are no more prohibits in parent contexts. - * @param object $context - * @param string $capability name - * @return bool + * Get an array of courses where cap requested is available + * and user is enrolled, this can be relatively slow. + * + * @deprecated + * @param int $userid A user id. By default (null) checks the permissions of the current user. + * @param string $cap - name of the capability + * @param array $accessdata_ignored + * @param bool $doanything_ignored + * @param string $sort - sorting fields - prefix each fieldname with "c." + * @param array $fields - additional fields you are interested in... + * @param int $limit_ignored + * @return array $courses - ordered array of course objects - see notes above */ -function prohibit_is_removable($roleid, $context, $capability) { - global $DB; +function get_user_courses_bycap($userid, $cap, $accessdata_ignored, $doanything_ignored, $sort = 'c.sortorder ASC', $fields = null, $limit_ignored = 0) { - $ctxids = trim($context->path, '/'); // kill leading slash - $ctxids = str_replace('/', ',', $ctxids); + $courses = enrol_get_users_courses($userid, true, $fields, $sort); + foreach ($courses as $id=>$course) { + $context = context_course::instance($id); + if (!has_capability($cap, $context, $userid)) { + unset($courses[$id]); + } + } - $params = array('roleid'=>$roleid, 'cap'=>$capability, 'prohibit'=>CAP_PROHIBIT); + return $courses; +} - $sql = "SELECT ctx.id - FROM {role_capabilities} rc - JOIN {context} ctx ON ctx.id = rc.contextid - WHERE rc.roleid = :roleid AND rc.permission = :prohibit AND rc.capability = :cap AND ctx.id IN ($ctxids) - ORDER BY ctx.depth DESC"; +/** + * Extracts the relevant capabilities given a contextid. + * All case based, example an instance of forum context. + * Will fetch all forum related capabilities, while course contexts + * Will fetch all capabilities + * + * capabilities + * `name` varchar(150) NOT NULL, + * `captype` varchar(50) NOT NULL, + * `contextlevel` int(10) NOT NULL, + * `component` varchar(100) NOT NULL, + * + * @deprecated + * @param context $context + * @return array + */ +function fetch_context_capabilities(context $context) { + return $context->get_capabilities(); +} - if (!$prohibits = $DB->get_records_sql($sql, $params)) { - // no prohibits == nothing to remove - return true; - } +/** + * Runs get_records select on context table and returns the result + * Does get_records_select on the context table, and returns the results ordered + * by contextlevel, and then the natural sort order within each level. + * for the purpose of $select, you need to know that the context table has been + * aliased to ctx, so for example, you can call get_sorted_contexts('ctx.depth = 3'); + * + * @deprecated + * @param string $select the contents of the WHERE clause. Remember to do ctx.fieldname. + * @param array $params any parameters required by $select. + * @return array the requested context records. + */ +function get_sorted_contexts($select, $params = array()) { - if (count($prohibits) > 1) { - // more prohibints can not be removed - return false; - } + //TODO: we should probably rewrite all the code that is using this thing, the trouble is we MUST NOT modify the context instances... - return !empty($prohibits[$context->id]); + global $DB; + if ($select) { + $select = 'WHERE ' . $select; + } + return $DB->get_records_sql(" + SELECT ctx.* + FROM {context} ctx + LEFT JOIN {user} u ON ctx.contextlevel = " . CONTEXT_USER . " AND u.id = ctx.instanceid + LEFT JOIN {course_categories} cat ON ctx.contextlevel = " . CONTEXT_COURSECAT . " AND cat.id = ctx.instanceid + LEFT JOIN {course} c ON ctx.contextlevel = " . CONTEXT_COURSE . " AND c.id = ctx.instanceid + LEFT JOIN {course_modules} cm ON ctx.contextlevel = " . CONTEXT_MODULE . " AND cm.id = ctx.instanceid + LEFT JOIN {block_instances} bi ON ctx.contextlevel = " . CONTEXT_BLOCK . " AND bi.id = ctx.instanceid + $select + ORDER BY ctx.contextlevel, bi.defaultregion, COALESCE(cat.sortorder, c.sortorder, cm.section, bi.defaultweight), u.lastname, u.firstname, cm.id + ", $params); } /** - * More user friendly role permission changing, - * it should produce as few overrides as possible. + * This is really slow!!! do not use above course context level + * + * @deprecated * @param int $roleid - * @param object $context - * @param string $capname capability name - * @param int $permission - * @return void + * @param context $context + * @return array */ -function role_change_permission($roleid, $context, $capname, $permission) { +function get_role_context_caps($roleid, context $context) { global $DB; - if ($permission == CAP_INHERIT) { - unassign_capability($capname, $roleid, $context->id); - mark_context_dirty($context->path); - return; - } - - $ctxids = trim($context->path, '/'); // kill leading slash - $ctxids = str_replace('/', ',', $ctxids); - - $params = array('roleid'=>$roleid, 'cap'=>$capname); - - $sql = "SELECT ctx.id, rc.permission, ctx.depth - FROM {role_capabilities} rc - JOIN {context} ctx ON ctx.id = rc.contextid - WHERE rc.roleid = :roleid AND rc.capability = :cap AND ctx.id IN ($ctxids) - ORDER BY ctx.depth DESC"; + //this is really slow!!!! - do not use above course context level! + $result = array(); + $result[$context->id] = array(); - if ($existing = $DB->get_records_sql($sql, $params)) { - foreach($existing as $e) { - if ($e->permission == CAP_PROHIBIT) { - // prohibit can not be overridden, no point in changing anything - return; - } - } - $lowest = array_shift($existing); - if ($lowest->permission == $permission) { - // permission already set in this context or parent - nothing to do - return; - } - if ($existing) { - $parent = array_shift($existing); - if ($parent->permission == $permission) { - // permission already set in parent context or parent - just unset in this context - // we do this because we want as few overrides as possible for performance reasons - unassign_capability($capname, $roleid, $context->id); - mark_context_dirty($context->path); - return; + // first emulate the parent context capabilities merging into context + $searchcontexts = array_reverse($context->get_parent_context_ids(true)); + foreach ($searchcontexts as $cid) { + if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) { + foreach ($capabilities as $cap) { + if (!array_key_exists($cap->capability, $result[$context->id])) { + $result[$context->id][$cap->capability] = 0; + } + $result[$context->id][$cap->capability] += $cap->permission; } } + } - } else { - if ($permission == CAP_PREVENT) { - // nothing means role does not have permission - return; + // now go through the contexts bellow given context + $searchcontexts = array_keys($context->get_child_contexts()); + foreach ($searchcontexts as $cid) { + if ($capabilities = $DB->get_records('role_capabilities', array('roleid'=>$roleid, 'contextid'=>$cid))) { + foreach ($capabilities as $cap) { + if (!array_key_exists($cap->contextid, $result)) { + $result[$cap->contextid] = array(); + } + $result[$cap->contextid][$cap->capability] = $cap->permission; + } } } - // assign the needed capability - assign_capability($capname, $permission, $roleid, $context->id, true); - - // force cap reloading - mark_context_dirty($context->path); + return $result; } +/** + * Gets a string for sql calls, searching for stuff in this context or above + * + * NOTE: use $DB->get_in_or_equal($context->get_parent_context_ids()... + * + * @deprecated + * @param context $context + * @return string + */ +function get_related_contexts_string(context $context) { + + if ($parents = $context->get_parent_context_ids()) { + return (' IN ('.$context->id.','.implode(',', $parents).')'); + } else { + return (' ='.$context->id); + } +} diff --git a/lib/datalib.php b/lib/datalib.php index 478156cd71232..ef75c4651a6e1 100644 --- a/lib/datalib.php +++ b/lib/datalib.php @@ -1014,7 +1014,11 @@ function fix_course_sortorder() { // now fix the paths and depths in context table if needed if ($fixcontexts) { - rebuild_contexts($fixcontexts); + foreach ($fixcontexts as $fixcontext) { + $fixcontext->reset_paths(false); + } + context_helper::build_all_paths(false); + unset($fixcontexts); } // release memory diff --git a/lib/db/install.php b/lib/db/install.php index 03797855fe0a3..de4c45b854792 100644 --- a/lib/db/install.php +++ b/lib/db/install.php @@ -29,14 +29,17 @@ function xmldb_main_install() { global $CFG, $DB, $SITE; - /// make sure system context exists - $syscontext = get_system_context(false); - if ($syscontext->id != 1) { + /// Make sure system context exists + $syscontext = context_system::instance(0, MUST_EXIST, false); + if ($syscontext->id != SYSCONTEXTID) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new system context id!'); } - /// create site course + /// Create site course + if ($DB->record_exists('course', array())) { + throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create frontpage course, courses already exist.'); + } $newsite = new stdClass(); $newsite->fullname = ''; $newsite->shortname = ''; @@ -48,18 +51,38 @@ function xmldb_main_install() { $newsite->timecreated = time(); $newsite->timemodified = $newsite->timecreated; - $newsite->id = $DB->insert_record('course', $newsite); + if (defined('SITEID')) { + $newsite->id = SITEID; + $DB->import_record('course', $newsite); + $DB->get_manager()->reset_sequence('course'); + } else { + $newsite->id = $DB->insert_record('course', $newsite); + define('SITEID', $newsite->id); + } $SITE = get_site(); - if ($newsite->id != 1 or $SITE->id != 1) { + if ($newsite->id != $SITE->id) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new site course id!'); } + // Make sure site course context exists + context_course::instance($SITE->id); + // Update the global frontpage cache + $SITE = $DB->get_record('course', array('id'=>$newsite->id), '*', MUST_EXIST); - /// make sure site course context exists - get_context_instance(CONTEXT_COURSE, $SITE->id); + /// Create default course category + if ($DB->record_exists('course_categories', array())) { + throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default course category, categories already exist.'); + } + $cat = new stdClass(); + $cat->name = get_string('miscellaneous'); + $cat->depth = 1; + $cat->sortorder = MAX_COURSES_IN_CATEGORY; + $cat->timemodified = time(); + $catid = $DB->insert_record('course_categories', $cat); + $DB->set_field('course_categories', 'path', '/'.$catid, array('id'=>$catid)); + // Make sure category context exists + context_coursecat::instance($catid); - /// create default course category - $cat = get_course_category(); $defaults = array( 'rolesactive' => '0', // marks fully set up system @@ -82,7 +105,7 @@ function xmldb_main_install() { } - /// bootstrap mnet + /// Bootstrap mnet $mnethost = new stdClass(); $mnethost->wwwroot = $CFG->wwwroot; $mnethost->name = ''; @@ -137,7 +160,10 @@ function xmldb_main_install() { $mnetallhosts->id = $DB->insert_record('mnet_host', $mnetallhosts, true); set_config('mnet_all_hosts_id', $mnetallhosts->id); - /// Create guest record - do not assign any role, guest user get's the default guest role automatically on the fly + /// Create guest record - do not assign any role, guest user gets the default guest role automatically on the fly + if ($DB->record_exists('user', array())) { + throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default users, users already exist.'); + } $guest = new stdClass(); $guest->auth = 'manual'; $guest->username = 'guest'; @@ -156,6 +182,8 @@ function xmldb_main_install() { } // Store guest id set_config('siteguest', $guest->id); + // Make sure user context exists + context_user::instance($guest->id); /// Now create admin user @@ -176,8 +204,10 @@ function xmldb_main_install() { if ($admin->id != 2) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new admin user id!'); } - /// Store list of admins + // Store list of admins set_config('siteadmins', $admin->id); + // Make sure user context exists + context_user::instance($admin->id); /// Install the roles system. @@ -264,17 +294,16 @@ function xmldb_main_install() { require_once($CFG->libdir . '/licenselib.php'); license_manager::install_licenses(); - /// Add two lines of data into this new table + // Init profile pages defaults + if ($DB->record_exists('my_pages', array())) { + throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default profile pages, records already exist.'); + } $mypage = new stdClass(); $mypage->userid = NULL; $mypage->name = '__default'; $mypage->private = 0; $mypage->sortorder = 0; - if (!$DB->record_exists('my_pages', array('userid'=>NULL, 'private'=>0))) { - $DB->insert_record('my_pages', $mypage); - } + $DB->insert_record('my_pages', $mypage); $mypage->private = 1; - if (!$DB->record_exists('my_pages', array('userid'=>NULL, 'private'=>1))) { - $DB->insert_record('my_pages', $mypage); - } + $DB->insert_record('my_pages', $mypage); } diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php index d822bda4a7845..5b3c9ca860b70 100644 --- a/lib/deprecatedlib.php +++ b/lib/deprecatedlib.php @@ -280,28 +280,28 @@ function isteacher() { * @deprecated */ function isteacherinanycourse() { - error('Function isteacherinanycourse() was removed, please use capabilities instead!'); + throw new coding_Exception('Function isteacherinanycourse() was removed, please use capabilities instead!'); } /** * @deprecated */ function get_guest() { - error('Function get_guest() was removed, please use capabilities instead!'); + throw new coding_Exception('Function get_guest() was removed, please use capabilities instead!'); } /** * @deprecated */ function isguest() { - error('Function isguest() was removed, please use capabilities instead!'); + throw new coding_Exception('Function isguest() was removed, please use capabilities instead!'); } /** * @deprecated */ function get_teacher() { - error('Function get_teacher() was removed, please use capabilities instead!'); + throw new coding_Exception('Function get_teacher() was removed, please use capabilities instead!'); } /** @@ -371,15 +371,7 @@ function get_recent_enrolments($courseid, $timestart) { * @return object */ function make_context_subobj($rec) { - $ctx = new StdClass; - $ctx->id = $rec->ctxid; unset($rec->ctxid); - $ctx->path = $rec->ctxpath; unset($rec->ctxpath); - $ctx->depth = $rec->ctxdepth; unset($rec->ctxdepth); - $ctx->contextlevel = $rec->ctxlevel; unset($rec->ctxlevel); - $ctx->instanceid = $rec->id; - - $rec->context = $ctx; - return $rec; + throw new coding_Exception('make_context_subobj() was removed, use new context preloading'); } /** @@ -395,10 +387,7 @@ function make_context_subobj($rec) { * for this thing. */ function is_context_subobj_valid($rec, $contextlevel) { - return isset($rec->context) && isset($rec->context->id) && - isset($rec->context->path) && isset($rec->context->depth) && - isset($rec->context->contextlevel) && isset($rec->context->instanceid) && - $rec->context->contextlevel == $contextlevel && $rec->context->instanceid == $rec->id; + throw new coding_Exception('is_context_subobj_valid() was removed, use new context preloading'); } /** @@ -415,9 +404,7 @@ function is_context_subobj_valid($rec, $contextlevel) { * @param integer $contextlevel the type of thing $rec is, one of the CONTEXT_... constants. */ function ensure_context_subobj_present(&$rec, $contextlevel) { - if (!is_context_subobj_valid($rec, $contextlevel)) { - $rec->context = get_context_instance($contextlevel, $rec->id); - } + throw new coding_Exception('ensure_context_subobj_present() was removed, use new context preloading'); } ########### FROM weblib.php ########################################################################## diff --git a/lib/enrollib.php b/lib/enrollib.php index 9f6795daf197c..fe3dce2a2e387 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -188,7 +188,9 @@ function enrol_is_enabled($enrol) { * Check all the login enrolment information for the given user object * by querying the enrolment plugins * - * @param object $user + * This function may be very slow, use only once after log-in or login-as. + * + * @param stdClass $user * @return void */ function enrol_check_plugins($user) { @@ -201,7 +203,7 @@ function enrol_check_plugins($user) { if (is_siteadmin()) { // no sync for admin user, please use admin accounts only for admin tasks like the unix root user! - // if plugin fails on sync admins need to be able to log in + // if plugin fails on sync admins need to be able to log in and fix the settings return; } @@ -1150,7 +1152,7 @@ public function enrol_user(stdClass $instance, $userid, $roleid = NULL, $timesta } if (isset($USER->enrol['tempguest'][$courseid])) { unset($USER->enrol['tempguest'][$courseid]); - $USER->access = remove_temp_roles($context, $USER->access); + remove_temp_course_roles($context); } } } @@ -1272,7 +1274,7 @@ public function unenrol_user(stdClass $instance, $userid) { } if (isset($USER->enrol['tempguest'][$courseid])) { unset($USER->enrol['tempguest'][$courseid]); - $USER->access = remove_temp_roles($context, $USER->access); + remove_temp_course_roles($context); } } } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 5f5e9350ed7a9..49a0cda2c42c1 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -2790,7 +2790,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $ } else { //expired unset($USER->enrol['tempguest'][$course->id]); - $USER->access = remove_temp_roles($coursecontext, $USER->access); + remove_temp_course_roles($coursecontext); } } @@ -2817,7 +2817,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $ $access = true; // remove traces of previous temp guest access - $USER->access = remove_temp_roles($coursecontext, $USER->access); + remove_temp_course_roles($coursecontext); } else { $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC'); @@ -2831,7 +2831,7 @@ function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $ $until = $enrols[$instance->enrol]->try_autoenrol($instance); if ($until !== false) { $USER->enrol['enrolled'][$course->id] = $until; - $USER->access = remove_temp_roles($coursecontext, $USER->access); + remove_temp_course_roles($coursecontext); $access = true; break; } @@ -3031,6 +3031,7 @@ function require_user_key_login($script, $instance=null) { } /// emulate normal session + enrol_check_plugins($user); session_set_user($user); /// note we are not using normal login @@ -3882,12 +3883,15 @@ function complete_user_login($user) { // this helps prevent session fixation attacks from the same domain session_regenerate_id(true); + // let enrol plugins deal with new enrolments if necessary + enrol_check_plugins($user); + // check enrolments, load caps and setup $USER object session_set_user($user); // reload preferences from DB - unset($user->preference); - check_user_preferences_loaded($user); + unset($USER->preference); + check_user_preferences_loaded($USER); // update login times update_user_login_times(); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 43ff1a2d1149f..e5463fd1e20f0 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -2868,7 +2868,7 @@ public function initialise() { $context = $this->context; if ($context->contextlevel == CONTEXT_BLOCK) { $this->load_block_settings(); - $context = $DB->get_record_sql('SELECT ctx.* FROM {block_instances} bi LEFT JOIN {context} ctx ON ctx.id=bi.parentcontextid WHERE bi.id=?', array($context->instanceid)); + $context = $context->get_parent_context(); } switch ($context->contextlevel) { diff --git a/lib/sessionlib.php b/lib/sessionlib.php index 53f03155f4739..c869ba86afaf2 100644 --- a/lib/sessionlib.php +++ b/lib/sessionlib.php @@ -894,7 +894,8 @@ function get_moodle_cookie() { /** * Setup $USER object - called during login, loginas, etc. - * Preloads capabilities and checks enrolment plugins + * + * Call sync_user_enrolments() manually after log-in, or log-in-as. * * @param stdClass $user full user record object * @return void @@ -902,11 +903,6 @@ function get_moodle_cookie() { function session_set_user($user) { $_SESSION['USER'] = $user; unset($_SESSION['USER']->description); // conserve memory - if (!isset($_SESSION['USER']->access)) { - // check enrolments and load caps only once - enrol_check_plugins($_SESSION['USER']); - load_all_capabilities(); - } sesskey(); // init session key } @@ -950,6 +946,10 @@ function session_loginas($userid, $context) { $user = get_complete_user_data('id', $userid); $user->realuser = $_SESSION['REALUSER']->id; $user->loginascontext = $context; + + // let enrol plugins deal with new enrolments if necessary + enrol_check_plugins($user); + // set up global $USER session_set_user($user); } diff --git a/lib/simpletest/fulltestaccesslib.php b/lib/simpletest/fulltestaccesslib.php new file mode 100644 index 0000000000000..b92e2c0fc688d --- /dev/null +++ b/lib/simpletest/fulltestaccesslib.php @@ -0,0 +1,1172 @@ +. + +/** + * Full functional accesslib test + * + * It is implemented as one test case because it would take hours + * to prepare the fake test site for each test, at the same time + * we have to work around multiple problems in UnitTestCaseUsingDatabase. + * + * @package core + * @copyright 2011 Petr Skoda (http://skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Context caching fixture + */ +class context_inspection extends context_helper { + public static function test_context_cache_size() { + return self::$cache_count; + } +} + + +/** + * Functional test for accesslib.php + * + * Note: execution may take many minutes especially on slower servers. + */ +class accesslib_test extends UnitTestCaseUsingDatabase { + // NOTE: The UnitTestCaseUsingDatabase is problematic and VERY dangerous especially for production servers! + // + // 1. We could create full install without plugins and use the test DB since the very beginning in lib/setup.php. + // 2. We should not execute the tests from the web UI because the static caches all over the place collide, instead + // we could use CLI + // 3. SimpleTest appears to be dead - unfortunately nobody else thinks we should switch to PHPUnit asap + // 4. switching of USER->id alone is not acceptable at all, we must switch the complete USER record + // and probably deal with _SESSION['USER'] too + // 5. we must close session before messing with anything in $USER or $SESSION + // 6. $SITE is not switched + // 7. tons of other static caches are not reset + // 8. etc. + // + // -- + // skodak + + //TODO: add more tests for the remaining accesslib parts that were not touched by the refactoring in 2.2dev + + public static $includecoverage = array('lib/accesslib.php'); + + protected $accesslibprevuser = null; + protected $accesslibprevsite = null; + + /** + * A small functional test of accesslib functions and classes. + */ + public function test_everything_in_accesslib() { + global $USER, $SITE, $CFG, $DB, $ACCESSLIB_PRIVATE; + + // First of all finalize the session, we must not carry over any of this mess to the next page via SESSION!!! + session_get_instance()->write_close(); + + // hack - this is going to take very long time + set_time_limit(3600); + + // Get rid of current user that would not work anyway, + // do NOT even think about using $this->switch_global_user_id()! + if (!isset($this->accesslibprevuser)) { + $this->accesslibprevuser = clone($USER); + $USER = new stdClass(); + $USER->id = 0; + } + + // Backup $SITE global + if (!isset($this->accesslibprevsite)) { + $this->accesslibprevsite = $SITE; + } + + // Switch DB, if it somehow fails or you specified wrong unittest prefix it will nuke real data, you have been warned! + $this->switch_to_test_db(); + + // Let's switch the CFG + $prevcfg = clone($CFG); + $this->switch_to_test_cfg(); + $CFG->config_php_settings = $prevcfg->config_php_settings; + $CFG->noemailever = true; + $CFG->admin = $prevcfg->admin; + $CFG->lang = 'en'; + $CFG->tempdir = $prevcfg->tempdir; + $CFG->cachedir = $prevcfg->cachedir; + $CFG->directorypermissions = $prevcfg->directorypermissions; + $CFG->filepermissions = $prevcfg->filepermissions; + + // Reset all caches + accesslib_clear_all_caches_for_unit_testing(); + + // Add some core tables - this is known to break constantly because we keep adding dependencies... + $tablenames = array('config', 'config_plugins', 'modules', 'course', 'course_modules', 'course_sections', 'course_categories', 'mnet_host', 'mnet_application', + 'capabilities', 'context', 'context_temp', 'role', 'role_capabilities', 'role_allow_switch', 'license', 'my_pages', 'block', 'block_instances', 'block_positions', + 'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels' ,'enrol', 'user_enrolments', 'filter_active', 'filter_config', 'comments', + 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'rating', 'files', 'role_names', 'user_preferences'); + $this->create_test_tables($tablenames, 'lib'); + + // Create all core default records and default settings + require_once("$CFG->libdir/db/install.php"); + xmldb_main_install(); // installs the capabilities too + + // Fake mod_page install + $tablenames = array('page'); + $this->create_test_tables($tablenames, 'mod/page'); + $module = new stdClass(); + require($CFG->dirroot .'/mod/page/version.php'); + $module->name = 'page'; + $pagemoduleid = $DB->insert_record('modules', $module); + update_capabilities('mod_page'); + + // Fake block_online_users install + $plugin = new stdClass(); + $plugin->version = NULL; + $plugin->cron = 0; + include($CFG->dirroot.'/blocks/online_users/version.php'); + $plugin->name = 'online_users'; + $onlineusersblockid = $DB->insert_record('block', $plugin); + update_capabilities('block_online_users'); + + // Finish roles setup + set_config('defaultfrontpageroleid', $DB->get_field('role', 'id', array('archetype'=>'frontpage'))); + set_config('defaultuserroleid', $DB->get_field('role', 'id', array('archetype'=>'user'))); + set_config('notloggedinroleid', $DB->get_field('role', 'id', array('archetype'=>'guest'))); + set_config('rolesactive', 1); + + // Init manual enrol + set_config('status', ENROL_INSTANCE_ENABLED, 'enrol_manual'); + set_config('defaultperiod', 0, 'enrol_manual'); + $manualenrol = enrol_get_plugin('manual'); + + // Fill the site with some real data + $testcategories = array(); + $testcourses = array(); + $testpages = array(); + $testblocks = array(); + $allroles = $DB->get_records_menu('role', array(), 'id', 'archetype, id'); + + $systemcontext = context_system::instance(); + $frontpagecontext = context_course::instance(SITEID); + + // Add block to system context + $bi = new stdClass(); + $bi->blockname = 'online_users'; + $bi->parentcontextid = $systemcontext->id; + $bi->showinsubcontexts = 1; + $bi->pagetypepattern = ''; + $bi->subpagepattern = ''; + $bi->defaultregion = ''; + $bi->defaultweight = 0; + $bi->configdata = ''; + $biid = $DB->insert_record('block_instances', $bi); + context_block::instance($biid); + $testblocks[] = $biid; + + // Some users + $testusers = array(); + for($i=0; $i<20; $i++) { + $user = new stdClass(); + $user->auth = 'manual'; + $user->firstname = 'user'.$i; + $user->lastname = 'user'.$i; + $user->username = 'user'.$i; + $user->password = 'doesnotexist'; + $user->email = "user$i@example.com"; + $user->confirmed = 1; + $user->mnethostid = $CFG->mnet_localhost_id; + $user->lang = $CFG->lang; + $user->maildisplay = 1; + $user->timemodified = time(); + $user->deleted = 0; + $user->lastip = '0.0.0.0'; + $userid = $DB->insert_record('user', $user); + $testusers[$i] = $userid; + $usercontext = context_user::instance($userid); + + // Add block to user profile + $bi->parentcontextid = $usercontext->id; + $biid = $DB->insert_record('block_instances', $bi); + context_block::instance($biid); + $testblocks[] = $biid; + } + // Deleted user - should be ignored everywhere, can not have context + $user = new stdClass(); + $user->auth = 'manual'; + $user->firstname = ''; + $user->lastname = ''; + $user->username = 'user@example.com121132132'; + $user->password = ''; + $user->email = ''; + $user->confirmed = 1; + $user->mnethostid = $CFG->mnet_localhost_id; + $user->lang = $CFG->lang; + $user->maildisplay = 1; + $user->timemodified = time(); + $user->deleted = 1; + $user->lastip = '0.0.0.0'; + $DB->insert_record('user', $user); + unset($user); + + // Add block to frontpage + $bi->parentcontextid = $frontpagecontext->id; + $biid = $DB->insert_record('block_instances', $bi); + $frontpageblockcontext = context_block::instance($biid); + $testblocks[] = $biid; + + // Add a resource to frontpage + $page = new stdClass(); + $page->course = $SITE->id; + $page->intro = '...'; + $page->introformat = FORMAT_HTML; + $page->content = '...'; + $page->contentformat = FORMAT_HTML; + $pageid = $DB->insert_record('page', $page); + $testpages[] = $pageid; + $cm = new stdClass(); + $cm->course = $SITE->id; + $cm->module = $pagemoduleid; + $cm->section = 1; + $cm->instance = $pageid; + $cmid = $DB->insert_record('course_modules', $cm); + $frontpagepagecontext = context_module::instance($cmid); + + // Add block to frontpage resource + $bi->parentcontextid = $frontpagepagecontext->id; + $biid = $DB->insert_record('block_instances', $bi); + $frontpagepageblockcontext = context_block::instance($biid); + $testblocks[] = $biid; + + // Some nested course categories with courses + require_once("$CFG->dirroot/course/lib.php"); + $path = ''; + $parentcat = 0; + for($i=0; $i<5; $i++) { + $cat = new stdClass(); + $cat->name = 'category'.$i; + $cat->parent = $parentcat; + $cat->depth = $i+1; + $cat->sortorder = MAX_COURSES_IN_CATEGORY * ($i+2); + $cat->timemodified = time(); + $catid = $DB->insert_record('course_categories', $cat); + $path = $path . '/' . $catid; + $DB->set_field('course_categories', 'path', $path, array('id'=>$catid)); + $parentcat = $catid; + $testcategories[] = $catid; + $catcontext = context_coursecat::instance($catid); + + if ($i >=4) { + continue; + } + + // Add resource to each category + $bi->parentcontextid = $catcontext->id; + $biid = $DB->insert_record('block_instances', $bi); + context_block::instance($biid); + + // Add a few courses to each category + for($j=0; $j<6; $j++) { + $course = new stdClass(); + $course->fullname = 'course'.$j; + $course->shortname = 'c'.$j; + $course->summary = 'bah bah bah'; + $course->newsitems = 0; + $course->numsections = 1; + $course->category = $catid; + $course->format = 'topics'; + $course->timecreated = time(); + $course->visible = 1; + $course->timemodified = $course->timecreated; + $courseid = $DB->insert_record('course', $course); + $section = new stdClass(); + $section->course = $courseid; + $section->section = 0; + $section->summaryformat = FORMAT_HTML; + $DB->insert_record('course_sections', $section); + $section->section = 1; + $DB->insert_record('course_sections', $section); + $testcourses[] = $courseid; + $coursecontext = context_course::instance($courseid); + + if ($j >= 5) { + continue; + } + // Add manual enrol instance + $manualenrol->add_default_instance($DB->get_record('course', array('id'=>$courseid))); + + // Add block to each course + $bi->parentcontextid = $coursecontext->id; + $biid = $DB->insert_record('block_instances', $bi); + context_block::instance($biid); + $testblocks[] = $biid; + + // Add a resource to each course + $page->course = $courseid; + $pageid = $DB->insert_record('page', $page); + $testpages[] = $pageid; + $cm->course = $courseid; + $cm->instance = $pageid; + $cm->id = $DB->insert_record('course_modules', $cm); + $modcontext = context_module::instance($cm->id); + + // Add block to each module + $bi->parentcontextid = $modcontext->id; + $biid = $DB->insert_record('block_instances', $bi); + context_block::instance($biid); + $testblocks[] = $biid; + } + } + + // Make sure all contexts were created properly + $count = 1; //system + $count += $DB->count_records('user', array('deleted'=>0)); + $count += $DB->count_records('course_categories'); + $count += $DB->count_records('course'); + $count += $DB->count_records('course_modules'); + $count += $DB->count_records('block_instances'); + $this->assertEqual($DB->count_records('context'), $count); + $this->assertEqual($DB->count_records('context', array('depth'=>0)), 0); + $this->assertEqual($DB->count_records('context', array('path'=>NULL)), 0); + + + // ====== context_helper::get_level_name() ================================ + + $levels = context_helper::get_all_levels(); + foreach ($levels as $level=>$classname) { + $name = context_helper::get_level_name($level); + $this->assertFalse(empty($name)); + } + + + // ======= context::instance_by_id(), context_xxx::instance(); + + $context = context::instance_by_id($frontpagecontext->id); + $this->assertidentical($context->contextlevel, CONTEXT_COURSE); + $this->assertFalse(context::instance_by_id(-1, IGNORE_MISSING)); + try { + context::instance_by_id(-1); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + $this->assertTrue(context_system::instance() instanceof context_system); + $this->assertTrue(context_coursecat::instance($testcategories[0]) instanceof context_coursecat); + $this->assertTrue(context_course::instance($testcourses[0]) instanceof context_course); + $this->assertTrue(context_module::instance($testpages[0]) instanceof context_module); + $this->assertTrue(context_block::instance($testblocks[0]) instanceof context_block); + + $this->assertFalse(context_coursecat::instance(-1, IGNORE_MISSING)); + $this->assertFalse(context_course::instance(-1, IGNORE_MISSING)); + $this->assertFalse(context_module::instance(-1, IGNORE_MISSING)); + $this->assertFalse(context_block::instance(-1, IGNORE_MISSING)); + try { + context_coursecat::instance(-1); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + try { + context_course::instance(-1); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + try { + context_module::instance(-1); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + try { + context_block::instance(-1); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + + + // ======= $context->get_url(), $context->get_context_name(), $context->get_capabilities() ========= + + $testcontexts = array(); + $testcontexts[CONTEXT_SYSTEM] = context_system::instance(); + $testcontexts[CONTEXT_COURSECAT] = context_coursecat::instance($testcategories[0]); + $testcontexts[CONTEXT_COURSE] = context_course::instance($testcourses[0]); + $testcontexts[CONTEXT_MODULE] = context_module::instance($testpages[0]); + $testcontexts[CONTEXT_BLOCK] = context_block::instance($testblocks[0]); + + foreach ($testcontexts as $context) { + $name = $context->get_context_name(true, true); + $this->assertFalse(empty($name)); + + $this->assertTrue($context->get_url() instanceof moodle_url); + + $caps = $context->get_capabilities(); + $this->assertTrue(is_array($caps)); + foreach ($caps as $cap) { + $cap = (array)$cap; + $this->assertIdentical(array_keys($cap), array('id', 'name', 'captype', 'contextlevel', 'component', 'riskbitmask')); + } + } + unset($testcontexts); + + // ===== $context->get_course_context() ========================================= + + $this->assertFalse($systemcontext->get_course_context(false)); + try { + $systemcontext->get_course_context(); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + $context = context_coursecat::instance($testcategories[0]); + $this->assertFalse($context->get_course_context(false)); + try { + $context->get_course_context(); + $this->fail('exception expected'); + } catch (Exception $e) { + $this->assertTrue(true); + } + $this->assertIdentical($frontpagecontext->get_course_context(true), $frontpagecontext); + $this->assertIdentical($frontpagepagecontext->get_course_context(true), $frontpagecontext); + $this->assertIdentical($frontpagepageblockcontext->get_course_context(true), $frontpagecontext); + + + // ======= $context->get_parent_context(), $context->get_parent_contexts(), $context->get_parent_context_ids() ======= + + $userid = reset($testusers); + $usercontext = context_user::instance($userid); + $this->assertIdentical($usercontext->get_parent_context(), $systemcontext); + $this->assertIdentical($usercontext->get_parent_contexts(), array($systemcontext->id=>$systemcontext)); + $this->assertIdentical($usercontext->get_parent_contexts(true), array($usercontext->id=>$usercontext, $systemcontext->id=>$systemcontext)); + + $this->assertIdentical($systemcontext->get_parent_contexts(), array()); + $this->assertIdentical($systemcontext->get_parent_contexts(true), array($systemcontext->id=>$systemcontext)); + $this->assertIdentical($systemcontext->get_parent_context_ids(), array()); + $this->assertIdentical($systemcontext->get_parent_context_ids(true), array($systemcontext->id)); + + $this->assertIdentical($frontpagecontext->get_parent_context(), $systemcontext); + $this->assertIdentical($frontpagecontext->get_parent_contexts(), array($systemcontext->id=>$systemcontext)); + $this->assertIdentical($frontpagecontext->get_parent_contexts(true), array($frontpagecontext->id=>$frontpagecontext, $systemcontext->id=>$systemcontext)); + $this->assertIdentical($frontpagecontext->get_parent_context_ids(), array($systemcontext->id)); + $this->assertEqual($frontpagecontext->get_parent_context_ids(true), array($frontpagecontext->id, $systemcontext->id)); + + $this->assertIdentical($systemcontext->get_parent_context(), false); + $frontpagecontext = context_course::instance($SITE->id); + $parent = $systemcontext; + foreach ($testcategories as $catid) { + $catcontext = context_coursecat::instance($catid); + $this->assertIdentical($catcontext->get_parent_context(), $parent); + $parent = $catcontext; + } + $this->assertIdentical($frontpagepagecontext->get_parent_context(), $frontpagecontext); + $this->assertIdentical($frontpageblockcontext->get_parent_context(), $frontpagecontext); + $this->assertIdentical($frontpagepageblockcontext->get_parent_context(), $frontpagepagecontext); + + + // ====== $context->get_child_contexts() ================================ + + $children = $systemcontext->get_child_contexts(); + $this->assertEqual(count($children)+1, $DB->count_records('context')); + + $context = context_coursecat::instance($testcategories[3]); + $children = $context->get_child_contexts(); + $countcats = 0; + $countcourses = 0; + $countblocks = 0; + foreach ($children as $child) { + if ($child->contextlevel == CONTEXT_COURSECAT) { + $countcats++; + } + if ($child->contextlevel == CONTEXT_COURSE) { + $countcourses++; + } + if ($child->contextlevel == CONTEXT_BLOCK) { + $countblocks++; + } + } + $this->assertEqual(count($children), 8); + $this->assertEqual($countcats, 1); + $this->assertEqual($countcourses, 6); + $this->assertEqual($countblocks, 1); + + $context = context_course::instance($testcourses[2]); + $children = $context->get_child_contexts(); + $this->assertEqual(count($children), 3); + + $context = context_module::instance($testpages[3]); + $children = $context->get_child_contexts(); + $this->assertEqual(count($children), 1); + + $context = context_block::instance($testblocks[1]); + $children = $context->get_child_contexts(); + $this->assertEqual(count($children), 0); + + unset($children); + unset($countcats); + unset($countcourses); + unset($countblocks); + + + // ======= context_helper::reset_caches() ============================ + + context_helper::reset_caches(); + $this->assertEqual(context_inspection::test_context_cache_size(), 0); + context_course::instance($SITE->id); + $this->assertEqual(context_inspection::test_context_cache_size(), 1); + + + // ======= context preloading ======================================== + + context_helper::reset_caches(); + $sql = "SELECT ".context_helper::get_preload_record_columns_sql('c')." + FROM {context} c + WHERE c.contextlevel <> ".CONTEXT_SYSTEM; + $records = $DB->get_records_sql($sql); + $firstrecord = reset($records); + $columns = context_helper::get_preload_record_columns('c'); + $firstrecord = (array)$firstrecord; + $this->assertIdentical(array_keys($firstrecord), array_values($columns)); + context_helper::reset_caches(); + foreach ($records as $record) { + context_helper::preload_from_record($record); + $this->assertIdentical($record, new stdClass()); + } + $this->assertEqual(context_inspection::test_context_cache_size(), count($records)); + unset($records); + unset($columns); + + context_helper::reset_caches(); + context_helper::preload_course($SITE->id); + $this->assertEqual(context_inspection::test_context_cache_size(), 4); + + // ====== assign_capability(), unassign_capability() ==================== + + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertFalse($rc); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext->id); + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertEqual($rc->permission, CAP_ALLOW); + assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext->id); + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertEqual($rc->permission, CAP_ALLOW); + assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $allroles['teacher'], $frontpagecontext, true); + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertEqual($rc->permission, CAP_PREVENT); + + assign_capability('moodle/site:accessallgroups', CAP_INHERIT, $allroles['teacher'], $frontpagecontext); + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertFalse($rc); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $allroles['teacher'], $frontpagecontext); + unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext, true); + $rc = $DB->get_record('role_capabilities', array('contextid'=>$frontpagecontext->id, 'roleid'=>$allroles['teacher'], 'capability'=>'moodle/site:accessallgroups')); + $this->assertFalse($rc); + unassign_capability('moodle/site:accessallgroups', $allroles['teacher'], $frontpagecontext->id, true); + unset($rc); + + accesslib_clear_all_caches(false); // must be done after assign_capability() + + + // ======= role_assign(), role_unassign(), role_unassign_all() ============== + + $context = context_course::instance($testcourses[1]); + $this->assertEqual($DB->count_records('role_assignments', array('contextid'=>$context->id)), 0); + role_assign($allroles['teacher'], $testusers[1], $context->id); + role_assign($allroles['teacher'], $testusers[2], $context->id); + role_assign($allroles['manager'], $testusers[1], $context->id); + $this->assertEqual($DB->count_records('role_assignments', array('contextid'=>$context->id)), 3); + role_unassign($allroles['teacher'], $testusers[1], $context->id); + $this->assertEqual($DB->count_records('role_assignments', array('contextid'=>$context->id)), 2); + role_unassign_all(array('contextid'=>$context->id)); + $this->assertEqual($DB->count_records('role_assignments', array('contextid'=>$context->id)), 0); + unset($context); + + accesslib_clear_all_caches(false); // just in case + + + // ====== has_capability(), get_users_by_capability(), role_switch(), reload_all_capabilities() and friends ======================== + + $adminid = get_admin()->id; + $guestid = $CFG->siteguest; + + // Enrol some users into some courses + $course1 = $DB->get_record('course', array('id'=>$testcourses[22]), '*', MUST_EXIST); + $course2 = $DB->get_record('course', array('id'=>$testcourses[7]), '*', MUST_EXIST); + $cms = $DB->get_records('course_modules', array('course'=>$course1->id), 'id'); + $cm1 = reset($cms); + $blocks = $DB->get_records('block_instances', array('parentcontextid'=>context_module::instance($cm1->id)->id), 'id'); + $block1 = reset($blocks); + $instance1 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course1->id)); + $instance2 = $DB->get_record('enrol', array('enrol'=>'manual', 'courseid'=>$course2->id)); + for($i=0; $i<9; $i++) { + $manualenrol->enrol_user($instance1, $testusers[$i], $allroles['student']); + } + $manualenrol->enrol_user($instance1, $testusers[8], $allroles['teacher']); + $manualenrol->enrol_user($instance1, $testusers[9], $allroles['editingteacher']); + + for($i=10; $i<15; $i++) { + $manualenrol->enrol_user($instance2, $testusers[$i], $allroles['student']); + } + $manualenrol->enrol_user($instance2, $testusers[15], $allroles['editingteacher']); + + // Add tons of role assignments - the more the better + role_assign($allroles['coursecreator'], $testusers[11], context_coursecat::instance($testcategories[2])); + role_assign($allroles['manager'], $testusers[12], context_coursecat::instance($testcategories[1])); + role_assign($allroles['student'], $testusers[9], context_module::instance($cm1->id)); + role_assign($allroles['teacher'], $testusers[8], context_module::instance($cm1->id)); + role_assign($allroles['guest'], $testusers[13], context_course::instance($course1->id)); + role_assign($allroles['teacher'], $testusers[7], context_block::instance($block1->id)); + role_assign($allroles['manager'], $testusers[9], context_block::instance($block1->id)); + role_assign($allroles['editingteacher'], $testusers[9], context_course::instance($course1->id)); + + role_assign($allroles['teacher'], $adminid, context_course::instance($course1->id)); + role_assign($allroles['editingteacher'], $adminid, context_block::instance($block1->id)); + + // Add tons of overrides - the more the better + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpageblockcontext, true); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpageblockcontext, true); + assign_capability('moodle/block:view', CAP_PROHIBIT, $allroles['guest'], $frontpageblockcontext, true); + assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['user'], $frontpageblockcontext, true); + assign_capability('block/online_users:viewlist', CAP_PREVENT, $allroles['student'], $frontpageblockcontext, true); + + assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $CFG->defaultuserroleid, $frontpagepagecontext, true); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true); + assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true); + assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true); + assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true); + + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true); + assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true); + assign_capability('mod/page:view', CAP_ALLOW, $allroles['guest'], $frontpagecontext, true); + assign_capability('mod/page:view', CAP_PROHIBIT, $allroles['user'], $frontpagecontext, true); + + assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $systemcontext, true); + + accesslib_clear_all_caches(false); // must be done after assign_capability() + + // Extra tests for guests and not-logged-in users because they can not be verified by cross checking + // with get_users_by_capability() where they are ignored + $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, $guestid)); + $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, $guestid)); + $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, $guestid)); + $this->assertFalse(has_capability('mod/page:view', $systemcontext, $guestid)); + + $this->assertFalse(has_capability('moodle/block:view', $frontpageblockcontext, 0)); + $this->assertFalse(has_capability('mod/page:view', $frontpagepagecontext, 0)); + $this->assertTrue(has_capability('mod/page:view', $frontpagecontext, 0)); + $this->assertFalse(has_capability('mod/page:view', $systemcontext, 0)); + + $this->assertFalse(has_capability('moodle/course:create', $systemcontext, $testusers[11])); + $this->assertTrue(has_capability('moodle/course:create', context_coursecat::instance($testcategories[2]), $testusers[11])); + $this->assertFalse(has_capability('moodle/course:create', context_course::instance($testcourses[1]), $testusers[11])); + $this->assertTrue(has_capability('moodle/course:create', context_course::instance($testcourses[19]), $testusers[11])); + + $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[1]), $testusers[9])); + $this->assertFalse(has_capability('moodle/course:update', context_course::instance($testcourses[19]), $testusers[9])); + $this->assertFalse(has_capability('moodle/course:update', $systemcontext, $testusers[9])); + + // Test the list of enrolled users + $coursecontext = context_course::instance($course1->id); + $enrolled = get_enrolled_users($coursecontext); + $this->assertEqual(count($enrolled), 10); + for($i=0; $i<10; $i++) { + $this->assertTrue(isset($enrolled[$testusers[$i]])); + } + $enrolled = get_enrolled_users($coursecontext, 'moodle/course:update'); + $this->assertEqual(count($enrolled), 1); + $this->assertTrue(isset($enrolled[$testusers[9]])); + unset($enrolled); + + // role switching + $userid = $testusers[9]; + $USER = $DB->get_record('user', array('id'=>$userid)); + load_all_capabilities(); + $coursecontext = context_course::instance($course1->id); + $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); + $this->assertFalse(is_role_switched($course1->id)); + role_switch($allroles['student'], $coursecontext); + $this->assertTrue(is_role_switched($course1->id)); + $this->assertEqual($USER->access['rsw'][$coursecontext->path], $allroles['student']); + $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); + reload_all_capabilities(); + $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); + role_switch(0, $coursecontext); + $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); + $userid = $adminid; + $USER = $DB->get_record('user', array('id'=>$userid)); + load_all_capabilities(); + $coursecontext = context_course::instance($course1->id); + $blockcontext = context_block::instance($block1->id); + $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); + role_switch($allroles['student'], $coursecontext); + $this->assertEqual($USER->access['rsw'][$coursecontext->path], $allroles['student']); + $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); + reload_all_capabilities(); + $this->assertFalse(has_capability('moodle/course:update', $blockcontext)); + load_all_capabilities(); + $this->assertTrue(has_capability('moodle/course:update', $blockcontext)); + + // temp course role for enrol + $DB->delete_records('cache_flags', array()); // this prevents problem with dirty contexts immediately resetting the temp role - this is a known problem... + $userid = $testusers[5]; + $roleid = $allroles['editingteacher']; + $USER = $DB->get_record('user', array('id'=>$userid)); + load_all_capabilities(); + $coursecontext = context_course::instance($course1->id); + $this->assertFalse(has_capability('moodle/course:update', $coursecontext)); + $this->assertFalse(isset($USER->access['ra'][$coursecontext->path][$roleid])); + load_temp_course_role($coursecontext, $roleid); + $this->assertEqual($USER->access['ra'][$coursecontext->path][$roleid], $roleid); + $this->assertTrue(has_capability('moodle/course:update', $coursecontext)); + remove_temp_course_roles($coursecontext); + $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); + load_temp_course_role($coursecontext, $roleid); + reload_all_capabilities(); + $this->assertFalse(has_capability('moodle/course:update', $coursecontext, $userid)); + $USER = new stdClass(); + $USER->id = 0; + + // Now cross check has_capability() with get_users_by_capability(), each using different code paths, + // they have to be kept in sync, usually only one of them breaks, so we know when something is wrong, + // at the same time validate extra restrictions (guest read only no risks, admin exception, non existent and deleted users) + $contexts = $DB->get_records('context', array(), 'id'); + $contexts = array_values($contexts); + $capabilities = $DB->get_records('capabilities', array(), 'id'); + $capabilities = array_values($capabilities); + $roles = array($allroles['guest'], $allroles['user'], $allroles['teacher'], $allroles['editingteacher'], $allroles['coursecreator'], $allroles['manager']); + + // Random time! + srand(666); + foreach($testusers as $userid) { // no guest or deleted + // each user gets 0-20 random roles + $rcount = rand(0, 10); + for($j=0; $j<$rcount; $j++) { + $roleid = $roles[rand(0, count($roles)-1)]; + $contextid = $contexts[rand(0, count($contexts)-1)]->id; + role_assign($roleid, $userid, $contextid); + } + } + $permissions = array(CAP_ALLOW, CAP_PREVENT, CAP_INHERIT, CAP_PREVENT); + for($j=0; $j<10000; $j++) { + $roleid = $roles[rand(0, count($roles)-1)]; + $contextid = $contexts[rand(0, count($contexts)-1)]->id; + $permission = $permissions[rand(0,count($permissions)-1)]; + $capname = $capabilities[rand(0, count($capabilities)-1)]->name; + assign_capability($capname, $permission, $roleid, $contextid, true); + } + unset($permissions); + unset($roles); + unset($contexts); + unset($users); + unset($capabilities); + + accesslib_clear_all_caches(false); // must be done after assign_capability() + + // Test time - let's set up some real user, just in case the logic for USER affects the others... + $USER = $DB->get_record('user', array('id'=>$testusers[3])); + load_all_capabilities(); + + $contexts = $DB->get_records('context', array(), 'id'); + $users = $DB->get_records('user', array(), 'id', 'id'); + $capabilities = $DB->get_records('capabilities', array(), 'id'); + $users[0] = null; // not-logged-in user + $users[-1] = null; // non-existent user + + foreach ($contexts as $crecord) { + $context = context::instance_by_id($crecord->id); + if ($coursecontext = $context->get_course_context(false)) { + $enrolled = get_enrolled_users($context); + } else { + $enrolled = array(); + } + foreach ($capabilities as $cap) { + $allowed = get_users_by_capability($context, $cap->name, 'u.id, u.username'); + if ($enrolled) { + $enrolledwithcap = get_enrolled_users($context, $cap->name); + } else { + $enrolledwithcap = array(); + } + foreach ($users as $userid=>$unused) { + if ($userid == 0 or isguestuser($userid)) { + if ($userid == 0) { + $CFG->forcelogin = true; + $this->assertFalse(has_capability($cap->name, $context, $userid)); + unset($CFG->forcelogin); + } + if (($cap->captype === 'write') or ($cap->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))) { + $this->assertFalse(has_capability($cap->name, $context, $userid)); + } + $this->assertFalse(isset($allowed[$userid])); + } else { + if (is_siteadmin($userid)) { + $this->assertTrue(has_capability($cap->name, $context, $userid, true)); + } + $hascap = has_capability($cap->name, $context, $userid, false); + $this->assertIdentical($hascap, isset($allowed[$userid]), "Capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); + if (isset($enrolled[$userid])) { + $this->assertIdentical(isset($allowed[$userid]), isset($enrolledwithcap[$userid]), "Enrolment with capability result mismatch user:$userid, context:$context->id, $cap->name, hascap: ".(int)$hascap." "); + } + } + } + } + } + // Back to nobody + $USER = new stdClass(); + $USER->id = 0; + unset($contexts); + unset($users); + unset($capabilities); + + // Now let's do all the remaining tests that break our carefully prepared fake site + + + + // ======= $context->mark_dirty() ======================================= + + $DB->delete_records('cache_flags', array()); + accesslib_clear_all_caches(false); + $systemcontext->mark_dirty(); + $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); + $this->assertTrue(isset($dirty[$systemcontext->path])); + $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$systemcontext->path])); + + + // ======= $context->reload_if_dirty(); ================================= + + $DB->delete_records('cache_flags', array()); + accesslib_clear_all_caches(false); + load_all_capabilities(); + $context = context_course::instance($testcourses[2]); + $page = $DB->get_record('page', array('course'=>$testcourses[2])); + $pagecontext = context_module::instance($page->id); + + $context->mark_dirty(); + $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); + $USER->access['test'] = true; + $context->reload_if_dirty(); + $this->assertFalse(isset($USER->access['test'])); + + $context->mark_dirty(); + $this->assertTrue(isset($ACCESSLIB_PRIVATE->dirtycontexts[$context->path])); + $USER->access['test'] = true; + $pagecontext->reload_if_dirty(); + $this->assertFalse(isset($USER->access['test'])); + + + // ======= context_helper::build_all_paths() ============================ + + $oldcontexts = $DB->get_records('context', array(), 'id'); + $DB->set_field_select('context', 'path', NULL, "contextlevel <> ".CONTEXT_SYSTEM); + $DB->set_field_select('context', 'depth', 0, "contextlevel <> ".CONTEXT_SYSTEM); + context_helper::build_all_paths(); + $newcontexts = $DB->get_records('context', array(), 'id'); + $this->assertIdentical($oldcontexts, $newcontexts); + unset($oldcontexts); + unset($newcontexts); + + + // ======= $context->reset_paths() ====================================== + + $context = context_course::instance($testcourses[2]); + $children = $context->get_child_contexts(); + $context->reset_paths(false); + $this->assertIdentical($DB->get_field('context', 'path', array('id'=>$context->id)), NULL); + $this->assertEqual($DB->get_field('context', 'depth', array('id'=>$context->id)), 0); + foreach ($children as $child) { + $this->assertIdentical($DB->get_field('context', 'path', array('id'=>$child->id)), NULL); + $this->assertEqual($DB->get_field('context', 'depth', array('id'=>$child->id)), 0); + } + $this->assertEqual(count($children)+1, $DB->count_records('context', array('depth'=>0))); + $this->assertEqual(count($children)+1, $DB->count_records('context', array('path'=>NULL))); + + $context = context_course::instance($testcourses[2]); + $context->reset_paths(true); + $context = context_course::instance($testcourses[2]); + $this->assertEqual($DB->get_field('context', 'path', array('id'=>$context->id)), $context->path); + $this->assertEqual($DB->get_field('context', 'depth', array('id'=>$context->id)), $context->depth); + $this->assertEqual(0, $DB->count_records('context', array('depth'=>0))); + $this->assertEqual(0, $DB->count_records('context', array('path'=>NULL))); + + + // ====== $context->update_moved(); ====================================== + + accesslib_clear_all_caches(false); + $DB->delete_records('cache_flags', array()); + $course = $DB->get_record('course', array('id'=>$testcourses[0])); + $context = context_course::instance($course->id); + $oldpath = $context->path; + $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); + $categorycontext = context_coursecat::instance($miscid); + $course->category = $miscid; + $DB->update_record('course', $course); + $context->update_moved($categorycontext); + + $context = context_course::instance($course->id); + $this->assertIdentical($context->get_parent_context(), $categorycontext); + $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); + $this->assertTrue(isset($dirty[$oldpath])); + $this->assertTrue(isset($dirty[$context->path])); + + + // ====== $context->delete_content() ===================================== + + context_helper::reset_caches(); + $context = context_module::instance($testpages[3]); + $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); + $this->assertEqual(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); + $context->delete_content(); + $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); + $this->assertEqual(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); + + + // ====== $context->delete() ============================= + + context_helper::reset_caches(); + $context = context_module::instance($testpages[4]); + $this->assertTrue($DB->record_exists('context', array('id'=>$context->id))); + $this->assertEqual(1, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); + $bi = $DB->get_record('block_instances', array('parentcontextid'=>$context->id)); + $bicontext = context_block::instance($bi->id); + $DB->delete_records('cache_flags', array()); + $context->delete(); // should delete also linked blocks + $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); + $this->assertTrue(isset($dirty[$context->path])); + $this->assertFalse($DB->record_exists('context', array('id'=>$context->id))); + $this->assertFalse($DB->record_exists('context', array('id'=>$bicontext->id))); + $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_MODULE, 'instanceid'=>$testpages[4]))); + $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK, 'instanceid'=>$bi->id))); + $this->assertEqual(0, $DB->count_records('block_instances', array('parentcontextid'=>$context->id))); + context_module::instance($testpages[4]); + + + // ====== context_helper::delete_instance() ============================= + + context_helper::reset_caches(); + $lastcourse = array_pop($testcourses); + $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); + $coursecontext = context_course::instance($lastcourse); + $this->assertEqual(context_inspection::test_context_cache_size(), 1); + $this->assertFalse($coursecontext->instanceid == CONTEXT_COURSE); + $DB->delete_records('cache_flags', array()); + context_helper::delete_instance(CONTEXT_COURSE, $lastcourse); + $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); + $this->assertTrue(isset($dirty[$coursecontext->path])); + $this->assertEqual(context_inspection::test_context_cache_size(), 0); + $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$lastcourse))); + context_course::instance($lastcourse); + + + // ======= context_helper::create_instances() ========================== + + $prevcount = $DB->count_records('context'); + $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); + context_helper::create_instances(null, true); + $this->assertIdentical($DB->count_records('context'), $prevcount); + $this->assertEqual($DB->count_records('context', array('depth'=>0)), 0); + $this->assertEqual($DB->count_records('context', array('path'=>NULL)), 0); + + $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); + $DB->delete_records('block_instances', array()); + $prevcount = $DB->count_records('context'); + $DB->delete_records_select('context', 'contextlevel <> '.CONTEXT_SYSTEM); + context_helper::create_instances(null, true); + $this->assertIdentical($DB->count_records('context'), $prevcount); + $this->assertEqual($DB->count_records('context', array('depth'=>0)), 0); + $this->assertEqual($DB->count_records('context', array('path'=>NULL)), 0); + + + // ======= context_helper::cleanup_instances() ========================== + + $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); + $DB->delete_records('course', array('id'=>$lastcourse)); + $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); + $DB->delete_records('course_categories', array('id'=>$lastcategory)); + $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); + $DB->delete_records('user', array('id'=>$lastuser)); + $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id)); + $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid)); + context_helper::cleanup_instances(); + $count = 1; //system + $count += $DB->count_records('user', array('deleted'=>0)); + $count += $DB->count_records('course_categories'); + $count += $DB->count_records('course'); + $count += $DB->count_records('course_modules'); + $count += $DB->count_records('block_instances'); + $this->assertEqual($DB->count_records('context'), $count); + + + // ======= context cache size restrictions ============================== + + $testusers= array(); + for ($i=0; $iauth = 'manual'; + $user->firstname = 'xuser'.$i; + $user->lastname = 'xuser'.$i; + $user->username = 'xuser'.$i; + $user->password = 'doesnotexist'; + $user->email = "xuser$i@example.com"; + $user->confirmed = 1; + $user->mnethostid = $CFG->mnet_localhost_id; + $user->lang = $CFG->lang; + $user->maildisplay = 1; + $user->timemodified = time(); + $user->lastip = '0.0.0.0'; + $userid = $DB->insert_record('user', $user); + $testusers[$i] = $userid; + } + context_helper::create_instances(null, true); + context_helper::reset_caches(); + for ($i=0; $iassertEqual(context_inspection::test_context_cache_size(), CONTEXT_CACHE_MAX_SIZE); + } else if ($i == CONTEXT_CACHE_MAX_SIZE) { + // once the limit is reached roughly 1/3 of records should be removed from cache + $this->assertEqual(context_inspection::test_context_cache_size(), (int)(CONTEXT_CACHE_MAX_SIZE * (2/3) +102)); + } + } + // We keep the first 100 cached + $prevsize = context_inspection::test_context_cache_size(); + for ($i=0; $i<100; $i++) { + context_user::instance($testusers[$i]); + $this->assertEqual(context_inspection::test_context_cache_size(), $prevsize); + } + context_user::instance($testusers[102]); + $this->assertEqual(context_inspection::test_context_cache_size(), $prevsize+1); + unset($testusers); + + + + // ================================================================= + // ======= basic test of legacy functions ========================== + // ================================================================= + // note: watch out, the fake site might be pretty borked already + + $this->assertIdentical(get_system_context(), context_system::instance()); + + foreach ($DB->get_records('context') as $contextid=>$record) { + $context = context::instance_by_id($contextid); + $this->assertIdentical(get_context_instance_by_id($contextid), $context); + $this->assertIdentical(get_context_instance($record->contextlevel, $record->instanceid), $context); + $this->assertIdentical(get_parent_contexts($context), $context->get_parent_context_ids()); + if ($context->id == SYSCONTEXTID) { + $this->assertIdentical(get_parent_contextid($context), false); + } else { + $this->assertIdentical(get_parent_contextid($context), $context->get_parent_context()->id); + } + } + + $children = get_child_contexts($systemcontext); + $this->assertEqual(count($children), $DB->count_records('context')-1); + unset($children); + + $DB->delete_records('context', array('contextlevel'=>CONTEXT_BLOCK)); + create_contexts(); + $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_BLOCK))); + + $DB->set_field('context', 'depth', 0, array('contextlevel'=>CONTEXT_BLOCK)); + build_context_path(); + $this->assertFalse($DB->record_exists('context', array('depth'=>0))); + + $lastcourse = $DB->get_field_sql("SELECT MAX(id) FROM {course}"); + $DB->delete_records('course', array('id'=>$lastcourse)); + $lastcategory = $DB->get_field_sql("SELECT MAX(id) FROM {course_categories}"); + $DB->delete_records('course_categories', array('id'=>$lastcategory)); + $lastuser = $DB->get_field_sql("SELECT MAX(id) FROM {user} WHERE deleted=0"); + $DB->delete_records('user', array('id'=>$lastuser)); + $DB->delete_records('block_instances', array('parentcontextid'=>$frontpagepagecontext->id)); + $DB->delete_records('course_modules', array('id'=>$frontpagepagecontext->instanceid)); + cleanup_contexts(); + $count = 1; //system + $count += $DB->count_records('user', array('deleted'=>0)); + $count += $DB->count_records('course_categories'); + $count += $DB->count_records('course'); + $count += $DB->count_records('course_modules'); + $count += $DB->count_records('block_instances'); + $this->assertEqual($DB->count_records('context'), $count); + + context_helper::reset_caches(); + preload_course_contexts($SITE->id); + $this->assertEqual(context_inspection::test_context_cache_size(), 1); + + context_helper::reset_caches(); + list($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSECAT, 'ctx'); + $sql = "SELECT c.id $select FROM {course_categories} c $join"; + $records = $DB->get_records_sql($sql); + foreach ($records as $record) { + context_instance_preload($record); + $record = (array)$record; + $this->assertEqual(1, count($record)); // only id left + } + $this->assertEqual(count($records), context_inspection::test_context_cache_size()); + + accesslib_clear_all_caches(true); + $DB->delete_records('cache_flags', array()); + mark_context_dirty($systemcontext->path); + $dirty = get_cache_flags('accesslib/dirtycontexts', time()-2); + $this->assertTrue(isset($dirty[$systemcontext->path])); + + accesslib_clear_all_caches(false); + $DB->delete_records('cache_flags', array()); + $course = $DB->get_record('course', array('id'=>$testcourses[2])); + $context = get_context_instance(CONTEXT_COURSE, $course->id); + $oldpath = $context->path; + $miscid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}"); + $categorycontext = context_coursecat::instance($miscid); + $course->category = $miscid; + $DB->update_record('course', $course); + context_moved($context, $categorycontext); + $context = get_context_instance(CONTEXT_COURSE, $course->id); + $this->assertIdentical($context->get_parent_context(), $categorycontext); + + $this->assertTrue($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2]))); + delete_context(CONTEXT_COURSE, $testcourses[2]); + $this->assertFalse($DB->record_exists('context', array('contextlevel'=>CONTEXT_COURSE, 'instanceid'=>$testcourses[2]))); + + $name = get_contextlevel_name(CONTEXT_COURSE); + $this->assertFalse(empty($name)); + + $context = get_context_instance(CONTEXT_COURSE, $testcourses[2]); + $name = print_context_name($context); + $this->assertFalse(empty($name)); + + $url = get_context_url($coursecontext); + $this->assertFalse($url instanceof modole_url); + + $page = $DB->get_record('page', array('id'=>$testpages[7])); + $context = get_context_instance(CONTEXT_MODULE, $page->id); + $coursecontext = get_course_context($context); + $this->assertEqual($coursecontext->contextlevel, CONTEXT_COURSE); + $this->assertEqual(get_courseid_from_context($context), $page->course); + + $caps = fetch_context_capabilities($systemcontext); + $this->assertTrue(is_array($caps)); + unset($caps); + } + + public function tearDown() { + global $USER, $SITE; + if (isset($this->accesslibprevuser)) { + $USER = $this->accesslibprevuser; + } + if (isset($this->accesslibprevsite)) { + $SITE = $this->accesslibprevsite; + } + + + parent::tearDown(); + } +} + diff --git a/lib/simpletest/testaccesslib.php b/lib/simpletest/testaccesslib.php deleted file mode 100644 index 44818797461a1..0000000000000 --- a/lib/simpletest/testaccesslib.php +++ /dev/null @@ -1,382 +0,0 @@ -assertEqual(get_parent_contexts($context), array()); - - $context = new stdClass; - $context->path = '/1/25'; - $this->assertEqual(get_parent_contexts($context), array(1)); - - $context = new stdClass; - $context->path = '/1/123/234/345/456'; - $this->assertEqual(get_parent_contexts($context), array(345, 234, 123, 1)); - } - - function test_get_parent_contextid() { - $context = get_context_instance(CONTEXT_SYSTEM); - $this->assertFalse(get_parent_contextid($context)); - - $context = new stdClass; - $context->path = '/1/25'; - $this->assertEqual(get_parent_contextid($context), 1); - - $context = new stdClass; - $context->path = '/1/123/234/345/456'; - $this->assertEqual(get_parent_contextid($context), 345); - } - - function test_get_users_by_capability() { - global $CFG; - - $tablenames = array('capabilities', 'context', 'role', 'role_capabilities', - 'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels', - 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'course'); - $this->create_test_tables($tablenames, 'lib'); - - accesslib_clear_all_caches_for_unit_testing(); - $this->switch_to_test_db(); - $this->switch_to_test_cfg(); - - $course = new stdClass(); - $course->category = 0; - $this->testdb->insert_record('course', $course); - $syscontext = get_system_context(false); - - /// Install the roles system. - $coursecreatorrole = create_role(get_string('coursecreators'), 'coursecreator', - get_string('coursecreatorsdescription'), 'coursecreator'); - $editteacherrole = create_role(get_string('defaultcourseteacher'), 'editingteacher', - get_string('defaultcourseteacherdescription'), 'editingteacher'); - $noneditteacherrole = create_role(get_string('noneditingteacher'), 'teacher', - get_string('noneditingteacherdescription'), 'teacher'); - $studentrole = create_role(get_string('defaultcoursestudent'), 'student', - get_string('defaultcoursestudentdescription'), 'student'); - $guestrole = create_role(get_string('guest'), 'guest', - get_string('guestdescription'), 'guest'); - $userrole = create_role(get_string('authenticateduser'), 'user', - get_string('authenticateduserdescription'), 'user'); - - /// Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles - update_capabilities('moodle'); - update_capabilities('mod_forum'); - update_capabilities('mod_quiz'); - - // Create some nested contexts. instanceid does not matter for this. Just - // ensure we don't violate any unique keys by using an unlikely number. - // We will fix paths in a second. - $contexts = $this->load_test_data('context', - array('contextlevel', 'instanceid', 'path', 'depth'), array( - 1 => array(40, 666, '', 2), - 2 => array(50, 666, '', 3), - 3 => array(70, 666, '', 4), - )); - $contexts[0] = $syscontext; - $contexts[1]->path = $contexts[0]->path . '/' . $contexts[1]->id; - $this->testdb->set_field('context', 'path', $contexts[1]->path, array('id' => $contexts[1]->id)); - $contexts[2]->path = $contexts[1]->path . '/' . $contexts[2]->id; - $this->testdb->set_field('context', 'path', $contexts[2]->path, array('id' => $contexts[2]->id)); - $contexts[3]->path = $contexts[2]->path . '/' . $contexts[3]->id; - $this->testdb->set_field('context', 'path', $contexts[3]->path, array('id' => $contexts[3]->id)); - - // Now make some test users. - $users = $this->load_test_data('user', - array('username', 'confirmed', 'deleted'), array( - 'a' => array('a', 1, 0), - 'cc' => array('cc', 1, 0), - 't1' => array('t1', 1, 0), - 's1' => array('s1', 1, 0), - 's2' => array('s2', 1, 0), - 'del' => array('del', 1, 1), - 'unc' => array('unc', 0, 0), - )); - - // Get some of the standard roles. - $creator = $this->testdb->get_record('role', array('shortname' => 'coursecreator')); - $teacher = $this->testdb->get_record('role', array('shortname' => 'editingteacher')); - $student = $this->testdb->get_record('role', array('shortname' => 'student')); - $authuser = $this->testdb->get_record('role', array('shortname' => 'user')); - - // And some role assignments. - $ras = $this->load_test_data('role_assignments', - array('userid', 'roleid', 'contextid'), array( - 'cc' => array($users['cc']->id, $creator->id, $contexts[1]->id), - 't1' => array($users['t1']->id, $teacher->id, $contexts[2]->id), - 's1' => array($users['s1']->id, $student->id, $contexts[2]->id), - 's2' => array($users['s2']->id, $student->id, $contexts[2]->id), - )); - - // And make user a into admin - $CFG->siteadmins = $users['a']->id; - $CFG->defaultuserroleid = $userrole; - - // And some group memebership. - $gms = $this->load_test_data('groups_members', - array('userid', 'groupid'), array( - array($users['t1']->id, 666), - array($users['s1']->id, 666), - array($users['s2']->id, 667), - )); - - // Test some simple cases - check that looking in coruse and module contextlevel gives the same answer. - foreach (array(2, 3) as $conindex) { - $results = get_users_by_capability($contexts[$conindex], 'mod/forum:replypost'); - // note: admin accounts are never returned, so no admin return here - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - $results)); - // Paging. - $firstuser = reset($results); - $this->assertEqual(array($firstuser->id => $firstuser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 0, 1)); - $seconduser = next($results); - $this->assertEqual(array($seconduser->id => $seconduser), get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', 1, 1)); - // $doanything = false (ignored now) - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', '', false))); - // group - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666))); - // exceptions - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', '', array($users['t1']->id)))); - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['s1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 666, array($users['t1']->id)))); - // $useviewallgroups - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], 'mod/forum:replypost', '', '', '', '', 667, '', false, false, true))); - // More than one capability. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[$conindex], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts')))); - } - -// For reference: get_users_by_capability argument order: -// $context, $capability, $fields='', $sort='', $limitfrom='', $limitnum='', -// $groups='', $exceptions='', $doanything=true, $view=false, $useviewallgroups=false - - // Now add some role overrides. - $rcs = $this->load_test_data('role_capabilities', - array('capability', 'roleid', 'contextid', 'permission'), array( - array('mod/forum:replypost', $student->id, $contexts[1]->id, CAP_PREVENT), - array('mod/forum:replypost', $student->id, $contexts[3]->id, CAP_ALLOW), - array('mod/quiz:attempt', $student->id, $contexts[2]->id, CAP_PREVENT), - array('mod/forum:startdiscussion', $student->id, $contexts[1]->id, CAP_PROHIBIT), - array('mod/forum:startdiscussion', $student->id, $contexts[3]->id, CAP_ALLOW), - array('mod/forum:viewrating', $authuser->id, $contexts[1]->id, CAP_PROHIBIT), - array('mod/forum:createattachment', $authuser->id, $contexts[3]->id, CAP_PREVENT), - )); - - // Now test the overridden cases. - // Students prevented at category level, with and without doanything. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[2], 'mod/forum:replypost'))); - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[2], 'mod/forum:replypost', '', '', '', '', '', '', false))); - // Students prevented at category level, but re-allowed at module level, with and without doanything. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], 'mod/forum:replypost', '', '', '', '', '', '', false))); - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], 'mod/forum:replypost'))); - // Students prohibited at category level, re-allowed at module level should have no effect. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[2], 'mod/forum:startdiscussion'))); - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], 'mod/forum:startdiscussion'))); - // Prevent on logged-in user should be overridden by student allow. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['t1']->id, $users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], 'mod/forum:createattachment'))); - - // Prohibit on logged-in user should trump student/teacher allow. - $this->assert(new ArraysHaveSameValuesExpectation( - array()), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], 'mod/forum:viewrating'))); - - // More than one capability, where students have one, but not the other. - $this->assert(new ArraysHaveSameValuesExpectation( - array($users['s1']->id, $users['s2']->id)), - array_map(create_function('$o', 'return $o->id;'), - get_users_by_capability($contexts[3], array('mod/quiz:attempt', 'mod/quiz:reviewmyattempts'), '', '', '', '', '', '', false))); - } - - function test_get_switchable_roles() { - global $USER; - - $tablenames = array('role' , 'role_capabilities', 'role_assignments', 'role_allow_switch', - 'capabilities', 'context', 'role_names'); - $this->create_test_tables($tablenames, 'lib'); - - $this->switch_to_test_db(); - $this->switch_to_test_cfg(); - - // Ensure SYSCONTEXTID is set. - get_context_instance(CONTEXT_SYSTEM); - - $contexts = $this->load_test_data('context', - array('contextlevel', 'instanceid', 'path', 'depth'), array( - 'sys' => array(CONTEXT_SYSTEM, 0, '/' . SYSCONTEXTID, 1), - 'cat' => array(CONTEXT_COURSECAT, 66, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1), 2), - 'cou' => array(CONTEXT_COURSE, 666, '/' . SYSCONTEXTID . '/' . (SYSCONTEXTID + 1) . '/' . (SYSCONTEXTID + 2), 3), - 'fp' => array(CONTEXT_COURSE, SITEID, '/' . SYSCONTEXTID . '/' . SITEID, 2))); - $this->testdb->set_field('context', 'id', SYSCONTEXTID, array('id' => $contexts['sys']->id)); - $this->testdb->set_field('context', 'id', SYSCONTEXTID + 1, array('id' => $contexts['cat']->id)); - $this->testdb->set_field('context', 'id', SYSCONTEXTID + 2, array('id' => $contexts['cou']->id)); - $syscontext = $contexts['sys']; - $syscontext->id = SYSCONTEXTID; - $context = $contexts['cou']; - $context->id = SYSCONTEXTID + 2; - - $roles = $this->load_test_data('role', - array( 'name', 'shortname', 'description', 'sortorder'), array( - 'r1' => array( 'r1', 'r1', 'not null', 2), - 'r2' => array( 'r2', 'r2', 'not null', 3), - 'funny' => array('funny', 'funny', 'not null', 4))); - $r1id = $roles['r1']->id; - $r2id = $roles['r2']->id; - $funnyid = $roles['funny']->id; // strange role - - // Note that get_switchable_roles requires at least one capability for - // each role. I am not really clear why it was implemented that way - // but this makes the test work. - $roles = $this->load_test_data('role_capabilities', - array('roleid', 'capability'), array( - array($r1id, 'moodle/say:hello'), - array($r2id, 'moodle/say:hello'), - array($funnyid, 'moodle/say:hello'), - )); - - $this->load_test_data('role_assignments', - array('userid', 'contextid', 'roleid'), array( - array( 2, SYSCONTEXTID + 1 , $r1id), - array( 3, SYSCONTEXTID + 2 , $r2id))); - - $this->load_test_data('role_allow_switch', - array('roleid', 'allowswitch'), array( - array( $r1id , $r2id), - array( $r2id , $r1id), - array( $r2id , $r2id), - array( $r2id , $funnyid))); - - // r1 should be able to switch to r2, but this user only has r1 in $context, not $syscontext. - $this->switch_global_user_id(2); - accesslib_clear_all_caches_for_unit_testing(); - $this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext))); - $this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($context))); - $this->revert_global_user_id(); - - // The table says r2 should be able to switch to all of r1, r2 and funny; - // this used to be restricted further beyond the switch table (for example - // to prevent you switching to roles with doanything) but is not any more - // (for example because doanything does not exist). - $this->switch_global_user_id(3); - accesslib_clear_all_caches_for_unit_testing(); - $this->assert(new ArraysHaveSameValuesExpectation(array()), array_keys(get_switchable_roles($syscontext))); - $this->assert(new ArraysHaveSameValuesExpectation(array($r2id, $r1id, $funnyid)), array_keys(get_switchable_roles($context))); - } - - function test_context_cache() { - // Create cache, empty - $cache = new context_cache(); - $this->assertEqual(0, $cache->get_approx_count()); - - // Put context in cache - $context = (object)array('id'=>37,'contextlevel'=>50,'instanceid'=>13); - $cache->add($context); - $this->assertEqual(1, $cache->get_approx_count()); - - // Get context out of cache - $this->assertEqual($context, $cache->get(50, 13)); - $this->assertEqual($context, $cache->get_by_id(37)); - - // Try to get context that isn't there - $this->assertEqual(false, $cache->get(50, 99)); - $this->assertEqual(false, $cache->get(99, 13)); - $this->assertEqual(false, $cache->get_by_id(99)); - - // Remove context from cache - $cache->remove($context); - $this->assertEqual(0, $cache->get_approx_count()); - $this->assertEqual(false, $cache->get(50, 13)); - $this->assertEqual(false, $cache->get_by_id(37)); - - // Add a stack of contexts to cache - for($i=0; $i$i, 'contextlevel'=>50, - 'instanceid'=>$i+1); - $cache->add($context); - } - $this->assertEqual(context_cache::MAX_SIZE, $cache->get_approx_count()); - - // Get a random sample from the middle - $sample = (object)array( - 'id'=>174, 'contextlevel'=>50, 'instanceid'=>175); - $this->assertEqual($sample, $cache->get(50, 175)); - $this->assertEqual($sample, $cache->get_by_id(174)); - - // Now add one more; should result in size being reduced - $context = (object)array('id'=>99999, 'contextlevel'=>50, - 'instanceid'=>99999); - $cache->add($context); - $this->assertEqual(context_cache::MAX_SIZE - context_cache::REDUCE_SIZE + 1, - $cache->get_approx_count()); - - // Check that the first ones are no longer there - $this->assertEqual(false, $cache->get(50, 175)); - $this->assertEqual(false, $cache->get_by_id(174)); - - // Check that the last ones are still there - $bigid = context_cache::MAX_SIZE - 10; - $sample = (object)array( - 'id'=>$bigid, 'contextlevel'=>50, 'instanceid'=>$bigid+1); - $this->assertEqual($sample, $cache->get(50, $bigid+1)); - $this->assertEqual($sample, $cache->get_by_id($bigid)); - - // Reset the cache - $cache->reset(); - $this->assertEqual(0, $cache->get_approx_count()); - $this->assertEqual(false, $cache->get(50, $bigid+1)); - } -} - diff --git a/lib/upgradelib.php b/lib/upgradelib.php index 8ea6abd93ba3a..edf8f0b382321 100644 --- a/lib/upgradelib.php +++ b/lib/upgradelib.php @@ -1417,11 +1417,11 @@ function upgrade_core($version, $verbose) { purge_all_caches(); // Clean up contexts - more and more stuff depends on existence of paths and contexts - cleanup_contexts(); - create_contexts(); - build_context_path(); - $syscontext = get_context_instance(CONTEXT_SYSTEM); - mark_context_dirty($syscontext->path); + context_helper::cleanup_instances(); + context_helper::create_instances(null, false); + context_helper::build_all_paths(false); + $syscontext = context_system::instance(); + $syscontext->mark_dirty(); print_upgrade_part_end('moodle', false, $verbose); } catch (Exception $ex) { diff --git a/login/token.php b/login/token.php index ef1a510171545..6ca05fa64f49d 100644 --- a/login/token.php +++ b/login/token.php @@ -56,6 +56,9 @@ } } + // let enrol plugins deal with new enrolments if necessary + enrol_check_plugins($user); + // setup user session to check capability session_set_user($user); diff --git a/rss/file.php b/rss/file.php index 7d2f9f5da2632..2b9c9906c9f38 100644 --- a/rss/file.php +++ b/rss/file.php @@ -16,9 +16,9 @@ /** * rss/file.php - entry point to serve rss streams - * + * * This script simply checks the parameters to construct a $USER - * then finds and calls a function in the relevant component to + * then finds and calls a function in the relevant component to * actually check security and create the RSS stream * * @package moodlecore @@ -81,7 +81,7 @@ if (!isset($modinfo->instances[$componentname])) { $modinfo->instances[$componentname] = array(); } - + foreach ($modinfo->instances[$componentname] as $modinstanceid=>$cm) { if ($modinstanceid==$instanceid) { $context = get_context_instance(CONTEXT_MODULE, $cm->id); @@ -122,6 +122,10 @@ } $user = get_complete_user_data('id', $userid); + +// let enrol plugins deal with new enrolments if necessary +enrol_check_plugins($user); + session_set_user($user); //for login and capability checks // Check the context actually exists diff --git a/webservice/lib.php b/webservice/lib.php index 53f7f596cb0cc..84c0927caae34 100644 --- a/webservice/lib.php +++ b/webservice/lib.php @@ -654,6 +654,7 @@ protected function authenticate_user() { } // now fake user login, the session is completely empty too + enrol_check_plugins($user); session_set_user($user); $this->userid = $user->id; diff --git a/webservice/upload.php b/webservice/upload.php index 9886b64947ef3..874b03ef1aec1 100644 --- a/webservice/upload.php +++ b/webservice/upload.php @@ -66,6 +66,8 @@ // log token access $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id)); +// let enrol plugins deal with new enrolments if necessary +enrol_check_plugins($user); session_set_user($user); $context = get_context_instance(CONTEXT_USER, $USER->id); require_capability('moodle/user:manageownfiles', $context); From 9ef8d17ab236db88f44f24c840b303c3893c5bb4 Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Sun, 16 Oct 2011 17:12:39 +0200 Subject: [PATCH 38/67] MDL-29785 import latest TinyMCE 3.4.6 using https://github.com/moodle/custom-tinymce/tree/MOODLE_22_3.4.6 and https://github.com/moodle/custom-tinymce_spellchecker_php/tree/MOODLE_22_2.0.6 customised branches --- lib/editor/tinymce/lib.php | 2 +- lib/editor/tinymce/readme_moodle.txt | 3 +- .../3.4.5/plugins/layer/editor_plugin.js | 1 - .../3.4.5/plugins/media/editor_plugin.js | 1 - .../3.4.5/plugins/paste/editor_plugin.js | 1 - lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce.js | 1 - .../tinymce/tiny_mce/3.4.5/tiny_mce_jquery.js | 1 - .../tiny_mce/3.4.5/tiny_mce_prototype.js | 1 - .../{3.4.5 => 3.4.6}/jquery.tinymce.js | 0 .../tiny_mce/{3.4.5 => 3.4.6}/langs/en.js | 2 +- .../tiny_mce/{3.4.5 => 3.4.6}/license.txt | 0 .../plugins/advhr/css/advhr.css | 0 .../plugins/advhr/editor_plugin.js | 0 .../plugins/advhr/editor_plugin_src.js | 0 .../{3.4.5 => 3.4.6}/plugins/advhr/js/rule.js | 0 .../plugins/advhr/langs/en_dlg.js | 0 .../{3.4.5 => 3.4.6}/plugins/advhr/rule.htm | 0 .../plugins/advimage/css/advimage.css | 0 .../plugins/advimage/editor_plugin.js | 0 .../plugins/advimage/editor_plugin_src.js | 0 .../plugins/advimage/image.htm | 0 .../plugins/advimage/img/sample.gif | Bin .../plugins/advimage/js/image.js | 0 .../plugins/advimage/langs/en_dlg.js | 0 .../plugins/advlink/css/advlink.css | 0 .../plugins/advlink/editor_plugin.js | 0 .../plugins/advlink/editor_plugin_src.js | 0 .../plugins/advlink/js/advlink.js | 0 .../plugins/advlink/langs/en_dlg.js | 0 .../{3.4.5 => 3.4.6}/plugins/advlink/link.htm | 0 .../plugins/advlist/editor_plugin.js | 0 .../plugins/advlist/editor_plugin_src.js | 0 .../plugins/autolink/editor_plugin.js | 0 .../plugins/autolink/editor_plugin_src.js | 0 .../plugins/autoresize/editor_plugin.js | 0 .../plugins/autoresize/editor_plugin_src.js | 0 .../plugins/autosave/editor_plugin.js | 0 .../plugins/autosave/editor_plugin_src.js | 0 .../plugins/autosave/langs/en.js | 0 .../plugins/bbcode/editor_plugin.js | 0 .../plugins/bbcode/editor_plugin_src.js | 0 .../plugins/contextmenu/editor_plugin.js | 0 .../plugins/contextmenu/editor_plugin_src.js | 0 .../plugins/directionality/editor_plugin.js | 0 .../directionality/editor_plugin_src.js | 0 .../plugins/dragmath/dragmath.php | 0 .../plugins/dragmath/editor_plugin.js | 0 .../plugins/dragmath/editor_plugin_src.js | 0 .../plugins/dragmath/img/dragmath.gif | Bin .../plugins/dragmath/js/dragmath.js | 0 .../plugins/dragmath/langs/en_dlg.js | 0 .../plugins/emotions/editor_plugin.js | 0 .../plugins/emotions/editor_plugin_src.js | 0 .../plugins/emotions/emotions.htm | 0 .../plugins/emotions/img/smiley-cool.gif | Bin .../plugins/emotions/img/smiley-cry.gif | Bin .../emotions/img/smiley-embarassed.gif | Bin .../emotions/img/smiley-foot-in-mouth.gif | Bin .../plugins/emotions/img/smiley-frown.gif | Bin .../plugins/emotions/img/smiley-innocent.gif | Bin .../plugins/emotions/img/smiley-kiss.gif | Bin .../plugins/emotions/img/smiley-laughing.gif | Bin .../emotions/img/smiley-money-mouth.gif | Bin .../plugins/emotions/img/smiley-sealed.gif | Bin .../plugins/emotions/img/smiley-smile.gif | Bin .../plugins/emotions/img/smiley-surprised.gif | Bin .../emotions/img/smiley-tongue-out.gif | Bin .../plugins/emotions/img/smiley-undecided.gif | Bin .../plugins/emotions/img/smiley-wink.gif | Bin .../plugins/emotions/img/smiley-yell.gif | Bin .../plugins/emotions/js/emotions.js | 0 .../plugins/emotions/langs/en_dlg.js | 0 .../plugins/example/dialog.htm | 0 .../plugins/example/editor_plugin.js | 0 .../plugins/example/editor_plugin_src.js | 0 .../plugins/example/img/example.gif | Bin .../plugins/example/js/dialog.js | 0 .../plugins/example/langs/en.js | 0 .../plugins/example/langs/en_dlg.js | 0 .../example_dependency/editor_plugin.js | 0 .../example_dependency/editor_plugin_src.js | 0 .../plugins/fullpage/css/fullpage.css | 0 .../plugins/fullpage/editor_plugin.js | 0 .../plugins/fullpage/editor_plugin_src.js | 0 .../plugins/fullpage/fullpage.htm | 0 .../plugins/fullpage/js/fullpage.js | 0 .../plugins/fullpage/langs/en_dlg.js | 0 .../plugins/fullscreen/editor_plugin.js | 0 .../plugins/fullscreen/editor_plugin_src.js | 0 .../plugins/fullscreen/fullscreen.htm | 0 .../plugins/iespell/editor_plugin.js | 0 .../plugins/iespell/editor_plugin_src.js | 0 .../plugins/inlinepopups/editor_plugin.js | 0 .../plugins/inlinepopups/editor_plugin_src.js | 0 .../skins/clearlooks2/img/alert.gif | Bin .../skins/clearlooks2/img/button.gif | Bin .../skins/clearlooks2/img/buttons.gif | Bin .../skins/clearlooks2/img/confirm.gif | Bin .../skins/clearlooks2/img/corners.gif | Bin .../skins/clearlooks2/img/horizontal.gif | Bin .../skins/clearlooks2/img/vertical.gif | Bin .../inlinepopups/skins/clearlooks2/window.css | 0 .../plugins/inlinepopups/template.htm | 0 .../plugins/insertdatetime/editor_plugin.js | 0 .../insertdatetime/editor_plugin_src.js | 0 .../3.4.6/plugins/layer/editor_plugin.js | 1 + .../plugins/layer/editor_plugin_src.js | 58 +++++++- .../plugins/legacyoutput/editor_plugin.js | 0 .../plugins/legacyoutput/editor_plugin_src.js | 0 .../plugins/lists/editor_plugin.js | 0 .../plugins/lists/editor_plugin_src.js | 0 .../plugins/media/css/media.css | 0 .../3.4.6/plugins/media/editor_plugin.js | 1 + .../plugins/media/editor_plugin_src.js | 84 ++++++++++- .../plugins/media/js/embed.js | 0 .../plugins/media/js/media.js | 76 +++++++++- .../plugins/media/langs/en_dlg.js | 2 +- .../{3.4.5 => 3.4.6}/plugins/media/media.htm | 49 +++++-- .../plugins/media/moxieplayer.swf | Bin .../plugins/moodleemoticon/dialog.php | 0 .../plugins/moodleemoticon/editor_plugin.js | 0 .../moodleemoticon/editor_plugin_src.js | 0 .../moodleemoticon/img/moodleemoticon.gif | Bin .../plugins/moodleemoticon/js/dialog.js | 0 .../plugins/moodlemedia/css/media.css | 0 .../plugins/moodlemedia/editor_plugin.js | 0 .../plugins/moodlemedia/editor_plugin_src.js | 0 .../plugins/moodlemedia/img/icon.gif | Bin .../plugins/moodlemedia/js/media.js | 0 .../plugins/moodlemedia/moodlemedia.htm | 0 .../plugins/moodlenolink/editor_plugin.js | 0 .../plugins/moodlenolink/editor_plugin_src.js | 0 .../plugins/moodlenolink/img/ed_nolink.gif | Bin .../plugins/moodlenolink/langs/en.js | 0 .../plugins/nonbreaking/editor_plugin.js | 0 .../plugins/nonbreaking/editor_plugin_src.js | 0 .../plugins/noneditable/editor_plugin.js | 0 .../plugins/noneditable/editor_plugin_src.js | 0 .../plugins/pagebreak/editor_plugin.js | 0 .../plugins/pagebreak/editor_plugin_src.js | 0 .../3.4.6/plugins/paste/editor_plugin.js | 1 + .../plugins/paste/editor_plugin_src.js | 12 +- .../plugins/paste/js/pastetext.js | 0 .../plugins/paste/js/pasteword.js | 0 .../plugins/paste/langs/en_dlg.js | 0 .../plugins/paste/pastetext.htm | 0 .../plugins/paste/pasteword.htm | 0 .../plugins/preview/editor_plugin.js | 0 .../plugins/preview/editor_plugin_src.js | 0 .../plugins/preview/example.html | 0 .../plugins/preview/jscripts/embed.js | 0 .../plugins/preview/preview.html | 0 .../plugins/print/editor_plugin.js | 0 .../plugins/print/editor_plugin_src.js | 0 .../plugins/save/editor_plugin.js | 0 .../plugins/save/editor_plugin_src.js | 0 .../searchreplace/css/searchreplace.css | 0 .../plugins/searchreplace/editor_plugin.js | 0 .../searchreplace/editor_plugin_src.js | 0 .../plugins/searchreplace/js/searchreplace.js | 0 .../plugins/searchreplace/langs/en_dlg.js | 0 .../plugins/searchreplace/searchreplace.htm | 0 .../plugins/spellchecker/changelog.txt | 3 +- .../spellchecker/classes/EnchantSpell.php | 0 .../spellchecker/classes/GoogleSpell.php | 0 .../plugins/spellchecker/classes/PSpell.php | 0 .../spellchecker/classes/PSpellShell.php | 0 .../spellchecker/classes/SpellChecker.php | 0 .../spellchecker/classes/utils/JSON.php | 0 .../spellchecker/classes/utils/Logger.php | 0 .../plugins/spellchecker/config.php | 0 .../plugins/spellchecker/css/content.css | 0 .../plugins/spellchecker/editor_plugin.js | 2 +- .../plugins/spellchecker/editor_plugin_src.js | 69 ++++----- .../plugins/spellchecker/img/wline.gif | Bin .../plugins/spellchecker/includes/general.php | 2 +- .../plugins/spellchecker/rpc.php | 0 .../plugins/style/css/props.css | 0 .../plugins/style/editor_plugin.js | 0 .../plugins/style/editor_plugin_src.js | 0 .../plugins/style/js/props.js | 0 .../plugins/style/langs/en_dlg.js | 0 .../{3.4.5 => 3.4.6}/plugins/style/props.htm | 0 .../plugins/tabfocus/editor_plugin.js | 0 .../plugins/tabfocus/editor_plugin_src.js | 0 .../{3.4.5 => 3.4.6}/plugins/table/cell.htm | 0 .../plugins/table/css/cell.css | 0 .../plugins/table/css/row.css | 0 .../plugins/table/css/table.css | 0 .../plugins/table/editor_plugin.js | 0 .../plugins/table/editor_plugin_src.js | 0 .../{3.4.5 => 3.4.6}/plugins/table/js/cell.js | 0 .../plugins/table/js/merge_cells.js | 0 .../{3.4.5 => 3.4.6}/plugins/table/js/row.js | 0 .../plugins/table/js/table.js | 0 .../plugins/table/langs/en_dlg.js | 0 .../plugins/table/merge_cells.htm | 0 .../{3.4.5 => 3.4.6}/plugins/table/row.htm | 0 .../{3.4.5 => 3.4.6}/plugins/table/table.htm | 0 .../plugins/template/blank.htm | 0 .../plugins/template/css/template.css | 0 .../plugins/template/editor_plugin.js | 0 .../plugins/template/editor_plugin_src.js | 0 .../plugins/template/js/template.js | 0 .../plugins/template/langs/en_dlg.js | 0 .../plugins/template/template.htm | 0 .../plugins/visualchars/editor_plugin.js | 0 .../plugins/visualchars/editor_plugin_src.js | 0 .../plugins/wordcount/editor_plugin.js | 0 .../plugins/wordcount/editor_plugin_src.js | 0 .../plugins/xhtmlxtras/abbr.htm | 0 .../plugins/xhtmlxtras/acronym.htm | 0 .../plugins/xhtmlxtras/attributes.htm | 0 .../plugins/xhtmlxtras/cite.htm | 0 .../plugins/xhtmlxtras/css/attributes.css | 0 .../plugins/xhtmlxtras/css/popup.css | 0 .../plugins/xhtmlxtras/del.htm | 0 .../plugins/xhtmlxtras/editor_plugin.js | 0 .../plugins/xhtmlxtras/editor_plugin_src.js | 0 .../plugins/xhtmlxtras/ins.htm | 0 .../plugins/xhtmlxtras/js/abbr.js | 0 .../plugins/xhtmlxtras/js/acronym.js | 0 .../plugins/xhtmlxtras/js/attributes.js | 0 .../plugins/xhtmlxtras/js/cite.js | 0 .../plugins/xhtmlxtras/js/del.js | 0 .../plugins/xhtmlxtras/js/element_common.js | 0 .../plugins/xhtmlxtras/js/ins.js | 0 .../plugins/xhtmlxtras/langs/en_dlg.js | 0 .../themes/advanced/about.htm | 0 .../themes/advanced/anchor.htm | 0 .../themes/advanced/charmap.htm | 0 .../themes/advanced/color_picker.htm | 0 .../themes/advanced/editor_template.js | 0 .../themes/advanced/editor_template_src.js | 0 .../themes/advanced/image.htm | 0 .../themes/advanced/img/colorpicker.jpg | Bin .../themes/advanced/img/flash.gif | Bin .../themes/advanced/img/icons.gif | Bin .../themes/advanced/img/iframe.gif | Bin .../themes/advanced/img/pagebreak.gif | Bin .../themes/advanced/img/quicktime.gif | Bin .../themes/advanced/img/realmedia.gif | Bin .../themes/advanced/img/shockwave.gif | Bin .../themes/advanced/img/trans.gif | Bin .../themes/advanced/img/video.gif | Bin .../themes/advanced/img/windowsmedia.gif | Bin .../themes/advanced/js/about.js | 0 .../themes/advanced/js/anchor.js | 5 +- .../themes/advanced/js/charmap.js | 0 .../themes/advanced/js/color_picker.js | 0 .../themes/advanced/js/image.js | 0 .../themes/advanced/js/link.js | 0 .../themes/advanced/js/source_editor.js | 0 .../themes/advanced/langs/en.js | 0 .../themes/advanced/langs/en_dlg.js | 0 .../{3.4.5 => 3.4.6}/themes/advanced/link.htm | 0 .../themes/advanced/shortcuts.htm | 0 .../themes/advanced/skins/default/content.css | 3 +- .../themes/advanced/skins/default/dialog.css | 0 .../advanced/skins/default/img/buttons.png | Bin .../advanced/skins/default/img/items.gif | Bin .../advanced/skins/default/img/menu_arrow.gif | Bin .../advanced/skins/default/img/menu_check.gif | Bin .../advanced/skins/default/img/progress.gif | Bin .../advanced/skins/default/img/tabs.gif | Bin .../themes/advanced/skins/default/ui.css | 0 .../advanced/skins/highcontrast/content.css | 2 +- .../advanced/skins/highcontrast/dialog.css | 0 .../themes/advanced/skins/highcontrast/ui.css | 0 .../themes/advanced/skins/o2k7/content.css | 2 +- .../themes/advanced/skins/o2k7/dialog.css | 0 .../advanced/skins/o2k7/img/button_bg.png | Bin .../skins/o2k7/img/button_bg_black.png | Bin .../skins/o2k7/img/button_bg_silver.png | Bin .../themes/advanced/skins/o2k7/ui.css | 0 .../themes/advanced/skins/o2k7/ui_black.css | 0 .../themes/advanced/skins/o2k7/ui_silver.css | 0 .../themes/advanced/source_editor.htm | 0 .../themes/simple/editor_template.js | 0 .../themes/simple/editor_template_src.js | 0 .../themes/simple/img/icons.gif | Bin .../themes/simple/langs/en.js | 0 .../themes/simple/skins/default/content.css | 0 .../themes/simple/skins/default/ui.css | 0 .../themes/simple/skins/o2k7/content.css | 0 .../simple/skins/o2k7/img/button_bg.png | Bin .../themes/simple/skins/o2k7/ui.css | 0 lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce.js | 1 + .../tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_dev.js | 0 .../tinymce/tiny_mce/3.4.6/tiny_mce_jquery.js | 1 + .../{3.4.5 => 3.4.6}/tiny_mce_jquery_src.js | 137 +++++++++++------- .../{3.4.5 => 3.4.6}/tiny_mce_popup.js | 0 .../{3.4.5 => 3.4.6}/tiny_mce_popup_src.js | 0 .../tiny_mce/3.4.6/tiny_mce_prototype.js | 1 + .../tiny_mce_prototype_src.js | 137 +++++++++++------- .../tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_src.js | 137 +++++++++++------- .../utils/editable_selects.js | 0 .../{3.4.5 => 3.4.6}/utils/form_utils.js | 0 .../tiny_mce/{3.4.5 => 3.4.6}/utils/mctabs.js | 0 .../{3.4.5 => 3.4.6}/utils/validate.js | 0 lib/editor/tinymce/version.php | 4 +- lib/thirdpartylibs.xml | 2 +- 302 files changed, 568 insertions(+), 237 deletions(-) delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin.js delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/editor_plugin.js delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/editor_plugin.js delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce.js delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery.js delete mode 100644 lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/jquery.tinymce.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/langs/en.js (98%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/license.txt (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/css/advhr.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/js/rule.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advhr/rule.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/css/advimage.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/image.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/img/sample.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/js/image.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advimage/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/css/advlink.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/js/advlink.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlink/link.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlist/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/advlist/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autolink/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autolink/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autoresize/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autoresize/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autosave/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autosave/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/autosave/langs/en.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/bbcode/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/bbcode/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/contextmenu/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/contextmenu/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/directionality/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/directionality/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/dragmath.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/img/dragmath.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/js/dragmath.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/dragmath/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/emotions.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-cool.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-cry.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-embarassed.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-foot-in-mouth.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-frown.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-innocent.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-kiss.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-laughing.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-money-mouth.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-sealed.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-smile.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-surprised.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-tongue-out.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-undecided.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-wink.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/img/smiley-yell.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/js/emotions.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/emotions/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/dialog.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/img/example.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/js/dialog.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/langs/en.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example_dependency/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/example_dependency/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/css/fullpage.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/fullpage.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/js/fullpage.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullpage/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullscreen/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullscreen/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/fullscreen/fullscreen.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/iespell/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/iespell/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/alert.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/button.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/corners.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/skins/clearlooks2/window.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/inlinepopups/template.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/insertdatetime/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/insertdatetime/editor_plugin_src.js (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/layer/editor_plugin_src.js (75%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/legacyoutput/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/legacyoutput/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/lists/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/lists/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/css/media.css (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/editor_plugin.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/editor_plugin_src.js (91%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/js/embed.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/js/media.js (85%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/langs/en_dlg.js (95%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/media.htm (96%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/media/moxieplayer.swf (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodleemoticon/dialog.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodleemoticon/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodleemoticon/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodleemoticon/img/moodleemoticon.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodleemoticon/js/dialog.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/css/media.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/img/icon.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/js/media.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlemedia/moodlemedia.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlenolink/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlenolink/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlenolink/img/ed_nolink.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/moodlenolink/langs/en.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/nonbreaking/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/nonbreaking/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/noneditable/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/noneditable/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/pagebreak/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/pagebreak/editor_plugin_src.js (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/editor_plugin_src.js (98%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/js/pastetext.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/js/pasteword.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/pastetext.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/paste/pasteword.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/preview/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/preview/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/preview/example.html (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/preview/jscripts/embed.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/preview/preview.html (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/print/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/print/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/save/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/save/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/css/searchreplace.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/js/searchreplace.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/searchreplace/searchreplace.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/changelog.txt (95%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/EnchantSpell.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/GoogleSpell.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/PSpell.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/PSpellShell.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/SpellChecker.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/utils/JSON.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/classes/utils/Logger.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/config.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/css/content.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/editor_plugin.js (51%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/editor_plugin_src.js (89%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/img/wline.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/includes/general.php (96%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/spellchecker/rpc.php (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/css/props.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/js/props.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/style/props.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/tabfocus/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/tabfocus/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/cell.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/css/cell.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/css/row.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/css/table.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/js/cell.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/js/merge_cells.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/js/row.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/js/table.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/merge_cells.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/row.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/table/table.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/blank.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/css/template.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/js/template.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/template/template.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/visualchars/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/visualchars/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/wordcount/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/wordcount/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/abbr.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/acronym.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/attributes.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/cite.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/css/attributes.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/css/popup.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/del.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/editor_plugin.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/editor_plugin_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/ins.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/abbr.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/acronym.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/attributes.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/cite.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/del.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/element_common.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/js/ins.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/plugins/xhtmlxtras/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/about.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/anchor.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/charmap.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/color_picker.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/editor_template.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/editor_template_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/image.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/colorpicker.jpg (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/flash.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/icons.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/iframe.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/pagebreak.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/quicktime.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/realmedia.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/shockwave.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/trans.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/video.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/img/windowsmedia.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/about.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/anchor.js (94%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/charmap.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/color_picker.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/image.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/link.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/js/source_editor.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/langs/en.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/langs/en_dlg.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/link.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/shortcuts.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/content.css (95%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/dialog.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/buttons.png (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/items.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/menu_arrow.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/menu_check.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/progress.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/img/tabs.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/default/ui.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/highcontrast/content.css (95%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/highcontrast/dialog.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/highcontrast/ui.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/content.css (97%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/dialog.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/img/button_bg.png (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/img/button_bg_black.png (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/img/button_bg_silver.png (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/ui.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/ui_black.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/skins/o2k7/ui_silver.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/advanced/source_editor.htm (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/editor_template.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/editor_template_src.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/img/icons.gif (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/langs/en.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/skins/default/content.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/skins/default/ui.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/skins/o2k7/content.css (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/skins/o2k7/img/button_bg.png (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/themes/simple/skins/o2k7/ui.css (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_dev.js (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_jquery_src.js (99%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_popup.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_popup_src.js (100%) create mode 100644 lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype.js rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_prototype_src.js (99%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/tiny_mce_src.js (99%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/utils/editable_selects.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/utils/form_utils.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/utils/mctabs.js (100%) rename lib/editor/tinymce/tiny_mce/{3.4.5 => 3.4.6}/utils/validate.js (100%) diff --git a/lib/editor/tinymce/lib.php b/lib/editor/tinymce/lib.php index 3f51eaed05963..473251e0e82a4 100644 --- a/lib/editor/tinymce/lib.php +++ b/lib/editor/tinymce/lib.php @@ -28,7 +28,7 @@ class tinymce_texteditor extends texteditor { /** @var string active version - directory name */ - public $version = '3.4.5'; + public $version = '3.4.6'; public function supported_by_browser() { if (check_browser_version('MSIE', 6)) { diff --git a/lib/editor/tinymce/readme_moodle.txt b/lib/editor/tinymce/readme_moodle.txt index 2963ddad49ac8..7d0f83f131f09 100644 --- a/lib/editor/tinymce/readme_moodle.txt +++ b/lib/editor/tinymce/readme_moodle.txt @@ -1,4 +1,4 @@ -Description of TinyMCE v3.4.5 library integration in Moodle +Description of TinyMCE v3.4.6 library integration in Moodle ========================================================================================= Copyright: (c) 2004-2011, Moxiecode Systems AB, All rights reserved. @@ -41,6 +41,7 @@ AMOS maintainer / custom-tinymce maintainer / integrators about that. ========================================================================================= Added: * plugins/gragmath/* + * plugins/moodlemotions/* * plugins/moodlenolink/* * plugins/moodlemedia/* diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin.js deleted file mode 100644 index d610f7e9de894..0000000000000 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin.js +++ /dev/null @@ -1 +0,0 @@ -(function(){tinymce.create("tinymce.plugins.Layer",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceInsertLayer",c._insertLayer,c);a.addCommand("mceMoveForward",function(){c._move(1)});a.addCommand("mceMoveBackward",function(){c._move(-1)});a.addCommand("mceMakeAbsolute",function(){c._toggleAbsolute()});a.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"});a.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"});a.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"});a.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"});a.onInit.add(function(){if(tinymce.isIE){a.getDoc().execCommand("2D-Position",false,true)}});a.onNodeChange.add(c._nodeChange,c);a.onVisualAid.add(c._visualAid,c)},getInfo:function(){return{longname:"Layer",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/layer",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,e){var c,d;c=this._getParentLayer(e);d=b.dom.getParent(e,"DIV,P,IMG");if(!d){a.setDisabled("absolute",1);a.setDisabled("moveforward",1);a.setDisabled("movebackward",1)}else{a.setDisabled("absolute",0);a.setDisabled("moveforward",!c);a.setDisabled("movebackward",!c);a.setActive("absolute",c&&c.style.position.toLowerCase()=="absolute")}},_visualAid:function(a,c,b){var d=a.dom;tinymce.each(d.select("div,p",c),function(f){if(/^(absolute|relative|static)$/i.test(f.style.position)){if(b){d.addClass(f,"mceItemVisualAid")}else{d.removeClass(f,"mceItemVisualAid")}}})},_move:function(h){var b=this.editor,f,g=[],e=this._getParentLayer(b.selection.getNode()),c=-1,j=-1,a;a=[];tinymce.walk(b.getBody(),function(d){if(d.nodeType==1&&/^(absolute|relative|static)$/i.test(d.style.position)){a.push(d)}},"childNodes");for(f=0;f-1){a[c].style.zIndex=g[j];a[j].style.zIndex=g[c]}else{if(g[c]>0){a[c].style.zIndex=g[c]-1}}}else{for(f=0;fg[c]){j=f;break}}if(j>-1){a[c].style.zIndex=g[j];a[j].style.zIndex=g[c]}else{a[c].style.zIndex=g[c]+1}}b.execCommand("mceRepaint")},_getParentLayer:function(a){return this.editor.dom.getParent(a,function(b){return b.nodeType==1&&/^(absolute|relative|static)$/i.test(b.style.position)})},_insertLayer:function(){var a=this.editor,b=a.dom.getPos(a.dom.getParent(a.selection.getNode(),"*"));a.dom.add(a.getBody(),"div",{style:{position:"absolute",left:b.x,top:(b.y>20?b.y:20),width:100,height:100},"class":"mceItemVisualAid"},a.selection.getContent()||a.getLang("layer.content"))},_toggleAbsolute:function(){var a=this.editor,b=this._getParentLayer(a.selection.getNode());if(!b){b=a.dom.getParent(a.selection.getNode(),"DIV,P,IMG")}if(b){if(b.style.position.toLowerCase()=="absolute"){a.dom.setStyles(b,{position:"",left:"",top:"",width:"",height:""});a.dom.removeClass(b,"mceItemVisualAid")}else{if(b.style.left==""){b.style.left=20+"px"}if(b.style.top==""){b.style.top=20+"px"}if(b.style.width==""){b.style.width=b.width?(b.width+"px"):"100px"}if(b.style.height==""){b.style.height=b.height?(b.height+"px"):"100px"}b.style.position="absolute";a.dom.setAttrib(b,"data-mce-style","");a.addVisual(a.getBody())}a.execCommand("mceRepaint");a.nodeChanged()}}});tinymce.PluginManager.add("layer",tinymce.plugins.Layer)})(); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/editor_plugin.js deleted file mode 100644 index f89959e2e2583..0000000000000 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/editor_plugin.js +++ /dev/null @@ -1 +0,0 @@ -(function(){var d=tinymce.explode("id,name,width,height,style,align,class,hspace,vspace,bgcolor,type"),h=tinymce.makeMap(d.join(",")),b=tinymce.html.Node,f,a,g=tinymce.util.JSON,e;f=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["Audio"]];function c(m){var l,j,k;if(m&&!m.splice){j=[];for(k=0;true;k++){if(m[k]){j[k]=m[k]}else{break}}return j}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(n,j){var r=this,l={},m,p,q,k;function o(i){return i&&i.nodeName==="IMG"&&n.dom.hasClass(i,"mceItemMedia")}r.editor=n;r.url=j;a="";for(m=0;m0){M+=(M?"&":"")+N+"="+escape(O)}});if(M.length){E.params.flashvars=M}J=o.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(J,function(O,N){E.params[N]=""+O})}}E=x.attr("data-mce-json");if(!E){return}E=g.parse(E);p=this.getType(x.attr("class"));z=x.attr("data-mce-style");if(!z){z=x.attr("style");if(z){z=o.dom.serializeStyle(o.dom.parseStyle(z,"img"))}}if(p.name==="Iframe"){v=new b("iframe",1);tinymce.each(d,function(i){var H=x.attr(i);if(i=="class"&&H){H=H.replace(/mceItem.+ ?/g,"")}if(H&&H.length>0){v.attr(i,H)}});for(G in E.params){v.attr(G,E.params[G])}v.attr({style:z,src:E.params.src});x.replace(v);return}if(this.editor.settings.media_use_script){v=new b("script",1).attr("type","text/javascript");w=new b("#text",3);w.value="write"+p.name+"("+g.serialize(tinymce.extend(E.params,{width:x.attr("width"),height:x.attr("height")}))+");";v.append(w);x.replace(v);return}if(p.name==="Video"&&E.video.sources[0]){A=new b("video",1).attr(tinymce.extend({id:x.attr("id"),width:x.attr("width"),height:x.attr("height"),style:z},E.video.attrs));if(E.video.attrs){l=E.video.attrs.poster}k=E.video.sources=c(E.video.sources);for(y=0;y")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="

    "+o.encode(r).replace(/\r?\n\r?\n/g,"

    ").replace(/\r?\n/g,"
    ")+"

    "}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9){d([[/(?:
     [\s\r\n]+|
    )*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:
     [\s\r\n]+|
    )*/g,"$1"]]);d([[/

    /g,"

    "],[/
    /g," "],[/

    /g,"
    "]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*( )+/gi,/( |]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/

    ]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"

    $1

    ")}if(b(k,"paste_convert_middot_lists")){d([[//gi,"$&__MCE_ITEM__"],[/(]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([//gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/ /gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/"/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/]*>/gi,"

    "],[/<\/h[1-6][^>]*>/gi,"

    "]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/ /g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.( |\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*( |\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.( |\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(g){var d=this.editor,e=b(d,"paste_text_linebreaktype"),i=b(d,"paste_text_replacements"),f=tinymce.is;function h(j){c(j,function(k){if(k.constructor==RegExp){g=g.replace(k,"")}else{g=g.replace(k[0],k[1])}})}if((typeof(g)==="string")&&(g.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(g)){h([/[\n\r]+/g])}else{h([/\r+/g])}h([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/ /gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"],[/\n{3,}/g,"\n\n"]]);g=d.dom.decode(tinymce.html.Entities.encodeRaw(g));if(f(i,"array")){h(i)}else{if(f(i,"string")){h(new RegExp(i,"gi"))}}if(e=="none"){h([[/\n+/g," "]])}else{if(e=="br"){h([[/\n/g,"
    "]])}else{h([[/\n\n/g,"

    "],[/^(.*<\/p>)(

    )$/,"

    $1"],[/\n/g,"
    "]])}}d.execCommand("mceInsertContent",false,g)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})(); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce.js b/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce.js deleted file mode 100644 index 5749c8642ddf9..0000000000000 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce.js +++ /dev/null @@ -1 +0,0 @@ -(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(d){var f=d.VK,e=f.BACKSPACE,c=f.DELETE;function b(g){var i=g.dom,h=g.selection;g.onKeyDown.add(function(k,o){var j,p,m,n,l;l=o.keyCode==c;if(l||o.keyCode==e){o.preventDefault();j=h.getRng();p=i.getParent(j.startContainer,i.isBlock);if(l){p=i.getNext(p,i.isBlock)}if(p){m=p.firstChild;if(m&&m.nodeName==="SPAN"){n=m.cloneNode(false)}}k.getDoc().execCommand(l?"ForwardDelete":"Delete",false,null);p=i.getParent(j.startContainer,i.isBlock);d.each(i.select("span.Apple-style-span,font.Apple-style-span",p),function(r){var q=i.createRng();q.setStartBefore(r);q.setEndBefore(r);if(n){i.replace(n.cloneNode(false),r,true)}else{i.remove(r,true)}h.setRng(q)})}})}function a(g){g.onKeyUp.add(function(h,j){var i=j.keyCode;if(i==c||i==e){if(h.dom.isEmpty(h.getBody())){h.setContent("",{format:"raw"});h.nodeChanged();return}}})}d.create("tinymce.util.Quirks",{Quirks:function(g){if(d.isWebKit){b(g);a(g)}if(d.isIE){a(g)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},select:function(l,k){var j=this;return h.dom.Sizzle(l,j.get(k)||j.get(j.settings.root_element)||j.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(a.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return h.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,j=0,d=Object.prototype.toString,o=false,i=true;[0,0].sort(function(){i=false;return 0});var b=function(v,e,z,A){z=z||[];e=e||document;var C=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!v||typeof v!=="string"){return z}var x=[],s,E,H,r,u=true,t=b.isXML(e),B=v,D,G,F,y;do{p.exec("");s=p.exec(B);if(s){B=s[3];x.push(s[1]);if(s[2]){r=s[3];break}}}while(s);if(x.length>1&&k.exec(v)){if(x.length===2&&f.relative[x[0]]){E=h(x[0]+x[1],e)}else{E=f.relative[x[0]]?[e]:b(x.shift(),e);while(x.length){v=x.shift();if(f.relative[v]){v+=x.shift()}E=h(v,E)}}}else{if(!A&&x.length>1&&e.nodeType===9&&!t&&f.match.ID.test(x[0])&&!f.match.ID.test(x[x.length-1])){D=b.find(x.shift(),e,t);e=D.expr?b.filter(D.expr,D.set)[0]:D.set[0]}if(e){D=A?{expr:x.pop(),set:a(A)}:b.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&e.parentNode?e.parentNode:e,t);E=D.expr?b.filter(D.expr,D.set):D.set;if(x.length>0){H=a(E)}else{u=false}while(x.length){G=x.pop();F=G;if(!f.relative[G]){G=""}else{F=x.pop()}if(F==null){F=e}f.relative[G](H,F,t)}}else{H=x=[]}}if(!H){H=E}if(!H){b.error(G||v)}if(d.call(H)==="[object Array]"){if(!u){z.push.apply(z,H)}else{if(e&&e.nodeType===1){for(y=0;H[y]!=null;y++){if(H[y]&&(H[y]===true||H[y].nodeType===1&&b.contains(e,H[y]))){z.push(E[y])}}}else{for(y=0;H[y]!=null;y++){if(H[y]&&H[y].nodeType===1){z.push(E[y])}}}}}else{a(H,z)}if(r){b(r,C,z,A);b.uniqueSort(z)}return z};b.uniqueSort=function(r){if(c){o=i;r.sort(c);if(o){for(var e=1;e":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){return r[1].toLowerCase()},CHILD:function(e){if(e[1]==="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=j++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if((p.exec(u[3])||"").length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return re[3]-0},nth:function(s,r,e){return e[3]-0===r},eq:function(s,r,e){return e[3]-0===r}},filter:{PSEUDO:function(s,y,x,z){var e=y[1],r=f.filters[e];if(r){return r(s,x,y,z)}else{if(e==="contains"){return(s.textContent||s.innerText||b.getText([s])||"").indexOf(y[3])>=0}else{if(e==="not"){var t=y[3];for(var v=0,u=t.length;v=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName.toLowerCase()===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!==r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var k=f.match.POS,g=function(r,e){return"\\"+(e-0+1)};for(var m in f.match){f.match[m]=new RegExp(f.match[m].source+(/(?![^\[]*\])(?![^\(]*\))/.source));f.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+f.match[m].source.replace(/\\(\d+)/g,g))}var a=function(r,e){r=Array.prototype.slice.call(r,0);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(l){a=function(u,t){var r=t||[],s=0;if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var e=u.length;s";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r);e=r=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="

    ";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!b.isXML(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}s=null})()}(function(){var e=document.createElement("div");e.innerHTML="
    ";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}};e=null})();function n(r,x,v,A,y,z){for(var t=0,s=A.length;t0){u=e;break}}}e=e[r]}A[t]=u}}}b.contains=document.compareDocumentPosition?function(r,e){return !!(r.compareDocumentPosition(e)&16)}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};b.isXML=function(e){var r=(e?e.ownerDocument||e:0).documentElement;return r?r.nodeName!=="HTML":false};var h=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var g=this,h,i;if(f!=g.selectedIndex){h=c.get(g.id+"_text");i=g.items[f];if(i){g.selectedValue=i.value;g.selectedIndex=f;c.setHTML(h,c.encode(i.title));c.removeClass(h,"mceTitle");c.setAttrib(g.id,"aria-valuenow",i.title)}else{c.setHTML(h,c.encode(g.settings.title));c.addClass(h,"mceTitle");g.selectedValue=g.selectedIndex=null;c.setAttrib(g.id,"aria-valuenow",g.settings.title)}h=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='';i+="";i+="";i+="";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{id:f.id,role:"presentation",tabindex:"0","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("span",{role:"button","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){this.keyNav.focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){if(a&&!Range.prototype.getClientRects){q.onMouseDown.add(function(t,z){if(z.target.nodeName==="HTML"){var s=q.getBody();s.blur();setTimeout(function(){s.focus()},0)}})}y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onClick.add(function(s,t){t=t.target;if(m.isWebKit&&t.nodeName=="IMG"){B.selection.getSel().setBaseAndExtent(t,0,t,1)}if(t.nodeName=="A"&&q.hasClass(t,"mceItemAnchor")){B.selection.select(t)}B.nodeChanged()});B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery.js b/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery.js deleted file mode 100644 index 7c004ff378a97..0000000000000 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery.js +++ /dev/null @@ -1 +0,0 @@ -(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(d){var f=d.VK,e=f.BACKSPACE,c=f.DELETE;function b(g){var i=g.dom,h=g.selection;g.onKeyDown.add(function(k,o){var j,p,m,n,l;l=o.keyCode==c;if(l||o.keyCode==e){o.preventDefault();j=h.getRng();p=i.getParent(j.startContainer,i.isBlock);if(l){p=i.getNext(p,i.isBlock)}if(p){m=p.firstChild;if(m&&m.nodeName==="SPAN"){n=m.cloneNode(false)}}k.getDoc().execCommand(l?"ForwardDelete":"Delete",false,null);p=i.getParent(j.startContainer,i.isBlock);d.each(i.select("span.Apple-style-span,font.Apple-style-span",p),function(r){var q=i.createRng();q.setStartBefore(r);q.setEndBefore(r);if(n){i.replace(n.cloneNode(false),r,true)}else{i.remove(r,true)}h.setRng(q)})}})}function a(g){g.onKeyUp.add(function(h,j){var i=j.keyCode;if(i==c||i==e){if(h.dom.isEmpty(h.getBody())){h.setContent("",{format:"raw"});h.nodeChanged();return}}})}d.create("tinymce.util.Quirks",{Quirks:function(g){if(d.isWebKit){b(g);a(g)}if(d.isIE){a(g)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(d){var f=d.each,c=d.DOM,b=d.isIE,e=d.isWebKit,a;d.create("tinymce.dom.EventUtils",{EventUtils:function(){this.inits=[];this.events=[]},add:function(m,p,l,j){var g,h=this,i=h.events,k;if(p instanceof Array){k=[];f(p,function(o){k.push(h.add(m,o,l,j))});return k}if(m&&m.hasOwnProperty&&m instanceof Array){k=[];f(m,function(n){n=c.get(n);k.push(h.add(n,p,l,j))});return k}m=c.get(m);if(!m){return}g=function(n){if(h.disabled){return}n=n||window.event;if(n&&b){if(!n.target){n.target=n.srcElement}d.extend(n,h._stoppers)}if(!j){return l(n)}return l.call(j,n)};if(p=="unload"){d.unloads.unshift({func:g});return g}if(p=="init"){if(h.domLoaded){g()}else{h.inits.push(g)}return g}i.push({obj:m,name:p,func:l,cfunc:g,scope:j});h._add(m,p,g);return l},remove:function(l,m,k){var h=this,g=h.events,i=false,j;if(l&&l.hasOwnProperty&&l instanceof Array){j=[];f(l,function(n){n=c.get(n);j.push(h.remove(n,m,k))});return j}l=c.get(l);f(g,function(o,n){if(o.obj==l&&o.name==m&&(!k||(o.func==k||o.cfunc==k))){g.splice(n,1);h._remove(l,m,o.cfunc);i=true;return false}});return i},clear:function(l){var j=this,g=j.events,h,k;if(l){l=c.get(l);for(h=g.length-1;h>=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var g=this,h,i;if(f!=g.selectedIndex){h=c.get(g.id+"_text");i=g.items[f];if(i){g.selectedValue=i.value;g.selectedIndex=f;c.setHTML(h,c.encode(i.title));c.removeClass(h,"mceTitle");c.setAttrib(g.id,"aria-valuenow",i.title)}else{c.setHTML(h,c.encode(g.settings.title));c.addClass(h,"mceTitle");g.selectedValue=g.selectedIndex=null;c.setAttrib(g.id,"aria-valuenow",g.settings.title)}h=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='';i+="";i+="";i+="";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{id:f.id,role:"presentation",tabindex:"0","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("span",{role:"button","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){this.keyNav.focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);if(j.adapter){j.adapter.patchEditor(m)}return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){if(a&&!Range.prototype.getClientRects){q.onMouseDown.add(function(t,z){if(z.target.nodeName==="HTML"){var s=q.getBody();s.blur();setTimeout(function(){s.focus()},0)}})}y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onClick.add(function(s,t){t=t.target;if(m.isWebKit&&t.nodeName=="IMG"){B.selection.getSel().setBaseAndExtent(t,0,t,1)}if(t.nodeName=="A"&&q.hasClass(t,"mceItemAnchor")){B.selection.select(t)}B.nodeChanged()});B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype.js b/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype.js deleted file mode 100644 index c61dc3f7c957b..0000000000000 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype.js +++ /dev/null @@ -1 +0,0 @@ -(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(d){var f=d.VK,e=f.BACKSPACE,c=f.DELETE;function b(g){var i=g.dom,h=g.selection;g.onKeyDown.add(function(k,o){var j,p,m,n,l;l=o.keyCode==c;if(l||o.keyCode==e){o.preventDefault();j=h.getRng();p=i.getParent(j.startContainer,i.isBlock);if(l){p=i.getNext(p,i.isBlock)}if(p){m=p.firstChild;if(m&&m.nodeName==="SPAN"){n=m.cloneNode(false)}}k.getDoc().execCommand(l?"ForwardDelete":"Delete",false,null);p=i.getParent(j.startContainer,i.isBlock);d.each(i.select("span.Apple-style-span,font.Apple-style-span",p),function(r){var q=i.createRng();q.setStartBefore(r);q.setEndBefore(r);if(n){i.replace(n.cloneNode(false),r,true)}else{i.remove(r,true)}h.setRng(q)})}})}function a(g){g.onKeyUp.add(function(h,j){var i=j.keyCode;if(i==c||i==e){if(h.dom.isEmpty(h.getBody())){h.setContent("",{format:"raw"});h.nodeChanged();return}}})}d.create("tinymce.util.Quirks",{Quirks:function(g){if(d.isWebKit){b(g);a(g)}if(d.isIE){a(g)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},select:function(l,k){var j=this;return h.dom.Sizzle(l,j.get(k)||j.get(j.settings.root_element)||j.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(a.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return h.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,j=0,d=Object.prototype.toString,o=false,i=true;[0,0].sort(function(){i=false;return 0});var b=function(v,e,z,A){z=z||[];e=e||document;var C=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!v||typeof v!=="string"){return z}var x=[],s,E,H,r,u=true,t=b.isXML(e),B=v,D,G,F,y;do{p.exec("");s=p.exec(B);if(s){B=s[3];x.push(s[1]);if(s[2]){r=s[3];break}}}while(s);if(x.length>1&&k.exec(v)){if(x.length===2&&f.relative[x[0]]){E=h(x[0]+x[1],e)}else{E=f.relative[x[0]]?[e]:b(x.shift(),e);while(x.length){v=x.shift();if(f.relative[v]){v+=x.shift()}E=h(v,E)}}}else{if(!A&&x.length>1&&e.nodeType===9&&!t&&f.match.ID.test(x[0])&&!f.match.ID.test(x[x.length-1])){D=b.find(x.shift(),e,t);e=D.expr?b.filter(D.expr,D.set)[0]:D.set[0]}if(e){D=A?{expr:x.pop(),set:a(A)}:b.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&e.parentNode?e.parentNode:e,t);E=D.expr?b.filter(D.expr,D.set):D.set;if(x.length>0){H=a(E)}else{u=false}while(x.length){G=x.pop();F=G;if(!f.relative[G]){G=""}else{F=x.pop()}if(F==null){F=e}f.relative[G](H,F,t)}}else{H=x=[]}}if(!H){H=E}if(!H){b.error(G||v)}if(d.call(H)==="[object Array]"){if(!u){z.push.apply(z,H)}else{if(e&&e.nodeType===1){for(y=0;H[y]!=null;y++){if(H[y]&&(H[y]===true||H[y].nodeType===1&&b.contains(e,H[y]))){z.push(E[y])}}}else{for(y=0;H[y]!=null;y++){if(H[y]&&H[y].nodeType===1){z.push(E[y])}}}}}else{a(H,z)}if(r){b(r,C,z,A);b.uniqueSort(z)}return z};b.uniqueSort=function(r){if(c){o=i;r.sort(c);if(o){for(var e=1;e":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){return r[1].toLowerCase()},CHILD:function(e){if(e[1]==="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=j++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if((p.exec(u[3])||"").length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return re[3]-0},nth:function(s,r,e){return e[3]-0===r},eq:function(s,r,e){return e[3]-0===r}},filter:{PSEUDO:function(s,y,x,z){var e=y[1],r=f.filters[e];if(r){return r(s,x,y,z)}else{if(e==="contains"){return(s.textContent||s.innerText||b.getText([s])||"").indexOf(y[3])>=0}else{if(e==="not"){var t=y[3];for(var v=0,u=t.length;v=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName.toLowerCase()===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!==r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var k=f.match.POS,g=function(r,e){return"\\"+(e-0+1)};for(var m in f.match){f.match[m]=new RegExp(f.match[m].source+(/(?![^\[]*\])(?![^\(]*\))/.source));f.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+f.match[m].source.replace(/\\(\d+)/g,g))}var a=function(r,e){r=Array.prototype.slice.call(r,0);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(l){a=function(u,t){var r=t||[],s=0;if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var e=u.length;s";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r);e=r=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="

    ";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!b.isXML(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}s=null})()}(function(){var e=document.createElement("div");e.innerHTML="
    ";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}};e=null})();function n(r,x,v,A,y,z){for(var t=0,s=A.length;t0){u=e;break}}}e=e[r]}A[t]=u}}}b.contains=document.compareDocumentPosition?function(r,e){return !!(r.compareDocumentPosition(e)&16)}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};b.isXML=function(e){var r=(e?e.ownerDocument||e:0).documentElement;return r?r.nodeName!=="HTML":false};var h=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var g=this,h,i;if(f!=g.selectedIndex){h=c.get(g.id+"_text");i=g.items[f];if(i){g.selectedValue=i.value;g.selectedIndex=f;c.setHTML(h,c.encode(i.title));c.removeClass(h,"mceTitle");c.setAttrib(g.id,"aria-valuenow",i.title)}else{c.setHTML(h,c.encode(g.settings.title));c.addClass(h,"mceTitle");g.selectedValue=g.selectedIndex=null;c.setAttrib(g.id,"aria-valuenow",g.settings.title)}h=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='';i+="";i+="";i+="";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{id:f.id,role:"presentation",tabindex:"0","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("span",{role:"button","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){this.keyNav.focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){if(a&&!Range.prototype.getClientRects){q.onMouseDown.add(function(t,z){if(z.target.nodeName==="HTML"){var s=q.getBody();s.blur();setTimeout(function(){s.focus()},0)}})}y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onClick.add(function(s,t){t=t.target;if(m.isWebKit&&t.nodeName=="IMG"){B.selection.getSel().setBaseAndExtent(t,0,t,1)}if(t.nodeName=="A"&&q.hasClass(t,"mceItemAnchor")){B.selection.select(t)}B.nodeChanged()});B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/jquery.tinymce.js b/lib/editor/tinymce/tiny_mce/3.4.6/jquery.tinymce.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/jquery.tinymce.js rename to lib/editor/tinymce/tiny_mce/3.4.6/jquery.tinymce.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/langs/en.js similarity index 98% rename from lib/editor/tinymce/tiny_mce/3.4.5/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/langs/en.js index 0b8ebc0c25e5e..6379b0d9584d3 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/langs/en.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/langs/en.js @@ -1 +1 @@ -tinyMCE.addI18n({en:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode_stick":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words"}}}); \ No newline at end of file +tinyMCE.addI18n({en:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode_stick":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words:"}}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/license.txt b/lib/editor/tinymce/tiny_mce/3.4.6/license.txt similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/license.txt rename to lib/editor/tinymce/tiny_mce/3.4.6/license.txt diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/css/advhr.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/css/advhr.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/css/advhr.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/css/advhr.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/js/rule.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/js/rule.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/js/rule.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/js/rule.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/rule.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/rule.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advhr/rule.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advhr/rule.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/css/advimage.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/css/advimage.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/css/advimage.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/css/advimage.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/image.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/image.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/image.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/image.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/img/sample.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/img/sample.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/img/sample.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/img/sample.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/js/image.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/js/image.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/js/image.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/js/image.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advimage/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advimage/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/css/advlink.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/css/advlink.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/css/advlink.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/css/advlink.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/js/advlink.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/js/advlink.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/js/advlink.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/js/advlink.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/link.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/link.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlink/link.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlink/link.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlist/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlist/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlist/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlist/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlist/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlist/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/advlist/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/advlist/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autolink/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autolink/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autolink/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autolink/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autolink/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autolink/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autolink/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autolink/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autoresize/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autoresize/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autoresize/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autoresize/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autoresize/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autoresize/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autoresize/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autoresize/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/langs/en.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/autosave/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/autosave/langs/en.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/bbcode/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/bbcode/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/bbcode/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/bbcode/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/bbcode/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/bbcode/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/bbcode/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/bbcode/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/contextmenu/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/contextmenu/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/contextmenu/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/contextmenu/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/contextmenu/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/contextmenu/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/contextmenu/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/contextmenu/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/directionality/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/directionality/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/directionality/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/directionality/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/directionality/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/directionality/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/directionality/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/directionality/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/dragmath.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/dragmath.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/dragmath.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/dragmath.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/img/dragmath.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/img/dragmath.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/img/dragmath.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/img/dragmath.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/js/dragmath.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/js/dragmath.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/js/dragmath.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/js/dragmath.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/dragmath/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/dragmath/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/emotions.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/emotions.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/emotions.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/emotions.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-cool.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-cool.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-cool.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-cool.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-cry.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-cry.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-cry.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-cry.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-embarassed.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-embarassed.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-embarassed.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-embarassed.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-foot-in-mouth.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-foot-in-mouth.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-foot-in-mouth.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-foot-in-mouth.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-frown.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-frown.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-frown.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-frown.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-innocent.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-innocent.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-innocent.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-innocent.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-kiss.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-kiss.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-kiss.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-kiss.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-laughing.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-laughing.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-laughing.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-laughing.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-money-mouth.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-money-mouth.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-money-mouth.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-money-mouth.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-sealed.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-sealed.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-sealed.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-sealed.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-smile.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-smile.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-smile.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-smile.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-surprised.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-surprised.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-surprised.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-surprised.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-tongue-out.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-tongue-out.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-tongue-out.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-tongue-out.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-undecided.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-undecided.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-undecided.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-undecided.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-wink.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-wink.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-wink.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-wink.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-yell.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-yell.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/img/smiley-yell.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/img/smiley-yell.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/js/emotions.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/js/emotions.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/js/emotions.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/js/emotions.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/emotions/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/emotions/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/dialog.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/dialog.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/dialog.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/dialog.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/img/example.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/img/example.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/img/example.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/img/example.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/js/dialog.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/js/dialog.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/js/dialog.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/js/dialog.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/langs/en.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/langs/en.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example_dependency/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example_dependency/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example_dependency/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example_dependency/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/example_dependency/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/example_dependency/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/example_dependency/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/example_dependency/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/css/fullpage.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/css/fullpage.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/css/fullpage.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/css/fullpage.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/fullpage.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/fullpage.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/fullpage.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/fullpage.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/js/fullpage.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/js/fullpage.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/js/fullpage.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/js/fullpage.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullpage/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullpage/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/fullscreen.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/fullscreen.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/fullscreen/fullscreen.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/fullscreen/fullscreen.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/iespell/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/iespell/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/iespell/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/iespell/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/iespell/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/iespell/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/iespell/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/iespell/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/alert.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/alert.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/alert.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/alert.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/button.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/button.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/button.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/button.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/buttons.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/confirm.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/corners.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/corners.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/corners.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/corners.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/horizontal.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/img/vertical.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/window.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/window.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/skins/clearlooks2/window.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/skins/clearlooks2/window.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/template.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/template.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/inlinepopups/template.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/inlinepopups/template.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/insertdatetime/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/insertdatetime/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/insertdatetime/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/insertdatetime/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/insertdatetime/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/insertdatetime/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/insertdatetime/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/insertdatetime/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin.js new file mode 100644 index 0000000000000..ca3857a74eec3 --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin.js @@ -0,0 +1 @@ +(function(){function a(b){do{if(b.className&&b.className.indexOf("mceItemLayer")!=-1){return b}}while(b=b.parentNode)}tinymce.create("tinymce.plugins.Layer",{init:function(b,c){var d=this;d.editor=b;b.addCommand("mceInsertLayer",d._insertLayer,d);b.addCommand("mceMoveForward",function(){d._move(1)});b.addCommand("mceMoveBackward",function(){d._move(-1)});b.addCommand("mceMakeAbsolute",function(){d._toggleAbsolute()});b.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"});b.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"});b.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"});b.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"});b.onInit.add(function(){var e=b.dom;if(tinymce.isIE){b.getDoc().execCommand("2D-Position",false,true)}});b.onMouseUp.add(function(f,h){var g=a(h.target);if(g){f.dom.setAttrib(g,"data-mce-style","")}});b.onMouseDown.add(function(f,j){var h=j.target,i=f.getDoc(),g;if(tinymce.isGecko){if(a(h)){if(i.designMode!=="on"){i.designMode="on";h=i.body;g=h.parentNode;g.removeChild(h);g.appendChild(h)}}else{if(i.designMode=="on"){i.designMode="off"}}}});b.onNodeChange.add(d._nodeChange,d);b.onVisualAid.add(d._visualAid,d)},getInfo:function(){return{longname:"Layer",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/layer",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(c,b,f){var d,e;d=this._getParentLayer(f);e=c.dom.getParent(f,"DIV,P,IMG");if(!e){b.setDisabled("absolute",1);b.setDisabled("moveforward",1);b.setDisabled("movebackward",1)}else{b.setDisabled("absolute",0);b.setDisabled("moveforward",!d);b.setDisabled("movebackward",!d);b.setActive("absolute",d&&d.style.position.toLowerCase()=="absolute")}},_visualAid:function(b,d,c){var f=b.dom;tinymce.each(f.select("div,p",d),function(g){if(/^(absolute|relative|fixed)$/i.test(g.style.position)){if(c){f.addClass(g,"mceItemVisualAid")}else{f.removeClass(g,"mceItemVisualAid")}f.addClass(g,"mceItemLayer")}})},_move:function(j){var c=this.editor,g,h=[],f=this._getParentLayer(c.selection.getNode()),e=-1,k=-1,b;b=[];tinymce.walk(c.getBody(),function(d){if(d.nodeType==1&&/^(absolute|relative|static)$/i.test(d.style.position)){b.push(d)}},"childNodes");for(g=0;g-1){b[e].style.zIndex=h[k];b[k].style.zIndex=h[e]}else{if(h[e]>0){b[e].style.zIndex=h[e]-1}}}else{for(g=0;gh[e]){k=g;break}}if(k>-1){b[e].style.zIndex=h[k];b[k].style.zIndex=h[e]}else{b[e].style.zIndex=h[e]+1}}c.execCommand("mceRepaint")},_getParentLayer:function(b){return this.editor.dom.getParent(b,function(c){return c.nodeType==1&&/^(absolute|relative|static)$/i.test(c.style.position)})},_insertLayer:function(){var c=this.editor,e=c.dom,d=e.getPos(e.getParent(c.selection.getNode(),"*")),b=c.getBody();c.dom.add(b,"div",{style:{position:"absolute",left:d.x,top:(d.y>20?d.y:20),width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},c.selection.getContent()||c.getLang("layer.content"));if(tinymce.isIE){e.setHTML(b,b.innerHTML)}},_toggleAbsolute:function(){var b=this.editor,c=this._getParentLayer(b.selection.getNode());if(!c){c=b.dom.getParent(b.selection.getNode(),"DIV,P,IMG")}if(c){if(c.style.position.toLowerCase()=="absolute"){b.dom.setStyles(c,{position:"",left:"",top:"",width:"",height:""});b.dom.removeClass(c,"mceItemVisualAid");b.dom.removeClass(c,"mceItemLayer")}else{if(c.style.left==""){c.style.left=20+"px"}if(c.style.top==""){c.style.top=20+"px"}if(c.style.width==""){c.style.width=c.width?(c.width+"px"):"100px"}if(c.style.height==""){c.style.height=c.height?(c.height+"px"):"100px"}c.style.position="absolute";b.dom.setAttrib(c,"data-mce-style","");b.addVisual(b.getBody())}b.execCommand("mceRepaint");b.nodeChanged()}}});tinymce.PluginManager.add("layer",tinymce.plugins.Layer)})(); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin_src.js similarity index 75% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin_src.js index ddf24c427253d..d31978bf60893 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/layer/editor_plugin_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/layer/editor_plugin_src.js @@ -9,6 +9,14 @@ */ (function() { + function findParentLayer(node) { + do { + if (node.className && node.className.indexOf('mceItemLayer') != -1) { + return node; + } + } while (node = node.parentNode); + }; + tinymce.create('tinymce.plugins.Layer', { init : function(ed, url) { var t = this; @@ -37,10 +45,43 @@ ed.addButton('insertlayer', {title : 'layer.insertlayer_desc', cmd : 'mceInsertLayer'}); ed.onInit.add(function() { + var dom = ed.dom; + if (tinymce.isIE) ed.getDoc().execCommand('2D-Position', false, true); }); + // Remove serialized styles when selecting a layer since it might be changed by a drag operation + ed.onMouseUp.add(function(ed, e) { + var layer = findParentLayer(e.target); + + if (layer) { + ed.dom.setAttrib(layer, 'data-mce-style', ''); + } + }); + + // Fixes edit focus issues with layers on Gecko + // This will enable designMode while inside a layer and disable it when outside + ed.onMouseDown.add(function(ed, e) { + var node = e.target, doc = ed.getDoc(), parent; + + if (tinymce.isGecko) { + if (findParentLayer(node)) { + if (doc.designMode !== 'on') { + doc.designMode = 'on'; + + // Repaint caret + node = doc.body; + parent = node.parentNode; + parent.removeChild(node); + parent.appendChild(node); + } + } else if (doc.designMode == 'on') { + doc.designMode = 'off'; + } + } + }); + ed.onNodeChange.add(t._nodeChange, t); ed.onVisualAid.add(t._visualAid, t); }, @@ -81,11 +122,13 @@ var dom = ed.dom; tinymce.each(dom.select('div,p', e), function(e) { - if (/^(absolute|relative|static)$/i.test(e.style.position)) { + if (/^(absolute|relative|fixed)$/i.test(e.style.position)) { if (s) dom.addClass(e, 'mceItemVisualAid'); else - dom.removeClass(e, 'mceItemVisualAid'); + dom.removeClass(e, 'mceItemVisualAid'); + + dom.addClass(e, 'mceItemLayer'); } }); }, @@ -153,9 +196,9 @@ }, _insertLayer : function() { - var ed = this.editor, p = ed.dom.getPos(ed.dom.getParent(ed.selection.getNode(), '*')); + var ed = this.editor, dom = ed.dom, p = dom.getPos(dom.getParent(ed.selection.getNode(), '*')), body = ed.getBody(); - ed.dom.add(ed.getBody(), 'div', { + ed.dom.add(body, 'div', { style : { position : 'absolute', left : p.x, @@ -163,8 +206,12 @@ width : 100, height : 100 }, - 'class' : 'mceItemVisualAid' + 'class' : 'mceItemVisualAid mceItemLayer' }, ed.selection.getContent() || ed.getLang('layer.content')); + + // Workaround for IE where it messes up the JS engine if you insert a layer on IE 6,7 + if (tinymce.isIE) + dom.setHTML(body, body.innerHTML); }, _toggleAbsolute : function() { @@ -184,6 +231,7 @@ }); ed.dom.removeClass(le, 'mceItemVisualAid'); + ed.dom.removeClass(le, 'mceItemLayer'); } else { if (le.style.left == "") le.style.left = 20 + 'px'; diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/legacyoutput/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/legacyoutput/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/legacyoutput/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/legacyoutput/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/legacyoutput/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/legacyoutput/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/legacyoutput/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/legacyoutput/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/lists/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/lists/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/lists/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/lists/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/lists/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/lists/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/lists/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/lists/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/css/media.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/css/media.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/css/media.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/css/media.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/editor_plugin.js new file mode 100644 index 0000000000000..37b4320bd9faa --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/editor_plugin.js @@ -0,0 +1 @@ +(function(){var d=tinymce.explode("id,name,width,height,style,align,class,hspace,vspace,bgcolor,type"),h=tinymce.makeMap(d.join(",")),b=tinymce.html.Node,f,a,g=tinymce.util.JSON,e;f=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["EmbeddedAudio"],["Audio"]];function c(m){var l,j,k;if(m&&!m.splice){j=[];for(k=0;true;k++){if(m[k]){j[k]=m[k]}else{break}}return j}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(n,j){var r=this,l={},m,p,q,k;function o(i){return i&&i.nodeName==="IMG"&&n.dom.hasClass(i,"mceItemMedia")}r.editor=n;r.url=j;a="";for(m=0;m0){N+=(N?"&":"")+O+"="+escape(P)}});if(N.length){G.params.flashvars=N}K=p.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(K,function(P,O){G.params[O]=""+P})}}G=z.attr("data-mce-json");if(!G){return}G=g.parse(G);q=this.getType(z.attr("class"));B=z.attr("data-mce-style");if(!B){B=z.attr("style");if(B){B=p.dom.serializeStyle(p.dom.parseStyle(B,"img"))}}if(q.name==="Iframe"){x=new b("iframe",1);tinymce.each(d,function(i){var n=z.attr(i);if(i=="class"&&n){n=n.replace(/mceItem.+ ?/g,"")}if(n&&n.length>0){x.attr(i,n)}});for(I in G.params){x.attr(I,G.params[I])}x.attr({style:B,src:G.params.src});z.replace(x);return}if(this.editor.settings.media_use_script){x=new b("script",1).attr("type","text/javascript");y=new b("#text",3);y.value="write"+q.name+"("+g.serialize(tinymce.extend(G.params,{width:z.attr("width"),height:z.attr("height")}))+");";x.append(y);z.replace(x);return}if(q.name==="Video"&&G.video.sources[0]){C=new b("video",1).attr(tinymce.extend({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);for(A=0;A'; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + + if (editor.getParam('media_embedded_audio', false)) { + html += ''; + } + + html += ''; + return html; } }; @@ -386,4 +450,4 @@ tinyMCEPopup.onInit.add(function() { Media.init(); }); -})(); \ No newline at end of file +})(); diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/langs/en_dlg.js similarity index 95% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/langs/en_dlg.js index efbc94c43f4da..49412596a10f0 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/langs/en_dlg.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/langs/en_dlg.js @@ -1 +1 @@ -tinyMCE.addI18n('en.media_dlg',{list:"List",file:"File/URL",advanced:"Advanced",general:"General",title:"Insert/Edit Embedded Media","align_top_left":"Top Left","align_center":"Center","align_left":"Left","align_bottom":"Bottom","align_right":"Right","align_top":"Top","qt_stream_warn":"Streamed RTSP resources should be added to the QT Source field under the Advanced tab.\nYou should also add a non-streamed version to the Source field.",qtsrc:"QT Source",progress:"Progress",sound:"Sound",swstretchvalign:"Stretch V-Align",swstretchhalign:"Stretch H-Align",swstretchstyle:"Stretch Style",scriptcallbacks:"Script Callbacks","align_top_right":"Top Right",uimode:"UI Mode",rate:"Rate",playcount:"Play Count",defaultframe:"Default Frame",currentposition:"Current Position",currentmarker:"Current Marker",captioningid:"Captioning ID",baseurl:"Base URL",balance:"Balance",windowlessvideo:"Windowless Video",stretchtofit:"Stretch to Fit",mute:"Mute",invokeurls:"Invoke URLs",fullscreen:"Full Screen",enabled:"Enabled",autostart:"Auto Start",volume:"Volume",target:"Target",qtsrcchokespeed:"Choke Speed",href:"HREF",endtime:"End Time",starttime:"Start Time",enablejavascript:"Enable JavaScript",correction:"No Correction",targetcache:"Target Cache",playeveryframe:"Play Every Frame",kioskmode:"Kiosk Mode",controller:"Controller",menu:"Show Menu",loop:"Loop",play:"Auto Play",hspace:"H-Space",vspace:"V-Space","class_name":"Class",name:"Name",id:"ID",type:"Type",size:"Dimensions",preview:"Preview","constrain_proportions":"Constrain Proportions",controls:"Controls",numloop:"Num Loops",console:"Console",cache:"Cache",autohref:"Auto HREF",liveconnect:"SWLiveConnect",flashvars:"Flash Vars",base:"Base",bgcolor:"Background",wmode:"WMode",salign:"SAlign",align:"Align",scale:"Scale",quality:"Quality",shuffle:"Shuffle",prefetch:"Prefetch",nojava:"No Java",maintainaspect:"Maintain Aspect",imagestatus:"Image Status",center:"Center",autogotourl:"Auto Goto URL","shockwave_options":"Shockwave Options","rmp_options":"Real Media Player Options","wmp_options":"Windows Media Player Options","qt_options":"QuickTime Options","flash_options":"Flash Options",hidden:"Hidden","align_bottom_left":"Bottom Left","align_bottom_right":"Bottom Right","html5_video_options":"HTML5 Video Options",altsource1:"Alternative source 1",altsource2:"Alternative source 2",preload:"Preload",poster:"Poster",source:"Source","html5_audio_options":"Audio Options","preload_none":"Don\'t Preload","preload_metadata":"Preload video metadata","preload_auto":"Let user\'s browser decide"}); \ No newline at end of file +tinyMCE.addI18n('en.media_dlg',{list:"List",file:"File/URL",advanced:"Advanced",general:"General",title:"Insert/Edit Embedded Media","align_top_left":"Top Left","align_center":"Center","align_left":"Left","align_bottom":"Bottom","align_right":"Right","align_top":"Top","qt_stream_warn":"Streamed RTSP resources should be added to the QT Source field under the Advanced tab.\nYou should also add a non-streamed version to the Source field.",qtsrc:"QT Source",progress:"Progress",sound:"Sound",swstretchvalign:"Stretch V-Align",swstretchhalign:"Stretch H-Align",swstretchstyle:"Stretch Style",scriptcallbacks:"Script Callbacks","align_top_right":"Top Right",uimode:"UI Mode",rate:"Rate",playcount:"Play Count",defaultframe:"Default Frame",currentposition:"Current Position",currentmarker:"Current Marker",captioningid:"Captioning ID",baseurl:"Base URL",balance:"Balance",windowlessvideo:"Windowless Video",stretchtofit:"Stretch to Fit",mute:"Mute",invokeurls:"Invoke URLs",fullscreen:"Full Screen",enabled:"Enabled",autostart:"Auto Start",volume:"Volume",target:"Target",qtsrcchokespeed:"Choke Speed",href:"HREF",endtime:"End Time",starttime:"Start Time",enablejavascript:"Enable JavaScript",correction:"No Correction",targetcache:"Target Cache",playeveryframe:"Play Every Frame",kioskmode:"Kiosk Mode",controller:"Controller",menu:"Show Menu",loop:"Loop",play:"Auto Play",hspace:"H-Space",vspace:"V-Space","class_name":"Class",name:"Name",id:"ID",type:"Type",size:"Dimensions",preview:"Preview","constrain_proportions":"Constrain Proportions",controls:"Controls",numloop:"Num Loops",console:"Console",cache:"Cache",autohref:"Auto HREF",liveconnect:"SWLiveConnect",flashvars:"Flash Vars",base:"Base",bgcolor:"Background",wmode:"WMode",salign:"SAlign",align:"Align",scale:"Scale",quality:"Quality",shuffle:"Shuffle",prefetch:"Prefetch",nojava:"No Java",maintainaspect:"Maintain Aspect",imagestatus:"Image Status",center:"Center",autogotourl:"Auto Goto URL","shockwave_options":"Shockwave Options","rmp_options":"Real Media Player Options","wmp_options":"Windows Media Player Options","qt_options":"QuickTime Options","flash_options":"Flash Options",hidden:"Hidden","align_bottom_left":"Bottom Left","align_bottom_right":"Bottom Right","html5_video_options":"HTML5 Video Options",altsource1:"Alternative source 1",altsource2:"Alternative source 2",preload:"Preload",poster:"Poster",source:"Source","html5_audio_options":"Audio Options","preload_none":"Don\'t Preload","preload_metadata":"Preload video metadata","preload_auto":"Let user\'s browser decide", "embedded_audio_options":"Embedded Audio Options"}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/media.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/media.htm similarity index 96% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/media.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/media.htm index 17fb858d9ff34..c77f8b0f3fabe 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/media.htm +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/media.htm @@ -29,16 +29,7 @@ - + @@ -214,6 +205,41 @@ +
    + {#media_dlg.embedded_audio_options} + + + + + + + + + +
    + + + + + +
    +
    + + + + + +
    +
    + + + + + +
    +
    +
    +
    {#media_dlg.html5_audio_options} @@ -246,7 +272,7 @@ @@ -286,7 +312,6 @@
    -
    {#media_dlg.flash_options} diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/moxieplayer.swf b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/moxieplayer.swf similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/media/moxieplayer.swf rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/media/moxieplayer.swf diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/dialog.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/dialog.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/dialog.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/dialog.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/img/moodleemoticon.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/img/moodleemoticon.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/img/moodleemoticon.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/img/moodleemoticon.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/js/dialog.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/js/dialog.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodleemoticon/js/dialog.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodleemoticon/js/dialog.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/css/media.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/css/media.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/css/media.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/css/media.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/img/icon.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/img/icon.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/img/icon.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/img/icon.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/js/media.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/js/media.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/js/media.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/js/media.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/moodlemedia.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/moodlemedia.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlemedia/moodlemedia.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlemedia/moodlemedia.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/img/ed_nolink.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/img/ed_nolink.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/img/ed_nolink.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/img/ed_nolink.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/langs/en.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/moodlenolink/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/moodlenolink/langs/en.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/nonbreaking/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/nonbreaking/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/nonbreaking/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/nonbreaking/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/nonbreaking/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/nonbreaking/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/nonbreaking/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/nonbreaking/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/noneditable/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/noneditable/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/noneditable/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/noneditable/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/noneditable/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/noneditable/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/noneditable/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/noneditable/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/pagebreak/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/pagebreak/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/pagebreak/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/pagebreak/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/pagebreak/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/pagebreak/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/pagebreak/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/pagebreak/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin.js new file mode 100644 index 0000000000000..e47a5c630afdf --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin.js @@ -0,0 +1 @@ +(function(){var c=tinymce.each,a={paste_auto_cleanup_on_paste:true,paste_enable_default_filters:true,paste_block_drop:false,paste_retain_style_properties:"none",paste_strip_class_attributes:"mso",paste_remove_spans:false,paste_remove_styles:false,paste_remove_styles_if_webkit:true,paste_convert_middot_lists:true,paste_convert_headers_to_strong:false,paste_dialog_width:"450",paste_dialog_height:"400",paste_text_use_dialog:false,paste_text_sticky:false,paste_text_sticky_default:false,paste_text_notifyalways:false,paste_text_linebreaktype:"combined",paste_text_replacements:[[/\u2026/g,"..."],[/[\x93\x94\u201c\u201d]/g,'"'],[/[\x60\x91\x92\u2018\u2019]/g,"'"]]};function b(d,e){return d.getParam(e,a[e])}tinymce.create("tinymce.plugins.PastePlugin",{init:function(d,e){var f=this;f.editor=d;f.url=e;f.onPreProcess=new tinymce.util.Dispatcher(f);f.onPostProcess=new tinymce.util.Dispatcher(f);f.onPreProcess.add(f._preProcess);f.onPostProcess.add(f._postProcess);f.onPreProcess.add(function(i,j){d.execCallback("paste_preprocess",i,j)});f.onPostProcess.add(function(i,j){d.execCallback("paste_postprocess",i,j)});d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){return false}});d.pasteAsPlainText=b(d,"paste_text_sticky_default");function h(l,j){var k=d.dom,i;f.onPreProcess.dispatch(f,l);l.node=k.create("div",0,l.content);if(tinymce.isGecko){i=d.selection.getRng(true);if(i.startContainer==i.endContainer&&i.startContainer.nodeType==3){if(l.node.childNodes.length===1&&/^(p|h[1-6]|pre)$/i.test(l.node.firstChild.nodeName)&&l.content.indexOf("__MCE_ITEM__")===-1){k.remove(l.node.firstChild,true)}}}f.onPostProcess.dispatch(f,l);l.content=d.serializer.serialize(l.node,{getInner:1,forced_root_block:""});if((!j)&&(d.pasteAsPlainText)){f._insertPlainText(l.content);if(!b(d,"paste_text_sticky")){d.pasteAsPlainText=false;d.controlManager.setActive("pastetext",false)}}else{f._insert(l.content)}}d.addCommand("mceInsertClipboardContent",function(i,j){h(j,true)});if(!b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(j,i){var k=tinymce.util.Cookie;d.pasteAsPlainText=!d.pasteAsPlainText;d.controlManager.setActive("pastetext",d.pasteAsPlainText);if((d.pasteAsPlainText)&&(!k.get("tinymcePasteText"))){if(b(d,"paste_text_sticky")){d.windowManager.alert(d.translate("paste.plaintext_mode_sticky"))}else{d.windowManager.alert(d.translate("paste.plaintext_mode"))}if(!b(d,"paste_text_notifyalways")){k.set("tinymcePasteText","1",new Date(new Date().getFullYear()+1,12,31))}}})}d.addButton("pastetext",{title:"paste.paste_text_desc",cmd:"mcePasteText"});d.addButton("selectall",{title:"paste.selectall_desc",cmd:"selectall"});function g(s){var l,p,j,t,k=d.selection,o=d.dom,q=d.getBody(),i,r;if(s.clipboardData||o.doc.dataTransfer){r=(s.clipboardData||o.doc.dataTransfer).getData("Text");if(d.pasteAsPlainText){s.preventDefault();h({content:o.encode(r).replace(/\r?\n/g,"
    ")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="

    "+o.encode(r).replace(/\r?\n\r?\n/g,"

    ").replace(/\r?\n/g,"
    ")+"

    "}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9){d([[/(?:
     [\s\r\n]+|
    )*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:
     [\s\r\n]+|
    )*/g,"$1"]]);d([[/

    /g,"

    "],[/
    /g," "],[/

    /g,"
    "]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*( )+/gi,/( |]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/

    ]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"

    $1

    ")}if(b(k,"paste_convert_middot_lists")){d([[//gi,"$&__MCE_ITEM__"],[/(]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([//gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/ /gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/"/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/]*>/gi,"

    "],[/<\/h[1-6][^>]*>/gi,"

    "]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/ /g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.( |\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*( |\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.( |\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(g){var d=this.editor,e=b(d,"paste_text_linebreaktype"),i=b(d,"paste_text_replacements"),f=tinymce.is;function h(j){c(j,function(k){if(k.constructor==RegExp){g=g.replace(k,"")}else{g=g.replace(k[0],k[1])}})}if((typeof(g)==="string")&&(g.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(g)){h([/[\n\r]+/g])}else{h([/\r+/g])}h([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/ /gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"],[/\n{3,}/g,"\n\n"]]);g=d.dom.decode(tinymce.html.Entities.encodeRaw(g));if(f(i,"array")){h(i)}else{if(f(i,"string")){h(new RegExp(i,"gi"))}}if(e=="none"){h([[/\n+/g," "]])}else{if(e=="br"){h([[/\n/g,"
    "]])}else{if(e=="p"){h([[/\n+/g,"

    "],[/^(.*<\/p>)(

    )$/,"

    $1"]])}else{h([[/\n\n/g,"

    "],[/^(.*<\/p>)(

    )$/,"

    $1"],[/\n/g,"
    "]])}}}d.execCommand("mceInsertContent",false,g)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})(); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin_src.js similarity index 98% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin_src.js index 49738f56d0085..cec4abf98727a 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/editor_plugin_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/editor_plugin_src.js @@ -27,7 +27,7 @@ paste_text_sticky : false, paste_text_sticky_default : false, paste_text_notifyalways : false, - paste_text_linebreaktype : "p", + paste_text_linebreaktype : "combined", paste_text_replacements : [ [/\u2026/g, "..."], [/[\x93\x94\u201c\u201d]/g, '"'], @@ -805,14 +805,24 @@ // Treat paragraphs as specified in the config if (linebr == "none") { + // Convert all line breaks to space process([ [/\n+/g, " "] ]); } else if (linebr == "br") { + // Convert all line breaks to
    process([ [/\n/g, "
    "] ]); + } else if (linebr == "p") { + // Convert all line breaks to

    ...

    + process([ + [/\n+/g, "

    "], + [/^(.*<\/p>)(

    )$/, '

    $1'] + ]); } else { + // defaults to "combined" + // Convert single line breaks to
    and double line breaks to

    ...

    process([ [/\n\n/g, "

    "], [/^(.*<\/p>)(

    )$/, '

    $1'], diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/js/pastetext.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/js/pastetext.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/js/pastetext.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/js/pastetext.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/js/pasteword.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/js/pasteword.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/js/pasteword.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/js/pasteword.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/pastetext.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/pastetext.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/pastetext.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/pastetext.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/pasteword.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/pasteword.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/paste/pasteword.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/paste/pasteword.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/example.html b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/example.html similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/example.html rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/example.html diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/jscripts/embed.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/jscripts/embed.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/jscripts/embed.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/jscripts/embed.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/preview.html b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/preview.html similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/preview/preview.html rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/preview/preview.html diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/print/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/print/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/print/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/print/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/print/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/print/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/print/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/print/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/save/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/save/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/save/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/save/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/save/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/save/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/save/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/save/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/css/searchreplace.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/css/searchreplace.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/css/searchreplace.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/css/searchreplace.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/js/searchreplace.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/js/searchreplace.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/js/searchreplace.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/js/searchreplace.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/searchreplace.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/searchreplace.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/searchreplace/searchreplace.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/searchreplace/searchreplace.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/changelog.txt b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/changelog.txt similarity index 95% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/changelog.txt rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/changelog.txt index 4f57326b1f499..f41ec7fdb1ce6 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/changelog.txt +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/changelog.txt @@ -1,4 +1,5 @@ -Version 2.0.6 (2011-??-??) +Version 2.0.6 (2011-09-29) + Fixed incorrect position of suggestion menu. Fixed handling of mispelled words with no suggestions in PSpellShell engine. Fixed PSpellShell command on Windows. Fixed bug where Javascript error is produced when enchant_dict_suggest() returns unexpected result. diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/EnchantSpell.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/EnchantSpell.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/EnchantSpell.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/EnchantSpell.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/GoogleSpell.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/GoogleSpell.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/GoogleSpell.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/GoogleSpell.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/PSpell.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/PSpell.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/PSpell.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/PSpell.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/PSpellShell.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/PSpellShell.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/PSpellShell.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/PSpellShell.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/SpellChecker.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/SpellChecker.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/SpellChecker.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/SpellChecker.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/utils/JSON.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/utils/JSON.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/utils/JSON.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/utils/JSON.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/utils/Logger.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/utils/Logger.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/classes/utils/Logger.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/classes/utils/Logger.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/config.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/config.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/config.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/config.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/css/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/css/content.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/css/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/css/content.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin.js similarity index 51% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin.js index 2cf81c0e7a5ff..71fbb68a64415 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin.js @@ -1 +1 @@ -(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d$2");while((r=o.indexOf(""))!=-1){m=o.substring(0,r);if(m.length){q=document.createTextNode(f.decode(m));p.appendChild(q)}o=o.substring(r+10);r=o.indexOf("");m=o.substring(0,r);o=o.substring(r+11);p.appendChild(f.create("span",{"class":"mceItemHiddenSpellWord"},m))}if(o.length){q=document.createTextNode(f.decode(o));p.appendChild(q)}}else{p.innerHTML=o.replace(e,'$1$2')}f.replace(p,s)}});h.moveToBookmark(i)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=k.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file +(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d$2");while((s=p.indexOf(""))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(f.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(f.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(f.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(e,'$1$2')}f.replace(q,t)}});h.moveToBookmark(i)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}if(h.getParam("show_ignore_words",true)){e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}})}if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin_src.js similarity index 89% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin_src.js index 00368ac02649d..24eec5a0147ef 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/editor_plugin_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/editor_plugin_src.js @@ -221,7 +221,7 @@ }, _markWords : function(wl) { - var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark(), nl = [], + var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [], w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g'); // Collect all text nodes @@ -254,7 +254,7 @@ // Add text node for the content before the word txt = v.substring(0, pos); if (txt.length) { - node = document.createTextNode(dom.decode(txt)); + node = doc.createTextNode(dom.decode(txt)); elem.appendChild(node); } v = v.substring(pos+10); @@ -266,7 +266,7 @@ } // Add text node for the rest of the content if (v.length) { - node = document.createTextNode(dom.decode(v)); + node = doc.createTextNode(dom.decode(v)); elem.appendChild(node); } } else { @@ -314,43 +314,44 @@ } else m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1); - ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", ''); - m.add({ - title : 'spellchecker.ignore_word', - onclick : function() { - var word = wordSpan.innerHTML; + if (ed.getParam('show_ignore_words', true)) { + ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", ''); + m.add({ + title : 'spellchecker.ignore_word', + onclick : function() { + var word = wordSpan.innerHTML; - dom.remove(wordSpan, 1); - t._checkDone(); + dom.remove(wordSpan, 1); + t._checkDone(); - // tell the server if we need to - if (ignoreRpc) { - ed.setProgressState(1); - t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) { - ed.setProgressState(0); - }); + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } } - } - }); + }); - m.add({ - title : 'spellchecker.ignore_words', - onclick : function() { - var word = wordSpan.innerHTML; + m.add({ + title : 'spellchecker.ignore_words', + onclick : function() { + var word = wordSpan.innerHTML; - t._removeWords(dom.decode(word)); - t._checkDone(); + t._removeWords(dom.decode(word)); + t._checkDone(); - // tell the server if we need to - if (ignoreRpc) { - ed.setProgressState(1); - t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) { - ed.setProgressState(0); - }); + // tell the server if we need to + if (ignoreRpc) { + ed.setProgressState(1); + t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) { + ed.setProgressState(0); + }); + } } - } - }); - + }); + } if (t.editor.getParam("spellchecker_enable_learn_rpc")) { m.add({ @@ -372,7 +373,7 @@ m.update(); }); - p1 = dom.getPos(ed.getContentAreaContainer()); + p1 = DOM.getPos(ed.getContentAreaContainer()); m.settings.offset_x = p1.x; m.settings.offset_y = p1.y; diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/img/wline.gif b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/img/wline.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/img/wline.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/img/wline.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/includes/general.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/includes/general.php similarity index 96% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/includes/general.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/includes/general.php index 9e1b7865e6d89..4414c291fbc01 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/includes/general.php +++ b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/includes/general.php @@ -4,7 +4,7 @@ * * @package MCManager.includes * @author Moxiecode - * @copyright Copyright (c) 2007, Moxiecode Systems AB, All rights reserved. + * @copyright Copyright © 2007, Moxiecode Systems AB, All rights reserved. */ @error_reporting(E_ALL ^ E_NOTICE); diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/rpc.php b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/rpc.php similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/spellchecker/rpc.php rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/spellchecker/rpc.php diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/css/props.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/css/props.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/css/props.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/css/props.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/js/props.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/js/props.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/js/props.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/js/props.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/props.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/props.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/style/props.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/style/props.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/tabfocus/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/tabfocus/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/tabfocus/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/tabfocus/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/tabfocus/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/tabfocus/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/tabfocus/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/tabfocus/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/cell.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/cell.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/cell.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/cell.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/cell.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/cell.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/cell.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/cell.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/row.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/row.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/row.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/row.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/table.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/table.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/css/table.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/css/table.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/cell.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/cell.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/cell.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/cell.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/merge_cells.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/merge_cells.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/merge_cells.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/merge_cells.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/row.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/row.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/row.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/row.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/table.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/table.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/js/table.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/js/table.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/merge_cells.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/merge_cells.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/merge_cells.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/merge_cells.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/row.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/row.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/row.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/row.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/table.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/table.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/table/table.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/table/table.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/blank.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/blank.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/blank.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/blank.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/css/template.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/css/template.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/css/template.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/css/template.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/js/template.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/js/template.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/js/template.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/js/template.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/template.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/template.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/template/template.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/template/template.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/visualchars/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/visualchars/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/visualchars/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/visualchars/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/visualchars/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/visualchars/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/visualchars/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/visualchars/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/wordcount/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/wordcount/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/wordcount/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/wordcount/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/wordcount/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/wordcount/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/wordcount/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/wordcount/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/abbr.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/abbr.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/abbr.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/abbr.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/acronym.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/acronym.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/acronym.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/acronym.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/attributes.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/attributes.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/attributes.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/attributes.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/cite.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/cite.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/cite.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/cite.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/css/attributes.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/css/attributes.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/css/attributes.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/css/attributes.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/css/popup.css b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/css/popup.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/css/popup.css rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/css/popup.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/del.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/del.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/del.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/del.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/editor_plugin.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/editor_plugin.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/editor_plugin.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/editor_plugin.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/editor_plugin_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/editor_plugin_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/editor_plugin_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/editor_plugin_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/ins.htm b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/ins.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/ins.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/ins.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/abbr.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/abbr.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/abbr.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/abbr.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/acronym.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/acronym.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/acronym.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/acronym.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/attributes.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/attributes.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/attributes.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/attributes.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/cite.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/cite.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/cite.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/cite.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/del.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/del.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/del.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/del.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/element_common.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/element_common.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/element_common.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/element_common.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/ins.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/ins.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/js/ins.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/js/ins.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/plugins/xhtmlxtras/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/plugins/xhtmlxtras/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/about.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/about.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/about.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/about.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/anchor.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/anchor.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/anchor.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/anchor.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/charmap.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/charmap.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/charmap.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/charmap.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/color_picker.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/color_picker.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/color_picker.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/color_picker.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/editor_template.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/editor_template.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/editor_template.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/editor_template.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/editor_template_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/editor_template_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/editor_template_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/editor_template_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/image.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/image.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/image.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/image.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/colorpicker.jpg b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/colorpicker.jpg similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/colorpicker.jpg rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/colorpicker.jpg diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/flash.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/flash.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/flash.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/flash.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/icons.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/icons.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/icons.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/icons.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/iframe.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/iframe.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/iframe.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/iframe.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/pagebreak.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/pagebreak.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/pagebreak.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/pagebreak.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/quicktime.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/quicktime.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/quicktime.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/quicktime.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/realmedia.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/realmedia.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/realmedia.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/realmedia.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/shockwave.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/shockwave.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/shockwave.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/shockwave.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/trans.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/trans.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/trans.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/trans.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/video.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/video.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/video.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/video.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/windowsmedia.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/windowsmedia.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/img/windowsmedia.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/img/windowsmedia.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/about.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/about.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/about.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/about.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/anchor.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/anchor.js similarity index 94% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/anchor.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/anchor.js index 7b556351df49d..b6c5b695c5a11 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/anchor.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/anchor.js @@ -30,9 +30,10 @@ var AnchorDialog = { ed.selection.collapse(1); elm = ed.dom.getParent(ed.selection.getNode(), 'A'); - if (elm) + if (elm) { + elm.setAttribute('name', name); elm.name = name; - else + } else ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : name, 'class' : 'mceItemAnchor'}, '')); tinyMCEPopup.close(); diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/charmap.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/charmap.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/charmap.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/charmap.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/color_picker.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/color_picker.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/color_picker.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/color_picker.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/image.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/image.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/image.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/image.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/link.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/link.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/link.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/link.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/source_editor.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/source_editor.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/js/source_editor.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/js/source_editor.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/langs/en.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/langs/en.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/langs/en_dlg.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/langs/en_dlg.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/langs/en_dlg.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/langs/en_dlg.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/link.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/link.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/link.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/link.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/shortcuts.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/shortcuts.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/shortcuts.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/shortcuts.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/content.css similarity index 95% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/content.css index b9adf6f02e1bf..4d63ca98103e7 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/content.css +++ b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/content.css @@ -13,7 +13,7 @@ a.mceItemAnchor {display:inline-block; -webkit-user-select:all; -webkit-user-mod span.mceItemNbsp {background: #DDD} td.mceSelected, th.mceSelected {background-color:#3399ff !important} img {border:0;} -table {cursor:default} +table, img, hr, .mceItemAnchor {cursor:default} table td, table th {cursor:text} ins {border-bottom:1px solid green; text-decoration: none; color:green} del {color:red; text-decoration:line-through} @@ -45,5 +45,6 @@ font[face=mceinline] {font-family:inherit !important} .mceItemRealMedia {background-image:url(../../img/realmedia.gif)} .mceItemVideo {background-image:url(../../img/video.gif)} .mceItemAudio {background-image:url(../../img/video.gif)} +.mceItemEmbeddedAudio {background-image:url(../../img/video.gif)} .mceItemIframe {background-image:url(../../img/iframe.gif)} .mcePageBreak {display:block;border:0;width:100%;height:12px;border-top:1px dotted #ccc;margin-top:15px;background:#fff url(../../img/pagebreak.gif) no-repeat center top;} diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/dialog.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/dialog.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/dialog.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/dialog.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/buttons.png b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/buttons.png similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/buttons.png rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/buttons.png diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/items.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/items.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/items.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/items.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/menu_arrow.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/menu_arrow.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/menu_arrow.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/menu_arrow.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/menu_check.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/menu_check.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/menu_check.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/menu_check.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/progress.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/progress.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/progress.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/progress.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/tabs.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/tabs.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/img/tabs.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/img/tabs.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/ui.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/ui.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/default/ui.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/default/ui.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/content.css similarity index 95% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/content.css index 46007b04267cf..ee3d369d02238 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/content.css +++ b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/content.css @@ -11,7 +11,7 @@ a.mceItemAnchor {display:inline-block; width:11px !important; height:11px !impo span.mceItemNbsp {background: #DDD} td.mceSelected, th.mceSelected {background-color:#3399ff !important} img {border:0;} -table {cursor:default} +table, img, hr, .mceItemAnchor {cursor:default} table td, table th {cursor:text} ins {border-bottom:1px solid green; text-decoration: none; color:green} del {color:red; text-decoration:line-through} diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/dialog.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/dialog.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/dialog.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/dialog.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/ui.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/ui.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/highcontrast/ui.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/highcontrast/ui.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/content.css similarity index 97% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/content.css index 47a39255a1201..631fa0ec87400 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/content.css +++ b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/content.css @@ -12,7 +12,7 @@ a.mceItemAnchor {display:inline-block; width:11px !important; height:11px !impo span.mceItemNbsp {background: #DDD} td.mceSelected, th.mceSelected {background-color:#3399ff !important} img {border:0;} -table {cursor:default} +table, img, hr, .mceItemAnchor {cursor:default} table td, table th {cursor:text} ins {border-bottom:1px solid green; text-decoration: none; color:green} del {color:red; text-decoration:line-through} diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/dialog.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/dialog.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/dialog.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/dialog.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg.png b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg.png similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg.png rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg.png diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg_black.png b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg_black.png similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg_black.png rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg_black.png diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg_silver.png b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg_silver.png similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/img/button_bg_silver.png rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/img/button_bg_silver.png diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui_black.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui_black.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui_black.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui_black.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui_silver.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui_silver.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/skins/o2k7/ui_silver.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/skins/o2k7/ui_silver.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/source_editor.htm b/lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/source_editor.htm similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/advanced/source_editor.htm rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/advanced/source_editor.htm diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/editor_template.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/editor_template.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/editor_template.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/editor_template.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/editor_template_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/editor_template_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/editor_template_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/editor_template_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/img/icons.gif b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/img/icons.gif similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/img/icons.gif rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/img/icons.gif diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/langs/en.js b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/langs/en.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/langs/en.js rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/langs/en.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/default/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/default/content.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/default/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/default/content.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/default/ui.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/default/ui.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/default/ui.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/default/ui.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/content.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/content.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/content.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/content.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/img/button_bg.png b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/img/button_bg.png similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/img/button_bg.png rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/img/button_bg.png diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/ui.css b/lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/ui.css similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/themes/simple/skins/o2k7/ui.css rename to lib/editor/tinymce/tiny_mce/3.4.6/themes/simple/skins/o2k7/ui.css diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce.js new file mode 100644 index 0000000000000..f6754fdd172bd --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce.js @@ -0,0 +1 @@ +(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(i){var g=i.VK,h=g.BACKSPACE,f=g.DELETE;function b(j){var l=j.dom,k=j.selection;j.onKeyDown.add(function(n,r){var m,s,p,q,o;o=r.keyCode==f;if(o||r.keyCode==h){r.preventDefault();m=k.getRng();s=l.getParent(m.startContainer,l.isBlock);if(o){s=l.getNext(s,l.isBlock)}if(s){p=s.firstChild;while(p.nodeType==3&&p.nodeValue.length==0){p=p.nextSibling}if(p&&p.nodeName==="SPAN"){q=p.cloneNode(false)}}n.getDoc().execCommand(o?"ForwardDelete":"Delete",false,null);s=l.getParent(m.startContainer,l.isBlock);i.each(l.select("span.Apple-style-span,font.Apple-style-span",s),function(t){var u=k.getBookmark();if(q){l.replace(q.cloneNode(false),t,true)}else{l.remove(t,true)}k.moveToBookmark(u)})}})}function c(j){j.onKeyUp.add(function(k,m){var l=m.keyCode;if(l==f||l==h){if(k.dom.isEmpty(k.getBody())){k.setContent("",{format:"raw"});k.nodeChanged();return}}})}function a(j){j.dom.bind(j.getDoc(),"focusin",function(){j.selection.setRng(j.selection.getRng())})}function e(j){if(!Range.prototype.getClientRects){j.onMouseDown.add(function(l,m){if(m.target.nodeName==="HTML"){var k=l.getBody();k.blur();setTimeout(function(){k.focus()},0)}})}}function d(j){j.onClick.add(function(k,l){l=l.target;if(/^(IMG|HR)$/.test(l.nodeName)){k.selection.getSel().setBaseAndExtent(l,0,l,1)}if(l.nodeName=="A"&&k.dom.hasClass(l,"mceItemAnchor")){k.selection.select(l)}k.nodeChanged()})}i.create("tinymce.util.Quirks",{Quirks:function(j){if(i.isWebKit){b(j);c(j);a(j);d(j)}if(i.isIE){c(j)}if(i.isGecko){e(j)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},select:function(l,k){var j=this;return h.dom.Sizzle(l,j.get(k)||j.get(j.settings.root_element)||j.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(a.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return h.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,j=0,d=Object.prototype.toString,o=false,i=true;[0,0].sort(function(){i=false;return 0});var b=function(v,e,z,A){z=z||[];e=e||document;var C=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!v||typeof v!=="string"){return z}var x=[],s,E,H,r,u=true,t=b.isXML(e),B=v,D,G,F,y;do{p.exec("");s=p.exec(B);if(s){B=s[3];x.push(s[1]);if(s[2]){r=s[3];break}}}while(s);if(x.length>1&&k.exec(v)){if(x.length===2&&f.relative[x[0]]){E=h(x[0]+x[1],e)}else{E=f.relative[x[0]]?[e]:b(x.shift(),e);while(x.length){v=x.shift();if(f.relative[v]){v+=x.shift()}E=h(v,E)}}}else{if(!A&&x.length>1&&e.nodeType===9&&!t&&f.match.ID.test(x[0])&&!f.match.ID.test(x[x.length-1])){D=b.find(x.shift(),e,t);e=D.expr?b.filter(D.expr,D.set)[0]:D.set[0]}if(e){D=A?{expr:x.pop(),set:a(A)}:b.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&e.parentNode?e.parentNode:e,t);E=D.expr?b.filter(D.expr,D.set):D.set;if(x.length>0){H=a(E)}else{u=false}while(x.length){G=x.pop();F=G;if(!f.relative[G]){G=""}else{F=x.pop()}if(F==null){F=e}f.relative[G](H,F,t)}}else{H=x=[]}}if(!H){H=E}if(!H){b.error(G||v)}if(d.call(H)==="[object Array]"){if(!u){z.push.apply(z,H)}else{if(e&&e.nodeType===1){for(y=0;H[y]!=null;y++){if(H[y]&&(H[y]===true||H[y].nodeType===1&&b.contains(e,H[y]))){z.push(E[y])}}}else{for(y=0;H[y]!=null;y++){if(H[y]&&H[y].nodeType===1){z.push(E[y])}}}}}else{a(H,z)}if(r){b(r,C,z,A);b.uniqueSort(z)}return z};b.uniqueSort=function(r){if(c){o=i;r.sort(c);if(o){for(var e=1;e":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){return r[1].toLowerCase()},CHILD:function(e){if(e[1]==="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=j++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if((p.exec(u[3])||"").length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return re[3]-0},nth:function(s,r,e){return e[3]-0===r},eq:function(s,r,e){return e[3]-0===r}},filter:{PSEUDO:function(s,y,x,z){var e=y[1],r=f.filters[e];if(r){return r(s,x,y,z)}else{if(e==="contains"){return(s.textContent||s.innerText||b.getText([s])||"").indexOf(y[3])>=0}else{if(e==="not"){var t=y[3];for(var v=0,u=t.length;v=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName.toLowerCase()===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!==r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var k=f.match.POS,g=function(r,e){return"\\"+(e-0+1)};for(var m in f.match){f.match[m]=new RegExp(f.match[m].source+(/(?![^\[]*\])(?![^\(]*\))/.source));f.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+f.match[m].source.replace(/\\(\d+)/g,g))}var a=function(r,e){r=Array.prototype.slice.call(r,0);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(l){a=function(u,t){var r=t||[],s=0;if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var e=u.length;s";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r);e=r=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="

    ";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!b.isXML(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}s=null})()}(function(){var e=document.createElement("div");e.innerHTML="
    ";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}};e=null})();function n(r,x,v,A,y,z){for(var t=0,s=A.length;t0){u=e;break}}}e=e[r]}A[t]=u}}}b.contains=document.compareDocumentPosition?function(r,e){return !!(r.compareDocumentPosition(e)&16)}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};b.isXML=function(e){var r=(e?e.ownerDocument||e:0).documentElement;return r?r.nodeName!=="HTML":false};var h=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var h=this,i,j,g;if(f!=h.selectedIndex){i=c.get(h.id+"_text");g=c.get(h.id+"_voiceDesc");j=h.items[f];if(j){h.selectedValue=j.value;h.selectedIndex=f;c.setHTML(i,c.encode(j.title));c.setHTML(g,h.settings.title+" - "+j.title);c.removeClass(i,"mceTitle");c.setAttrib(h.id,"aria-valuenow",j.title)}else{c.setHTML(i,c.encode(h.settings.title));c.setHTML(g,c.encode(h.settings.title));c.addClass(i,"mceTitle");h.selectedValue=h.selectedIndex=null;c.setAttrib(h.id,"aria-valuenow",h.settings.title)}i=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='';i+="";i+="";i+="";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.role="option";h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';for(z=0;z'}y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_dev.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_dev.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_dev.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_dev.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery.js new file mode 100644 index 0000000000000..adff8d580bce9 --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery.js @@ -0,0 +1 @@ +(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(i){var g=i.VK,h=g.BACKSPACE,f=g.DELETE;function b(j){var l=j.dom,k=j.selection;j.onKeyDown.add(function(n,r){var m,s,p,q,o;o=r.keyCode==f;if(o||r.keyCode==h){r.preventDefault();m=k.getRng();s=l.getParent(m.startContainer,l.isBlock);if(o){s=l.getNext(s,l.isBlock)}if(s){p=s.firstChild;while(p.nodeType==3&&p.nodeValue.length==0){p=p.nextSibling}if(p&&p.nodeName==="SPAN"){q=p.cloneNode(false)}}n.getDoc().execCommand(o?"ForwardDelete":"Delete",false,null);s=l.getParent(m.startContainer,l.isBlock);i.each(l.select("span.Apple-style-span,font.Apple-style-span",s),function(t){var u=k.getBookmark();if(q){l.replace(q.cloneNode(false),t,true)}else{l.remove(t,true)}k.moveToBookmark(u)})}})}function c(j){j.onKeyUp.add(function(k,m){var l=m.keyCode;if(l==f||l==h){if(k.dom.isEmpty(k.getBody())){k.setContent("",{format:"raw"});k.nodeChanged();return}}})}function a(j){j.dom.bind(j.getDoc(),"focusin",function(){j.selection.setRng(j.selection.getRng())})}function e(j){if(!Range.prototype.getClientRects){j.onMouseDown.add(function(l,m){if(m.target.nodeName==="HTML"){var k=l.getBody();k.blur();setTimeout(function(){k.focus()},0)}})}}function d(j){j.onClick.add(function(k,l){l=l.target;if(/^(IMG|HR)$/.test(l.nodeName)){k.selection.getSel().setBaseAndExtent(l,0,l,1)}if(l.nodeName=="A"&&k.dom.hasClass(l,"mceItemAnchor")){k.selection.select(l)}k.nodeChanged()})}i.create("tinymce.util.Quirks",{Quirks:function(j){if(i.isWebKit){b(j);c(j);a(j);d(j)}if(i.isIE){c(j)}if(i.isGecko){e(j)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(d){var f=d.each,c=d.DOM,b=d.isIE,e=d.isWebKit,a;d.create("tinymce.dom.EventUtils",{EventUtils:function(){this.inits=[];this.events=[]},add:function(m,p,l,j){var g,h=this,i=h.events,k;if(p instanceof Array){k=[];f(p,function(o){k.push(h.add(m,o,l,j))});return k}if(m&&m.hasOwnProperty&&m instanceof Array){k=[];f(m,function(n){n=c.get(n);k.push(h.add(n,p,l,j))});return k}m=c.get(m);if(!m){return}g=function(n){if(h.disabled){return}n=n||window.event;if(n&&b){if(!n.target){n.target=n.srcElement}d.extend(n,h._stoppers)}if(!j){return l(n)}return l.call(j,n)};if(p=="unload"){d.unloads.unshift({func:g});return g}if(p=="init"){if(h.domLoaded){g()}else{h.inits.push(g)}return g}i.push({obj:m,name:p,func:l,cfunc:g,scope:j});h._add(m,p,g);return l},remove:function(l,m,k){var h=this,g=h.events,i=false,j;if(l&&l.hasOwnProperty&&l instanceof Array){j=[];f(l,function(n){n=c.get(n);j.push(h.remove(n,m,k))});return j}l=c.get(l);f(g,function(o,n){if(o.obj==l&&o.name==m&&(!k||(o.func==k||o.cfunc==k))){g.splice(n,1);h._remove(l,m,o.cfunc);i=true;return false}});return i},clear:function(l){var j=this,g=j.events,h,k;if(l){l=c.get(l);for(h=g.length-1;h>=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var h=this,i,j,g;if(f!=h.selectedIndex){i=c.get(h.id+"_text");g=c.get(h.id+"_voiceDesc");j=h.items[f];if(j){h.selectedValue=j.value;h.selectedIndex=f;c.setHTML(i,c.encode(j.title));c.setHTML(g,h.settings.title+" - "+j.title);c.removeClass(i,"mceTitle");c.setAttrib(h.id,"aria-valuenow",j.title)}else{c.setHTML(i,c.encode(h.settings.title));c.setHTML(g,c.encode(h.settings.title));c.addClass(i,"mceTitle");h.selectedValue=h.selectedIndex=null;c.setAttrib(h.id,"aria-valuenow",h.settings.title)}i=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='';i+="";i+="";i+="";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.role="option";h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);if(j.adapter){j.adapter.patchEditor(m)}return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';for(z=0;z'}y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery_src.js similarity index 99% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery_src.js index 29df1d6657879..4f8aa99530c36 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_jquery_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_jquery_src.js @@ -1337,6 +1337,10 @@ tinymce.create('static tinymce.util.XHR', { if (blockElm) { node = blockElm.firstChild; + // Ignore empty text nodes + while (node.nodeType == 3 && node.nodeValue.length == 0) + node = node.nextSibling; + if (node && node.nodeName === 'SPAN') { clonedSpan = node.cloneNode(false); } @@ -1348,11 +1352,7 @@ tinymce.create('static tinymce.util.XHR', { // Find all odd apple-style-spans blockElm = dom.getParent(rng.startContainer, dom.isBlock); tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) { - var rng = dom.createRng(); - - // Set range selection before the span we are about to remove - rng.setStartBefore(span); - rng.setEndBefore(span); + var bm = selection.getBookmark(); if (clonedSpan) { dom.replace(clonedSpan.cloneNode(false), span, true); @@ -1361,7 +1361,7 @@ tinymce.create('static tinymce.util.XHR', { } // Restore the selection - selection.setRng(rng); + selection.moveToBookmark(bm); }); } }); @@ -1380,19 +1380,69 @@ tinymce.create('static tinymce.util.XHR', { } }); }; - + + function inputMethodFocus(ed) { + ed.dom.bind(ed.getDoc(), 'focusin', function() { + ed.selection.setRng(ed.selection.getRng()); + }); + }; + + function focusBody(ed) { + // Fix for a focus bug in FF 3.x where the body element + // wouldn't get proper focus if the user clicked on the HTML element + if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 + ed.onMouseDown.add(function(ed, e) { + if (e.target.nodeName === "HTML") { + var body = ed.getBody(); + + // Blur the body it's focused but not correctly focused + body.blur(); + + // Refocus the body after a little while + setTimeout(function() { + body.focus(); + }, 0); + } + }); + } + }; + + function selectControlElements(ed) { + ed.onClick.add(function(ed, e) { + e = e.target; + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // Needs tobe the setBaseAndExtend or it will fail to select floated images + if (/^(IMG|HR)$/.test(e.nodeName)) + ed.selection.getSel().setBaseAndExtent(e, 0, e, 1); + + if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor')) + ed.selection.select(e); + + ed.nodeChanged(); + }); + }; + tinymce.create('tinymce.util.Quirks', { Quirks: function(ed) { - // Load WebKit specific fixed + // WebKit if (tinymce.isWebKit) { cleanupStylesWhenDeleting(ed); emptyEditorWhenDeleting(ed); + inputMethodFocus(ed); + selectControlElements(ed); } - // Load IE specific fixes + // IE if (tinymce.isIE) { emptyEditorWhenDeleting(ed); } + + // Gecko + if (tinymce.isGecko) { + focusBody(ed); + } } }); })(tinymce); @@ -8873,20 +8923,23 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }, selectByIndex : function(idx) { - var t = this, e, o; + var t = this, e, o, label; if (idx != t.selectedIndex) { e = DOM.get(t.id + '_text'); + label = DOM.get(t.id + '_voiceDesc'); o = t.items[idx]; if (o) { t.selectedValue = o.value; t.selectedIndex = idx; DOM.setHTML(e, DOM.encode(o.title)); + DOM.setHTML(label, t.settings.title + " - " + o.title); DOM.removeClass(e, 'mceTitle'); DOM.setAttrib(t.id, 'aria-valuenow', o.title); } else { DOM.setHTML(e, DOM.encode(t.settings.title)); + DOM.setHTML(label, DOM.encode(t.settings.title)); DOM.addClass(e, 'mceTitle'); t.selectedValue = t.selectedIndex = null; DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title); @@ -8915,7 +8968,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { renderHTML : function() { var h = '', t = this, s = t.settings, cp = t.classPrefix; - h = ''; + h = ''; h += ''; h += ''; @@ -9011,6 +9064,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { if (o.value === undefined) { m.add({ title : o.title, + role : "option", 'class' : 'mceMenuItemTitle', onclick : function() { if (t.settings.onselect('') !== false) @@ -9019,6 +9073,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }); } else { o.id = DOM.uniqueId(); + o.role= "option"; o.onclick = function() { if (t.settings.onselect(o.value) !== false) t.select(o.value); // Must be runned after @@ -9350,8 +9405,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { h += ''; h += ''; - h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); - return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); + h = DOM.createHTML('table', { role: 'presentation', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); + return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); }, postRender : function() { @@ -9623,7 +9678,8 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { }, focus : function() { - this.keyNav.focus(); + var t = this; + dom.get(t.id).focus(); }, postRender : function() { @@ -9641,6 +9697,10 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { root: t.id, items: items, onCancel: function() { + //Move focus if webkit so that navigation back will read the item. + if (tinymce.isWebKit) { + dom.get(t.editor.id+"_ifr").focus(); + } t.editor.focus(); }, excludeFromTabOrder: !t.settings.tab_focus_toolbar @@ -10576,6 +10636,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.iframeHTML += ''; + // Load the CSS by injecting them into the HTML this will reduce "flicker" + for (i = 0; i < t.contentCSS.length; i++) { + t.iframeHTML += ''; + } + bi = s.body_id || 'tinymce'; if (bi.indexOf('=') != -1) { bi = t.getParam('body_id', '', 'hash'); @@ -10593,7 +10658,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Domain relaxing enabled, then set document domain if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) { // We need to write the contents here in IE since multiple writes messes up refresh button and back button - u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; + u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; } // Create iframe @@ -10627,24 +10692,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Setup iframe body if (!isIE || !tinymce.relaxedDomain) { - // Fix for a focus bug in FF 3.x where the body element - // wouldn't get proper focus if the user clicked on the HTML element - if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 - t.onMouseDown.add(function(ed, e) { - if (e.target.nodeName === "HTML") { - var body = t.getBody(); - - // Blur the body it's focused but not correctly focused - body.blur(); - - // Refocus the body after a little while - setTimeout(function() { - body.focus(); - }, 0); - } - }); - } - d.open(); d.write(t.iframeHTML); d.close(); @@ -11831,21 +11878,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.onMouseDown.add(setOpts); } - t.onClick.add(function(ed, e) { - e = e.target; - - // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 - // WebKit can't even do simple things like selecting an image - // Needs tobe the setBaseAndExtend or it will fail to select floated images - if (tinymce.isWebKit && e.nodeName == 'IMG') - t.selection.getSel().setBaseAndExtent(e, 0, e, 1); - - if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) - t.selection.select(e); - - t.nodeChanged(); - }); - // Add node change handlers t.onMouseUp.add(t.nodeChanged); //t.onClick.add(t.nodeChanged); @@ -13598,7 +13630,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { id = t.prefix + id; - if (ed.settings.use_native_selects) + + function useNativeListForAccessibility(ed) { + return ed.settings.use_accessible_selects && !tinymce.isGecko + } + + if (ed.settings.use_native_selects || useNativeListForAccessibility(ed)) c = new tinymce.ui.NativeListBox(id, s); else { cls = cc || t._cls.listbox || tinymce.ui.ListBox; @@ -14614,7 +14651,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { selection.moveToBookmark(bookmark); // Check if start element still has formatting then we are at: "text|text" and need to move the start into the next text node - if (match(name, vars, selection.getStart())) { + if (format.inline && match(name, vars, selection.getStart())) { moveStart(selection.getRng(true)); } diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_popup.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_popup.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_popup.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_popup.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_popup_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_popup_src.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_popup_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_popup_src.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype.js new file mode 100644 index 0000000000000..a99d300e28725 --- /dev/null +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype.js @@ -0,0 +1 @@ +(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"@@tinymce_major_version@@",minorVersion:"@@tinymce_minor_version@@",releaseDate:"@@tinymce_release_date@@",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8}})(tinymce);(function(i){var g=i.VK,h=g.BACKSPACE,f=g.DELETE;function b(j){var l=j.dom,k=j.selection;j.onKeyDown.add(function(n,r){var m,s,p,q,o;o=r.keyCode==f;if(o||r.keyCode==h){r.preventDefault();m=k.getRng();s=l.getParent(m.startContainer,l.isBlock);if(o){s=l.getNext(s,l.isBlock)}if(s){p=s.firstChild;while(p.nodeType==3&&p.nodeValue.length==0){p=p.nextSibling}if(p&&p.nodeName==="SPAN"){q=p.cloneNode(false)}}n.getDoc().execCommand(o?"ForwardDelete":"Delete",false,null);s=l.getParent(m.startContainer,l.isBlock);i.each(l.select("span.Apple-style-span,font.Apple-style-span",s),function(t){var u=k.getBookmark();if(q){l.replace(q.cloneNode(false),t,true)}else{l.remove(t,true)}k.moveToBookmark(u)})}})}function c(j){j.onKeyUp.add(function(k,m){var l=m.keyCode;if(l==f||l==h){if(k.dom.isEmpty(k.getBody())){k.setContent("",{format:"raw"});k.nodeChanged();return}}})}function a(j){j.dom.bind(j.getDoc(),"focusin",function(){j.selection.setRng(j.selection.getRng())})}function e(j){if(!Range.prototype.getClientRects){j.onMouseDown.add(function(l,m){if(m.target.nodeName==="HTML"){var k=l.getBody();k.blur();setTimeout(function(){k.focus()},0)}})}}function d(j){j.onClick.add(function(k,l){l=l.target;if(/^(IMG|HR)$/.test(l.nodeName)){k.selection.getSel().setBaseAndExtent(l,0,l,1)}if(l.nodeName=="A"&&k.dom.hasClass(l,"mceItemAnchor")){k.selection.select(l)}k.nodeChanged()})}i.create("tinymce.util.Quirks",{Quirks:function(j){if(i.isWebKit){b(j);c(j);a(j);d(j)}if(i.isIE){c(j)}if(i.isGecko){e(j)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)\\s*((?:[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},select:function(l,k){var j=this;return h.dom.Sizzle(l,j.get(k)||j.get(j.settings.root_element)||j.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(a.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return h.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+""}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="
    "+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
    "+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,j=0,d=Object.prototype.toString,o=false,i=true;[0,0].sort(function(){i=false;return 0});var b=function(v,e,z,A){z=z||[];e=e||document;var C=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!v||typeof v!=="string"){return z}var x=[],s,E,H,r,u=true,t=b.isXML(e),B=v,D,G,F,y;do{p.exec("");s=p.exec(B);if(s){B=s[3];x.push(s[1]);if(s[2]){r=s[3];break}}}while(s);if(x.length>1&&k.exec(v)){if(x.length===2&&f.relative[x[0]]){E=h(x[0]+x[1],e)}else{E=f.relative[x[0]]?[e]:b(x.shift(),e);while(x.length){v=x.shift();if(f.relative[v]){v+=x.shift()}E=h(v,E)}}}else{if(!A&&x.length>1&&e.nodeType===9&&!t&&f.match.ID.test(x[0])&&!f.match.ID.test(x[x.length-1])){D=b.find(x.shift(),e,t);e=D.expr?b.filter(D.expr,D.set)[0]:D.set[0]}if(e){D=A?{expr:x.pop(),set:a(A)}:b.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&e.parentNode?e.parentNode:e,t);E=D.expr?b.filter(D.expr,D.set):D.set;if(x.length>0){H=a(E)}else{u=false}while(x.length){G=x.pop();F=G;if(!f.relative[G]){G=""}else{F=x.pop()}if(F==null){F=e}f.relative[G](H,F,t)}}else{H=x=[]}}if(!H){H=E}if(!H){b.error(G||v)}if(d.call(H)==="[object Array]"){if(!u){z.push.apply(z,H)}else{if(e&&e.nodeType===1){for(y=0;H[y]!=null;y++){if(H[y]&&(H[y]===true||H[y].nodeType===1&&b.contains(e,H[y]))){z.push(E[y])}}}else{for(y=0;H[y]!=null;y++){if(H[y]&&H[y].nodeType===1){z.push(E[y])}}}}}else{a(H,z)}if(r){b(r,C,z,A);b.uniqueSort(z)}return z};b.uniqueSort=function(r){if(c){o=i;r.sort(c);if(o){for(var e=1;e":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){return r[1].toLowerCase()},CHILD:function(e){if(e[1]==="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=j++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if((p.exec(u[3])||"").length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return re[3]-0},nth:function(s,r,e){return e[3]-0===r},eq:function(s,r,e){return e[3]-0===r}},filter:{PSEUDO:function(s,y,x,z){var e=y[1],r=f.filters[e];if(r){return r(s,x,y,z)}else{if(e==="contains"){return(s.textContent||s.innerText||b.getText([s])||"").indexOf(y[3])>=0}else{if(e==="not"){var t=y[3];for(var v=0,u=t.length;v=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName.toLowerCase()===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!==r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var k=f.match.POS,g=function(r,e){return"\\"+(e-0+1)};for(var m in f.match){f.match[m]=new RegExp(f.match[m].source+(/(?![^\[]*\])(?![^\(]*\))/.source));f.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+f.match[m].source.replace(/\\(\d+)/g,g))}var a=function(r,e){r=Array.prototype.slice.call(r,0);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(l){a=function(u,t){var r=t||[],s=0;if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var e=u.length;s";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r);e=r=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="

    ";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!b.isXML(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}s=null})()}(function(){var e=document.createElement("div");e.innerHTML="
    ";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}};e=null})();function n(r,x,v,A,y,z){for(var t=0,s=A.length;t0){u=e;break}}}e=e[r]}A[t]=u}}}b.contains=document.compareDocumentPosition?function(r,e){return !!(r.compareDocumentPosition(e)&16)}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};b.isXML=function(e){var r=(e?e.ownerDocument||e:0).documentElement;return r?r.nodeName!=="HTML":false};var h=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='_';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('_'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'
    ':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(g,f){var i=this,j=i.dom,m,h,l,k=[];m=j.getParent(g||i.getStart(),j.isBlock);h=j.getParent(f||i.getEnd(),j.isBlock);if(m){k.push(m)}if(m&&h&&m!=h){l=m;while((l=l.nextSibling)&&l!=h){if(j.isBlock(l)){k.push(l)}}}if(h&&m!=h){k.push(h)}return k},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// "}}else{if(o.length>0){n.firstChild.value=""}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,r){var h=d.startContainer,k=d.startOffset,s=d.endContainer,l=d.endOffset,i,f,n,g,q,p,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(t){r([t])});return}function o(v,u,t){var x=[];for(;v&&v!=t;v=v[u]){x.push(v)}return x}function m(u,t){do{if(u.parentNode==t){return u}u=u.parentNode}while(u)}function j(v,u,x){var t=x?"nextSibling":"previousSibling";for(g=v,q=g.parentNode;g&&g!=u;g=q){q=g.parentNode;p=o(g==v?g:g[t],t);if(p.length){if(!x){p.reverse()}r(p)}}}if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[k]}if(s.nodeType==1&&s.hasChildNodes()){s=s.childNodes[Math.min(l-1,s.childNodes.length-1)]}i=c.findCommonAncestor(h,s);if(h==s){return r([h])}for(g=h;g;g=g.parentNode){if(g==s){return j(h,i,true)}if(g==i){break}}for(g=s;g;g=g.parentNode){if(g==h){return j(s,i)}if(g==i){break}}f=m(h,i)||h;n=m(s,i)||s;j(h,f,true);p=o(f==h?f:f.nextSibling,"nextSibling",n==s?n.nextSibling:n);if(p.length){r(p)}j(s,n)}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeightv){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var h=this,i,j,g;if(f!=h.selectedIndex){i=c.get(h.id+"_text");g=c.get(h.id+"_voiceDesc");j=h.items[f];if(j){h.selectedValue=j.value;h.selectedIndex=f;c.setHTML(i,c.encode(j.title));c.setHTML(g,h.settings.title+" - "+j.title);c.removeClass(i,"mceTitle");c.setAttrib(h.id,"aria-valuenow",j.title)}else{c.setHTML(i,c.encode(h.settings.title));c.setHTML(g,c.encode(h.settings.title));c.addClass(i,"mceTitle");h.selectedValue=h.selectedIndex=null;c.setAttrib(h.id,"aria-valuenow",h.settings.title)}i=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='
    ';i+="";i+="";i+="
    ";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.role="option";h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+=""+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"";i+="";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('
    ');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
    ");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,""));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},""+f+"")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){return;var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l':"",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/../../extra/strings.php?elanguage="+v.language+"ðeme="+v.theme)}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),I.theme_advanced_resizing_min_height||100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<(I.theme_advanced_resizing_min_height||100)){A=I.theme_advanced_resizing_min_height||100}H.iframeHTML=I.doctype+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';for(z=0;z'}y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='
    ';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length;while(z--){s[z].attr("type","mce-text/javascript")}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
    ")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
    '+t.content+"
    "}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
    [\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'>
    "}else{u='
    '}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isWebKit){q.bind(B.getDoc(),"selectionchange",function(){if(B.selectionTimer){clearTimeout(B.selectionTimer);B.selectionTimer=0}B.selectionTimer=window.setTimeout(function(){B.nodeChanged()},50)})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.removeAllAttribs(t);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='\uFEFF';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(//i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.yL.x+L.w||C.x")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e0||this.typing},hasRedo:function(){return e');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('
    ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"
    ");T=v.dom.add(O,V.element,null,"
    ")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
    ")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"
    ";return r[0]}else{y.innerHTML=b?"\u00a0":"
    "}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(BL.y+L.h){v.getWin().scrollTo(0,B1||ac==au){return ac}}}var am=V.selection.getRng();var aq=am.startContainer;var al=am.endContainer;if(aq!=al&&am.endOffset==0){var ap=an(aq,al);var ao=ap.nodeType==3?ap.length:ap.childNodes.length;am.setEnd(ap,ao)}return am}function Y(ao,au,ar,aq,am){var al=[],an=-1,at,aw=-1,ap=-1,av;O(ao.childNodes,function(ay,ax){if(ay.nodeName==="UL"||ay.nodeName==="OL"){an=ax;at=ay;return false}});O(ao.childNodes,function(ay,ax){if(ay.nodeName==="SPAN"&&c.getAttrib(ay,"data-mce-type")=="bookmark"){if(ay.id==au.id+"_start"){aw=ax}else{if(ay.id==au.id+"_end"){ap=ax}}}});if(an<=0||(awan)){O(a.grep(ao.childNodes),am);return 0}else{av=ar.cloneNode(S);O(a.grep(ao.childNodes),function(ay,ax){if((awan&&ax>an)){al.push(ay);ay.parentNode.removeChild(ay)}});if(awan){ao.insertBefore(av,at.nextSibling)}}aq.push(av);O(al,function(ax){av.appendChild(ax)});return av}}function aj(am,ao){var al=[],ap,an;ap=ai.inline||ai.block;an=c.create(ap);W(an);K.walk(am,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ai.block){c.remove(au)}return}if(ai.wrapper&&x(au,Z,ah)){ar=0;return}if(ai.block&&!ai.wrapper&&G(ax)){au=c.rename(au,ap);W(au);al.push(au);ar=0;return}if(ai.selector){O(ad,function(ay){if("collapsed" in ay&&ay.collapsed!==ae){return}if(c.is(au,ay.selector)&&!b(au)){W(au,ay);av=true}});if(!ai.inline||av){ar=0;return}}if(d(ap,ax)&&d(aw,ap)&&!(au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)){if(!ar){ar=an.cloneNode(S);au.parentNode.insertBefore(ar,au);al.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&ao){ar=Y(au,ao,an,al,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ai.wrap_links===false){O(al,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=an.cloneNode(S);al.push(au);at=a.grep(aw.childNodes);for(av=0;av1||!F(at))&&aq===0){c.remove(at,1);return}if(ai.inline||ai.wrapper){if(!ai.exact&&aq===1){at=ar(at)}O(ad,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}U(av,ah,ax,av.exact?ax:null)})});if(x(at.parentNode,Z,ah)){c.remove(at,1);at=0;return B}if(ai.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Z,ah)){c.remove(at,1);at=0;return B}})}if(at&&ai.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ai){if(ac){X=c.createRng();X.setStartBefore(ac);X.setEndAfter(ac);aj(o(X,ad))}else{if(!ae||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){var ak=V.selection.getNode();V.selection.setRng(ab());ag=q.getBookmark();aj(o(q.getRng(B),ad),ag);if(ai.styles&&(ai.styles.color||ai.styles.textDecoration)){a.walk(ak,I,"childNodes");I(ak)}q.moveToBookmark(ag);q.setRng(aa(q.getRng(B)));V.nodeChanged()}else{Q("apply",Z,ah)}}}}function A(Y,ah,ab){var ac=R(Y),aj=ac[0],ag,af,X;function aa(am){var al=am.startContainer,ar=am.startOffset,aq,ap,an,ao;if(al.nodeType==3&&ar>=al.nodeValue.length-1){al=al.parentNode;ar=s(al)+1}if(al.nodeType==1){an=al.childNodes;al=an[Math.min(ar,an.length-1)];aq=new t(al);if(ar>an.length-1){aq.next()}for(ap=aq.current();ap;ap=aq.next()){if(ap.nodeType==3&&!f(ap)){ao=c.create("a",null,E);ap.parentNode.insertBefore(ao,ap);am.setStart(ap,0);q.setRng(am);c.remove(ao);return}}}}function Z(ao){var an,am,al;an=a.grep(ao.childNodes);for(am=0,al=ac.length;am=0;Z--){if(P.apply[Z].name==Y){return true}}for(Z=P.remove.length-1;Z>=0;Z--){if(P.remove[Z].name==Y){return false}}return W(q.getNode())}aa=q.getNode();if(W(aa)){return B}X=q.getStart();if(X!=aa){if(W(X)){return B}}return S}function v(ad,ac){var aa,ab=[],Z={},Y,X,W;if(q.isCollapsed()){for(X=0;X=0;Y--){W=ad[X];if(P.remove[Y].name==W){Z[W]=true;break}}}for(Y=P.apply.length-1;Y>=0;Y--){for(X=0;X=0;X--){W=ac[X].selector;if(!W){return B}for(ab=Y.length-1;ab>=0;ab--){if(c.is(Y[ab],W)){return B}}}}return S}a.extend(this,{get:R,register:k,apply:T,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(W,X){if(g(W,X.inline)){return B}if(g(W,X.block)){return B}if(X.selector){return c.is(W,X.selector)}}function g(X,W){X=X||"";W=W||"";X=""+(X.nodeName||X);W=""+(W.nodeName||W);return X.toLowerCase()==W.toLowerCase()}function L(X,W){var Y=c.getStyle(X,W);if(W=="color"||W=="backgroundColor"){Y=c.toHex(Y)}if(W=="fontWeight"&&Y==700){Y="bold"}return""+Y}function r(W,X){if(typeof(W)!="string"){W=W(X)}else{if(X){W=W.replace(/%(\w+)/g,function(Z,Y){return X[Y]||Z})}}return W}function f(W){return W&&W.nodeType===3&&/^([\s\r\n]+|)$/.test(W.nodeValue)}function N(Y,X,W){var Z=c.create(X,W);Y.parentNode.insertBefore(Z,Y);Z.appendChild(Y);return Z}function o(W,ag,Z){var Y=W.startContainer,ad=W.startOffset,aj=W.endContainer,ae=W.endOffset,ai,af,ac;function ah(am,an,ak,al){var ao,ap;al=al||c.getRoot();for(;;){ao=am.parentNode;if(ao==al||(!ag[0].block_expand&&F(ao))){return am}for(ai=ao[an];ai&&ai!=am;ai=ai[ak]){if(ai.nodeType==1&&!H(ai)){return am}if(ai.nodeType==3&&!f(ai)){return am}}am=am.parentNode}return am}function ab(ak,al){if(al===p){al=ak.nodeType===3?ak.length:ak.childNodes.length}while(ak&&ak.hasChildNodes()){ak=ak.childNodes[al];if(ak){al=ak.nodeType===3?ak.length:ak.childNodes.length}}return{node:ak,offset:al}}if(Y.nodeType==1&&Y.hasChildNodes()){af=Y.childNodes.length-1;Y=Y.childNodes[ad>af?af:ad];if(Y.nodeType==3){ad=0}}if(aj.nodeType==1&&aj.hasChildNodes()){af=aj.childNodes.length-1;aj=aj.childNodes[ae>af?af:ae-1];if(aj.nodeType==3){ae=aj.nodeValue.length}}if(H(Y.parentNode)){Y=Y.parentNode}if(H(Y)){Y=Y.nextSibling||Y}if(H(aj.parentNode)){ae=c.nodeIndex(aj);aj=aj.parentNode}if(H(aj)&&aj.previousSibling){aj=aj.previousSibling;ae=aj.length}if(ag[0].inline){ac=ab(aj,ae);if(ac.node){while(ac.node&&ac.offset===0&&ac.node.previousSibling){ac=ab(ac.node.previousSibling)}if(ac.node&&ac.offset>0&&ac.node.nodeType===3&&ac.node.nodeValue.charAt(ac.offset-1)===" "){if(ac.offset>1){aj=ac.node;aj.splitText(ac.offset-1)}else{if(ac.node.previousSibling){aj=ac.node.previousSibling}}}}}if(ag[0].inline||ag[0].block_expand){Y=ah(Y,"firstChild","nextSibling");aj=ah(aj,"lastChild","previousSibling")}if(ag[0].selector&&ag[0].expand!==S&&!ag[0].inline){function aa(al,ak){var am,an,ap,ao;if(al.nodeType==3&&al.nodeValue.length==0&&al[ak]){al=al[ak]}am=m(al);for(an=0;anY?Y:Z]}return W}function Q(ad,Y,ac){var aa,X=P[ad],ae=P[ad=="apply"?"remove":"apply"];function af(){return P.apply.length||P.remove.length}function ab(){P.apply=[];P.remove=[]}function ag(ah){O(P.apply.reverse(),function(ai){T(ai.name,ai.vars,ah);if(ai.name==="forecolor"&&ai.vars.value){I(ah.parentNode)}});O(P.remove.reverse(),function(ai){A(ai.name,ai.vars,ah)});c.remove(ah,1);ab()}for(aa=X.length-1;aa>=0;aa--){if(X[aa].name==Y){return}}X.push({name:Y,vars:ac});for(aa=ae.length-1;aa>=0;aa--){if(ae[aa].name==Y){ae.splice(aa,1)}}if(af()){V.getDoc().execCommand("FontName",false,"mceinline");P.lastRng=q.getRng();O(c.select("font,span"),function(ai){var ah;if(b(ai)){ah=q.getBookmark();ag(ai);q.moveToBookmark(ah);V.nodeChanged()}});if(!P.isListening&&af()){P.isListening=true;function W(ai,aj){var ah=c.createRng();ag(ai);ah.setStart(aj,aj.nodeValue.length);ah.setEnd(aj,aj.nodeValue.length);q.setRng(ah);V.nodeChanged()}var Z=false;O("onKeyDown,onKeyUp,onKeyPress,onMouseUp".split(","),function(ah){V[ah].addToTop(function(ai,al){if(al.keyCode==13&&!al.shiftKey){Z=true;return}if(af()&&!a.dom.RangeUtils.compareRanges(P.lastRng,q.getRng())){var aj=false;O(c.select("font,span"),function(ao){var ap,an;if(b(ao)){aj=true;ap=ao.firstChild;while(ap&&ap.nodeType!=3){ap=ap.firstChild}if(ap){W(ao,ap)}else{c.remove(ao)}}});if(Z&&!aj){var ak=q.getNode();var am=ak;while(am&&am.nodeType!=3){am=am.firstChild}if(am){ak=am.parentNode;while(!F(ak)){ak=ak.parentNode}W(ak,am)}}if(al.type=="keyup"||al.type=="mouseup"){ab();Z=false}}})})}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_style_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype_src.js similarity index 99% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype_src.js index dbca5168ad59c..4037560feb679 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_prototype_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_prototype_src.js @@ -1089,6 +1089,10 @@ tinymce.create('static tinymce.util.XHR', { if (blockElm) { node = blockElm.firstChild; + // Ignore empty text nodes + while (node.nodeType == 3 && node.nodeValue.length == 0) + node = node.nextSibling; + if (node && node.nodeName === 'SPAN') { clonedSpan = node.cloneNode(false); } @@ -1100,11 +1104,7 @@ tinymce.create('static tinymce.util.XHR', { // Find all odd apple-style-spans blockElm = dom.getParent(rng.startContainer, dom.isBlock); tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) { - var rng = dom.createRng(); - - // Set range selection before the span we are about to remove - rng.setStartBefore(span); - rng.setEndBefore(span); + var bm = selection.getBookmark(); if (clonedSpan) { dom.replace(clonedSpan.cloneNode(false), span, true); @@ -1113,7 +1113,7 @@ tinymce.create('static tinymce.util.XHR', { } // Restore the selection - selection.setRng(rng); + selection.moveToBookmark(bm); }); } }); @@ -1132,19 +1132,69 @@ tinymce.create('static tinymce.util.XHR', { } }); }; - + + function inputMethodFocus(ed) { + ed.dom.bind(ed.getDoc(), 'focusin', function() { + ed.selection.setRng(ed.selection.getRng()); + }); + }; + + function focusBody(ed) { + // Fix for a focus bug in FF 3.x where the body element + // wouldn't get proper focus if the user clicked on the HTML element + if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 + ed.onMouseDown.add(function(ed, e) { + if (e.target.nodeName === "HTML") { + var body = ed.getBody(); + + // Blur the body it's focused but not correctly focused + body.blur(); + + // Refocus the body after a little while + setTimeout(function() { + body.focus(); + }, 0); + } + }); + } + }; + + function selectControlElements(ed) { + ed.onClick.add(function(ed, e) { + e = e.target; + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // Needs tobe the setBaseAndExtend or it will fail to select floated images + if (/^(IMG|HR)$/.test(e.nodeName)) + ed.selection.getSel().setBaseAndExtent(e, 0, e, 1); + + if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor')) + ed.selection.select(e); + + ed.nodeChanged(); + }); + }; + tinymce.create('tinymce.util.Quirks', { Quirks: function(ed) { - // Load WebKit specific fixed + // WebKit if (tinymce.isWebKit) { cleanupStylesWhenDeleting(ed); emptyEditorWhenDeleting(ed); + inputMethodFocus(ed); + selectControlElements(ed); } - // Load IE specific fixes + // IE if (tinymce.isIE) { emptyEditorWhenDeleting(ed); } + + // Gecko + if (tinymce.isGecko) { + focusBody(ed); + } } }); })(tinymce); @@ -9728,20 +9778,23 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }, selectByIndex : function(idx) { - var t = this, e, o; + var t = this, e, o, label; if (idx != t.selectedIndex) { e = DOM.get(t.id + '_text'); + label = DOM.get(t.id + '_voiceDesc'); o = t.items[idx]; if (o) { t.selectedValue = o.value; t.selectedIndex = idx; DOM.setHTML(e, DOM.encode(o.title)); + DOM.setHTML(label, t.settings.title + " - " + o.title); DOM.removeClass(e, 'mceTitle'); DOM.setAttrib(t.id, 'aria-valuenow', o.title); } else { DOM.setHTML(e, DOM.encode(t.settings.title)); + DOM.setHTML(label, DOM.encode(t.settings.title)); DOM.addClass(e, 'mceTitle'); t.selectedValue = t.selectedIndex = null; DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title); @@ -9770,7 +9823,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { renderHTML : function() { var h = '', t = this, s = t.settings, cp = t.classPrefix; - h = ''; + h = ''; h += ''; h += ''; @@ -9866,6 +9919,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { if (o.value === undefined) { m.add({ title : o.title, + role : "option", 'class' : 'mceMenuItemTitle', onclick : function() { if (t.settings.onselect('') !== false) @@ -9874,6 +9928,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }); } else { o.id = DOM.uniqueId(); + o.role= "option"; o.onclick = function() { if (t.settings.onselect(o.value) !== false) t.select(o.value); // Must be runned after @@ -10205,8 +10260,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { h += ''; h += ''; - h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); - return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); + h = DOM.createHTML('table', { role: 'presentation', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); + return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); }, postRender : function() { @@ -10478,7 +10533,8 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { }, focus : function() { - this.keyNav.focus(); + var t = this; + dom.get(t.id).focus(); }, postRender : function() { @@ -10496,6 +10552,10 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { root: t.id, items: items, onCancel: function() { + //Move focus if webkit so that navigation back will read the item. + if (tinymce.isWebKit) { + dom.get(t.editor.id+"_ifr").focus(); + } t.editor.focus(); }, excludeFromTabOrder: !t.settings.tab_focus_toolbar @@ -11426,6 +11486,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.iframeHTML += ''; + // Load the CSS by injecting them into the HTML this will reduce "flicker" + for (i = 0; i < t.contentCSS.length; i++) { + t.iframeHTML += ''; + } + bi = s.body_id || 'tinymce'; if (bi.indexOf('=') != -1) { bi = t.getParam('body_id', '', 'hash'); @@ -11443,7 +11508,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Domain relaxing enabled, then set document domain if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) { // We need to write the contents here in IE since multiple writes messes up refresh button and back button - u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; + u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; } // Create iframe @@ -11477,24 +11542,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Setup iframe body if (!isIE || !tinymce.relaxedDomain) { - // Fix for a focus bug in FF 3.x where the body element - // wouldn't get proper focus if the user clicked on the HTML element - if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 - t.onMouseDown.add(function(ed, e) { - if (e.target.nodeName === "HTML") { - var body = t.getBody(); - - // Blur the body it's focused but not correctly focused - body.blur(); - - // Refocus the body after a little while - setTimeout(function() { - body.focus(); - }, 0); - } - }); - } - d.open(); d.write(t.iframeHTML); d.close(); @@ -12681,21 +12728,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.onMouseDown.add(setOpts); } - t.onClick.add(function(ed, e) { - e = e.target; - - // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 - // WebKit can't even do simple things like selecting an image - // Needs tobe the setBaseAndExtend or it will fail to select floated images - if (tinymce.isWebKit && e.nodeName == 'IMG') - t.selection.getSel().setBaseAndExtent(e, 0, e, 1); - - if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) - t.selection.select(e); - - t.nodeChanged(); - }); - // Add node change handlers t.onMouseUp.add(t.nodeChanged); //t.onClick.add(t.nodeChanged); @@ -14448,7 +14480,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { id = t.prefix + id; - if (ed.settings.use_native_selects) + + function useNativeListForAccessibility(ed) { + return ed.settings.use_accessible_selects && !tinymce.isGecko + } + + if (ed.settings.use_native_selects || useNativeListForAccessibility(ed)) c = new tinymce.ui.NativeListBox(id, s); else { cls = cc || t._cls.listbox || tinymce.ui.ListBox; @@ -15464,7 +15501,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { selection.moveToBookmark(bookmark); // Check if start element still has formatting then we are at: "text|text" and need to move the start into the next text node - if (match(name, vars, selection.getStart())) { + if (format.inline && match(name, vars, selection.getStart())) { moveStart(selection.getRng(true)); } diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_src.js b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_src.js similarity index 99% rename from lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_src.js rename to lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_src.js index 5ea751901a13d..94ac101690ee8 100644 --- a/lib/editor/tinymce/tiny_mce/3.4.5/tiny_mce_src.js +++ b/lib/editor/tinymce/tiny_mce/3.4.6/tiny_mce_src.js @@ -1062,6 +1062,10 @@ tinymce.create('static tinymce.util.XHR', { if (blockElm) { node = blockElm.firstChild; + // Ignore empty text nodes + while (node.nodeType == 3 && node.nodeValue.length == 0) + node = node.nextSibling; + if (node && node.nodeName === 'SPAN') { clonedSpan = node.cloneNode(false); } @@ -1073,11 +1077,7 @@ tinymce.create('static tinymce.util.XHR', { // Find all odd apple-style-spans blockElm = dom.getParent(rng.startContainer, dom.isBlock); tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) { - var rng = dom.createRng(); - - // Set range selection before the span we are about to remove - rng.setStartBefore(span); - rng.setEndBefore(span); + var bm = selection.getBookmark(); if (clonedSpan) { dom.replace(clonedSpan.cloneNode(false), span, true); @@ -1086,7 +1086,7 @@ tinymce.create('static tinymce.util.XHR', { } // Restore the selection - selection.setRng(rng); + selection.moveToBookmark(bm); }); } }); @@ -1105,19 +1105,69 @@ tinymce.create('static tinymce.util.XHR', { } }); }; - + + function inputMethodFocus(ed) { + ed.dom.bind(ed.getDoc(), 'focusin', function() { + ed.selection.setRng(ed.selection.getRng()); + }); + }; + + function focusBody(ed) { + // Fix for a focus bug in FF 3.x where the body element + // wouldn't get proper focus if the user clicked on the HTML element + if (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 + ed.onMouseDown.add(function(ed, e) { + if (e.target.nodeName === "HTML") { + var body = ed.getBody(); + + // Blur the body it's focused but not correctly focused + body.blur(); + + // Refocus the body after a little while + setTimeout(function() { + body.focus(); + }, 0); + } + }); + } + }; + + function selectControlElements(ed) { + ed.onClick.add(function(ed, e) { + e = e.target; + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // Needs tobe the setBaseAndExtend or it will fail to select floated images + if (/^(IMG|HR)$/.test(e.nodeName)) + ed.selection.getSel().setBaseAndExtent(e, 0, e, 1); + + if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor')) + ed.selection.select(e); + + ed.nodeChanged(); + }); + }; + tinymce.create('tinymce.util.Quirks', { Quirks: function(ed) { - // Load WebKit specific fixed + // WebKit if (tinymce.isWebKit) { cleanupStylesWhenDeleting(ed); emptyEditorWhenDeleting(ed); + inputMethodFocus(ed); + selectControlElements(ed); } - // Load IE specific fixes + // IE if (tinymce.isIE) { emptyEditorWhenDeleting(ed); } + + // Gecko + if (tinymce.isGecko) { + focusBody(ed); + } } }); })(tinymce); @@ -9701,20 +9751,23 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }, selectByIndex : function(idx) { - var t = this, e, o; + var t = this, e, o, label; if (idx != t.selectedIndex) { e = DOM.get(t.id + '_text'); + label = DOM.get(t.id + '_voiceDesc'); o = t.items[idx]; if (o) { t.selectedValue = o.value; t.selectedIndex = idx; DOM.setHTML(e, DOM.encode(o.title)); + DOM.setHTML(label, t.settings.title + " - " + o.title); DOM.removeClass(e, 'mceTitle'); DOM.setAttrib(t.id, 'aria-valuenow', o.title); } else { DOM.setHTML(e, DOM.encode(t.settings.title)); + DOM.setHTML(label, DOM.encode(t.settings.title)); DOM.addClass(e, 'mceTitle'); t.selectedValue = t.selectedIndex = null; DOM.setAttrib(t.id, 'aria-valuenow', t.settings.title); @@ -9743,7 +9796,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { renderHTML : function() { var h = '', t = this, s = t.settings, cp = t.classPrefix; - h = ''; + h = ''; h += ''; h += ''; @@ -9839,6 +9892,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { if (o.value === undefined) { m.add({ title : o.title, + role : "option", 'class' : 'mceMenuItemTitle', onclick : function() { if (t.settings.onselect('') !== false) @@ -9847,6 +9901,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { }); } else { o.id = DOM.uniqueId(); + o.role= "option"; o.onclick = function() { if (t.settings.onselect(o.value) !== false) t.select(o.value); // Must be runned after @@ -10178,8 +10233,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { h += ''; h += ''; - h = DOM.createHTML('table', {id : t.id, role: 'presentation', tabindex: '0', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); - return DOM.createHTML('span', {role: 'button', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); + h = DOM.createHTML('table', { role: 'presentation', 'class' : 'mceSplitButton mceSplitButtonEnabled ' + s['class'], cellpadding : '0', cellspacing : '0', title : s.title}, h); + return DOM.createHTML('div', {id : t.id, role: 'button', tabindex: '0', 'aria-labelledby': t.id + '_voice', 'aria-haspopup': 'true'}, h); }, postRender : function() { @@ -10451,7 +10506,8 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { }, focus : function() { - this.keyNav.focus(); + var t = this; + dom.get(t.id).focus(); }, postRender : function() { @@ -10469,6 +10525,10 @@ tinymce.create('tinymce.ui.ToolbarGroup:tinymce.ui.Container', { root: t.id, items: items, onCancel: function() { + //Move focus if webkit so that navigation back will read the item. + if (tinymce.isWebKit) { + dom.get(t.editor.id+"_ifr").focus(); + } t.editor.focus(); }, excludeFromTabOrder: !t.settings.tab_focus_toolbar @@ -11399,6 +11459,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.iframeHTML += ''; + // Load the CSS by injecting them into the HTML this will reduce "flicker" + for (i = 0; i < t.contentCSS.length; i++) { + t.iframeHTML += ''; + } + bi = s.body_id || 'tinymce'; if (bi.indexOf('=') != -1) { bi = t.getParam('body_id', '', 'hash'); @@ -11416,7 +11481,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Domain relaxing enabled, then set document domain if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) { // We need to write the contents here in IE since multiple writes messes up refresh button and back button - u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; + u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'; } // Create iframe @@ -11450,24 +11515,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { // Setup iframe body if (!isIE || !tinymce.relaxedDomain) { - // Fix for a focus bug in FF 3.x where the body element - // wouldn't get proper focus if the user clicked on the HTML element - if (isGecko && !Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 - t.onMouseDown.add(function(ed, e) { - if (e.target.nodeName === "HTML") { - var body = t.getBody(); - - // Blur the body it's focused but not correctly focused - body.blur(); - - // Refocus the body after a little while - setTimeout(function() { - body.focus(); - }, 0); - } - }); - } - d.open(); d.write(t.iframeHTML); d.close(); @@ -12654,21 +12701,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { t.onMouseDown.add(setOpts); } - t.onClick.add(function(ed, e) { - e = e.target; - - // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 - // WebKit can't even do simple things like selecting an image - // Needs tobe the setBaseAndExtend or it will fail to select floated images - if (tinymce.isWebKit && e.nodeName == 'IMG') - t.selection.getSel().setBaseAndExtent(e, 0, e, 1); - - if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) - t.selection.select(e); - - t.nodeChanged(); - }); - // Add node change handlers t.onMouseUp.add(t.nodeChanged); //t.onClick.add(t.nodeChanged); @@ -14421,7 +14453,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { id = t.prefix + id; - if (ed.settings.use_native_selects) + + function useNativeListForAccessibility(ed) { + return ed.settings.use_accessible_selects && !tinymce.isGecko + } + + if (ed.settings.use_native_selects || useNativeListForAccessibility(ed)) c = new tinymce.ui.NativeListBox(id, s); else { cls = cc || t._cls.listbox || tinymce.ui.ListBox; @@ -15437,7 +15474,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { selection.moveToBookmark(bookmark); // Check if start element still has formatting then we are at: "text|text" and need to move the start into the next text node - if (match(name, vars, selection.getStart())) { + if (format.inline && match(name, vars, selection.getStart())) { moveStart(selection.getRng(true)); } diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/utils/editable_selects.js b/lib/editor/tinymce/tiny_mce/3.4.6/utils/editable_selects.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/utils/editable_selects.js rename to lib/editor/tinymce/tiny_mce/3.4.6/utils/editable_selects.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/utils/form_utils.js b/lib/editor/tinymce/tiny_mce/3.4.6/utils/form_utils.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/utils/form_utils.js rename to lib/editor/tinymce/tiny_mce/3.4.6/utils/form_utils.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/utils/mctabs.js b/lib/editor/tinymce/tiny_mce/3.4.6/utils/mctabs.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/utils/mctabs.js rename to lib/editor/tinymce/tiny_mce/3.4.6/utils/mctabs.js diff --git a/lib/editor/tinymce/tiny_mce/3.4.5/utils/validate.js b/lib/editor/tinymce/tiny_mce/3.4.6/utils/validate.js similarity index 100% rename from lib/editor/tinymce/tiny_mce/3.4.5/utils/validate.js rename to lib/editor/tinymce/tiny_mce/3.4.6/utils/validate.js diff --git a/lib/editor/tinymce/version.php b/lib/editor/tinymce/version.php index 22b23026938ec..86f99c60ef143 100644 --- a/lib/editor/tinymce/version.php +++ b/lib/editor/tinymce/version.php @@ -26,6 +26,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011091400; +$plugin->version = 2011101600; $plugin->requires = 2011090700; -$plugin->release = '3.4.5'; +$plugin->release = '3.4.6'; diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml index 04a9d0b234de7..0acdd8fad8dff 100644 --- a/lib/thirdpartylibs.xml +++ b/lib/thirdpartylibs.xml @@ -32,7 +32,7 @@ editor/tinymce/tiny_mceTinyMCELGPL - 3.4.5 + 3.4.62.1+ From 626509d478b5d28f84564b8fdfa2b172bb3a991f Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Sun, 16 Oct 2011 17:37:12 +0200 Subject: [PATCH 39/67] MDL-29785 enable TinyMCE on iOS 5 devices --- lib/editor/tinymce/lib.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/editor/tinymce/lib.php b/lib/editor/tinymce/lib.php index 473251e0e82a4..c3c68d875fd6b 100644 --- a/lib/editor/tinymce/lib.php +++ b/lib/editor/tinymce/lib.php @@ -46,6 +46,9 @@ public function supported_by_browser() { if (check_browser_version('Opera', 9)) { return true; } + if (check_browser_version('Safari iOS', 534)) { + return true; + } return false; } From 39b3ee70c4db5fd7b5e07de5f9b0aee4045bae8d Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Sun, 16 Oct 2011 17:51:59 +0200 Subject: [PATCH 40/67] MDL-29768 debugstringids new help lang string now used --- admin/settings/development.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/settings/development.php b/admin/settings/development.php index 609fadb813ce0..192df5b8d3018 100644 --- a/admin/settings/development.php +++ b/admin/settings/development.php @@ -26,7 +26,7 @@ $temp->add(new admin_setting_configcheckbox('xmlstrictheaders', get_string('xmlstrictheaders', 'admin'), get_string('configxmlstrictheaders', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('debugsmtp', get_string('debugsmtp', 'admin'), get_string('configdebugsmtp', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('perfdebug', get_string('perfdebug', 'admin'), get_string('configperfdebug', 'admin'), '7', '15', '7')); - $temp->add(new admin_setting_configcheckbox('debugstringids', get_string('debugstringids', 'admin'), get_string('configdebugstringids', 'admin'), 0)); + $temp->add(new admin_setting_configcheckbox('debugstringids', get_string('debugstringids', 'admin'), get_string('debugstringids_desc', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('debugvalidators', get_string('debugvalidators', 'admin'), get_string('configdebugvalidators', 'admin'), 0)); $temp->add(new admin_setting_configcheckbox('debugpageinfo', get_string('debugpageinfo', 'admin'), get_string('configdebugpageinfo', 'admin'), 0)); $ADMIN->add('development', $temp); From e4784b616a25649ee7e665f66d931bd9cbf1bae8 Mon Sep 17 00:00:00 2001 From: Stephen Bourget Date: Fri, 7 Oct 2011 12:17:23 -0400 Subject: [PATCH 41/67] MDL-29698 Assignment: Fix broken logentry --- mod/assignment/type/upload/assignment.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/assignment/type/upload/assignment.class.php b/mod/assignment/type/upload/assignment.class.php index b909f25a407ba..a7115700cc4ee 100644 --- a/mod/assignment/type/upload/assignment.class.php +++ b/mod/assignment/type/upload/assignment.class.php @@ -730,7 +730,7 @@ function unfinalize($forcemode=null) { $updated->data2 = ''; $DB->update_record('assignment_submissions', $updated); //TODO: add unfinalize action to log - add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->assignment->id, $this->assignment->id, $this->cm->id); + add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id.'&userid='.$userid.'&mode='.$mode.'&offset='.$offset, $this->assignment->id, $this->cm->id); $submission = $this->get_submission($userid); $this->update_grade($submission); } From 6b3e5ab959179c4512f4159a54331ca71a80348c Mon Sep 17 00:00:00 2001 From: Sun Zhigang Date: Wed, 31 Aug 2011 16:36:31 +0800 Subject: [PATCH 42/67] MDL-29191 - Show creator's fullname instead of username --- mod/wiki/pagelib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/wiki/pagelib.php b/mod/wiki/pagelib.php index 59690f7aa50e7..7fa366f9ab8e9 100644 --- a/mod/wiki/pagelib.php +++ b/mod/wiki/pagelib.php @@ -1218,7 +1218,7 @@ private function print_history_content() { $creator = wiki_get_user_info($version0page->userid); $a = new StdClass; $a->date = userdate($this->page->timecreated, get_string('strftimedaydatetime', 'langconfig')); - $a->username = $creator->username; + $a->username = fullname($creator); echo $OUTPUT->heading(get_string('createddate', 'wiki', $a), 4, 'wiki_headingtime'); if ($vcount > 0) { From 140af2a0f0a4598bf568b9ae182cb81eb583edeb Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Sun, 16 Oct 2011 19:01:57 +0200 Subject: [PATCH 43/67] MDL-29191 wiki - avoid one more username leak --- mod/wiki/pagelib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/wiki/pagelib.php b/mod/wiki/pagelib.php index 7fa366f9ab8e9..4e19f57bee9d8 100644 --- a/mod/wiki/pagelib.php +++ b/mod/wiki/pagelib.php @@ -2545,7 +2545,7 @@ private function print_delete_version() { $creator = wiki_get_user_info($version0page->userid); $a = new stdClass(); $a->date = userdate($this->page->timecreated, get_string('strftimedaydatetime', 'langconfig')); - $a->username = $creator->username; + $a->username = fullname($creator); echo $OUTPUT->heading(get_string('createddate', 'wiki', $a), 4, 'wiki_headingtime'); if ($versioncount > 0) { /// If there is only one version, we don't need radios nor forms From f35c45006ae4d6764bcdc9fc8ff946009ab1bf2d Mon Sep 17 00:00:00 2001 From: Sam Hemelryk Date: Mon, 17 Oct 2011 11:12:33 +1300 Subject: [PATCH 44/67] MDL-19488 mod_feedback: Coding style improvement --- mod/feedback/delete_template.php | 54 ++++++++++++++++---------------- mod/feedback/edit_form.php | 2 -- mod/feedback/lib.php | 2 +- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/mod/feedback/delete_template.php b/mod/feedback/delete_template.php index e9685083edce9..6adb1baa1e8b4 100644 --- a/mod/feedback/delete_template.php +++ b/mod/feedback/delete_template.php @@ -95,7 +95,7 @@ /// Print the page header $strfeedbacks = get_string("modulenameplural", "feedback"); $strfeedback = get_string("modulename", "feedback"); -$str_delete_feedback = get_string('delete_template','feedback'); +$strdeletefeedback = get_string('delete_template','feedback'); $PAGE->set_heading(format_string($course->fullname)); $PAGE->set_title(format_string($feedback->name)); @@ -108,7 +108,7 @@ /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// -echo $OUTPUT->heading($str_delete_feedback); +echo $OUTPUT->heading($strdeletefeedback); if($shoulddelete == 1) { echo $OUTPUT->box_start('generalbox errorboxcontent boxaligncenter boxwidthnormal'); @@ -125,17 +125,17 @@ echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal'); $tablecolumns = array('template', 'action'); $tableheaders = array(get_string('template', 'feedback'), ''); - $table_course = new flexible_table('feedback_template_course_table'); + $tablecourse = new flexible_table('feedback_template_course_table'); - $table_course->define_columns($tablecolumns); - $table_course->define_headers($tableheaders); - $table_course->define_baseurl($deleteurl); - $table_course->column_style('action', 'width', '10%'); + $tablecourse->define_columns($tablecolumns); + $tablecourse->define_headers($tableheaders); + $tablecourse->define_baseurl($deleteurl); + $tablecourse->column_style('action', 'width', '10%'); - $table_course->sortable(false); - $table_course->set_attribute('width', '100%'); - $table_course->set_attribute('class', 'generaltable'); - $table_course->setup(); + $tablecourse->sortable(false); + $tablecourse->set_attribute('width', '100%'); + $tablecourse->set_attribute('class', 'generaltable'); + $tablecourse->setup(); foreach($templates as $template) { $data = array(); @@ -146,10 +146,10 @@ 'shoulddelete'=>1, )); - $data[] = $OUTPUT->single_button($url, $str_delete_feedback, 'post'); - $table_course->add_data($data); + $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post'); + $tablecourse->add_data($data); } - $table_course->finish_output(); + $tablecourse->finish_output(); echo $OUTPUT->box_end(); } //now we get the public templates if it is permitted @@ -162,19 +162,19 @@ }else { echo $OUTPUT->heading(get_string('public', 'feedback'), 3); echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal'); - $tablecolumns = $tablecolumns = array('template', 'action'); + $tablecolumns = array('template', 'action'); $tableheaders = array(get_string('template', 'feedback'), ''); - $table_public = new flexible_table('feedback_template_public_table'); + $tablepublic = new flexible_table('feedback_template_public_table'); - $table_public->define_columns($tablecolumns); - $table_public->define_headers($tableheaders); - $table_public->define_baseurl($deleteurl); - $table_public->column_style('action', 'width', '10%'); + $tablepublic->define_columns($tablecolumns); + $tablepublic->define_headers($tableheaders); + $tablepublic->define_baseurl($deleteurl); + $tablepublic->column_style('action', 'width', '10%'); - $table_public->sortable(false); - $table_public->set_attribute('width', '100%'); - $table_public->set_attribute('class', 'generaltable'); - $table_public->setup(); + $tablepublic->sortable(false); + $tablepublic->set_attribute('width', '100%'); + $tablepublic->set_attribute('class', 'generaltable'); + $tablepublic->setup(); // echo $OUTPUT->heading(get_string('public', 'feedback'), 3); @@ -188,10 +188,10 @@ 'shoulddelete'=>1, )); - $data[] = $OUTPUT->single_button($url, $str_delete_feedback, 'post'); - $table_public->add_data($data); + $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post'); + $tablepublic->add_data($data); } - $table_public->finish_output(); + $tablepublic->finish_output(); echo $OUTPUT->box_end(); } } diff --git a/mod/feedback/edit_form.php b/mod/feedback/edit_form.php index 0d69d04bb6d15..0af28680fb536 100644 --- a/mod/feedback/edit_form.php +++ b/mod/feedback/edit_form.php @@ -131,8 +131,6 @@ function set_feedbackdata($data) { } function set_form_elements(){ - global $CFG; - $mform =& $this->_form; // $capabilities = $this->feedbackdata->capabilities; diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 0342cf6f6e60c..8b8d2c3a4c2f9 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -220,7 +220,7 @@ function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedow } } - if ($context->contextlevel == CONTEXT_COURSE) { + if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_SYSTEM) { if ($filearea !== 'template') { return false; } From 77c36d4ce8b408838eca85671f8c689d54945060 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 17 Oct 2011 02:09:46 +0200 Subject: [PATCH 45/67] MDL-28170 environment - add new 2.2 section nothing changes from 2.1 as far as it was agreed that this will be the last version supporting 1.9 upgrade. See issue for details. --- admin/environment.xml | 114 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/admin/environment.xml b/admin/environment.xml index 25d3c139bf3c0..a064ba8bfd074 100644 --- a/admin/environment.xml +++ b/admin/environment.xml @@ -442,4 +442,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 22ca3e49e00de03f38cc38ce7c147e57039f56c7 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 17 Oct 2011 10:19:59 +0200 Subject: [PATCH 46/67] NOBUG - whitespace fixes (MDL-19488, MDL-27242, MDL-29435) --- lib/db/upgrade.php | 6 +++--- mod/feedback/delete_template.php | 13 ++++++------- mod/feedback/edit.php | 2 +- mod/feedback/lib.php | 12 ++++++------ webservice/xmlrpc/locallib.php | 4 ++-- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index cea5a0f9e907f..a100802e516b4 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -6796,12 +6796,12 @@ function xmldb_main_upgrade($oldversion) { // The conditional availability date system used to rely on dates being // set to 23:59:59 for the end date, but now that exact times are // supported, it uses midnight on the following day. - + // The query is restricted on 'time mod 10 = 9' in order that // it is safe to run this upgrade twice if something goes wrong. $DB->execute('UPDATE {course_modules} SET availableuntil = availableuntil + 1 ' . 'WHERE availableuntil > 0 AND ' . $DB->sql_modulo('availableuntil', 10) . ' = 9'); - + // Because availableuntil is stored in modinfo, we need to clear modinfo // for all courses. rebuild_course_cache(0, true); @@ -6809,7 +6809,7 @@ function xmldb_main_upgrade($oldversion) { // Main savepoint reached upgrade_main_savepoint(true, 2011101200.01); } - + return true; } diff --git a/mod/feedback/delete_template.php b/mod/feedback/delete_template.php index 6adb1baa1e8b4..f108a276c1528 100644 --- a/mod/feedback/delete_template.php +++ b/mod/feedback/delete_template.php @@ -81,13 +81,13 @@ if(!$template = $DB->get_record("feedback_template", array("id"=>$deletetempl))) { print_error('error'); } - + if($template->ispublic) { $systemcontext = get_system_context(); require_capability('mod/feedback:createpublictemplate', $systemcontext); require_capability('mod/feedback:deletetemplate', $systemcontext); } - + feedback_delete_template($template); redirect($deleteurl->out(false)); } @@ -145,7 +145,7 @@ 'deletetempl'=>$template->id, 'shoulddelete'=>1, )); - + $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post'); $tablecourse->add_data($data); } @@ -176,7 +176,6 @@ $tablepublic->set_attribute('class', 'generaltable'); $tablepublic->setup(); - // echo $OUTPUT->heading(get_string('public', 'feedback'), 3); // echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide'); foreach($templates as $template) { @@ -187,7 +186,7 @@ 'deletetempl'=>$template->id, 'shoulddelete'=>1, )); - + $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post'); $tablepublic->add_data($data); } @@ -195,13 +194,13 @@ echo $OUTPUT->box_end(); } } - + echo $OUTPUT->box_start('boxaligncenter boxwidthnormal'); $url = new moodle_url($deleteurl, array( 'id'=>$id, 'canceldelete'=>1, )); - + echo $OUTPUT->single_button($url, get_string('back'), 'post'); echo $OUTPUT->box_end(); } diff --git a/mod/feedback/edit.php b/mod/feedback/edit.php index c0bc9951df77a..98632b12c6e83 100644 --- a/mod/feedback/edit.php +++ b/mod/feedback/edit.php @@ -209,7 +209,7 @@ if(is_array($feedbackitems)){ $itemnr = 0; - + $align = right_to_left() ? 'right' : 'left'; $helpbutton = $OUTPUT->help_icon('preview', 'feedback'); diff --git a/mod/feedback/lib.php b/mod/feedback/lib.php index 8b8d2c3a4c2f9..2b7a8f5cced45 100644 --- a/mod/feedback/lib.php +++ b/mod/feedback/lib.php @@ -158,7 +158,7 @@ function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedow if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) { return false; } - + //if the filearea is "item" so we check the permissions like view/complete the feedback if($filearea === 'item') { //get the feedback @@ -171,12 +171,12 @@ function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedow if(has_capability('mod/feedback:complete', $context)) { $canload = true; } - + //now we check whether the user has the view capability if(has_capability('mod/feedback:view', $context)) { $canload = true; } - + //if the feedback is on frontpage and anonymous and the fullanonymous is allowed //so the file can be loaded too. if(isset($CFG->feedback_allowfullanonymous) @@ -193,7 +193,7 @@ function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedow if(!$template = $DB->get_record('feedback_template', array('id'=>$item->template))) { return false; } - + //if the file is not public so the capability edititems has to be there if(!$template->ispublic) { if(!has_capability('mod/feedback:edititems', $context)) { @@ -1176,7 +1176,7 @@ function feedback_items_from_template($feedback, $templateid, $deleteold = false $item->position = $item->position + $positionoffset; $item->id = $DB->insert_record('feedback_item', $item); - + //TODO: moving the files to the new items if ($templatefiles = $fs->get_area_files($s_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) { foreach($templatefiles as $tfile) { @@ -1409,7 +1409,7 @@ function feedback_delete_item($itemid, $renumber = true, $template = false){ //deleting the files from the item $fs = get_file_storage(); - + if($template) { if($template->ispublic) { $context = get_system_context(); diff --git a/webservice/xmlrpc/locallib.php b/webservice/xmlrpc/locallib.php index eeab1895c0e2d..d94f249505dd3 100644 --- a/webservice/xmlrpc/locallib.php +++ b/webservice/xmlrpc/locallib.php @@ -62,7 +62,7 @@ public function fault($fault = null, $code = 404) class webservice_xmlrpc_server extends webservice_zend_server { /** * Contructor - * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_* + * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_* */ public function __construct($authmethod) { require_once 'Zend/XmlRpc/Server.php'; @@ -106,4 +106,4 @@ public function simpletest($serverurl, $function, $params) { $client = new Zend_XmlRpc_Client($serverurl); return $client->call($function, $params); } -} \ No newline at end of file +} From 7fb46992661e2eb1fb7bfd1a6d3f7f1ccf8fd306 Mon Sep 17 00:00:00 2001 From: sam marshall Date: Mon, 10 Oct 2011 13:50:01 +0100 Subject: [PATCH 47/67] MDL-29719 Option to display course shortname alongside fullname on course lists --- admin/settings/appearance.php | 5 ++++- course/category.php | 3 ++- course/lib.php | 23 +++++++++++++++++++++-- course/simpletest/testcourselib.php | 20 ++++++++++++++++++++ lang/en/admin.php | 2 ++ 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 1646cab08f2f5..7da57866803b6 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -118,8 +118,11 @@ $ADMIN->add('appearance', $temp); // coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc. - $temp = new admin_settingpage('coursecontact', get_string('coursecontact', 'admin')); + $temp = new admin_settingpage('coursecontact', get_string('courses')); $temp->add(new admin_setting_special_coursecontact()); + $temp->add(new admin_setting_configcheckbox('courselistshortnames', + get_string('courselistshortnames', 'admin'), + get_string('courselistshortnames_desc', 'admin'), 0)); $ADMIN->add('appearance', $temp); $temp = new admin_settingpage('ajax', get_string('ajaxuse')); diff --git a/course/category.php b/course/category.php index 9ed7ebca867d7..0fb8ac571cf95 100644 --- a/course/category.php +++ b/course/category.php @@ -314,7 +314,8 @@ $linkcss = $acourse->visible ? '' : ' class="dimmed" '; echo ''; - echo ''; + $coursename = get_course_display_name_for_list($course); + echo ''; if ($editingon) { echo ''; - $coursename = get_course_display_name_for_list($course); + $coursename = get_course_display_name_for_list($acourse); echo ''; if ($editingon) { echo '