diff --git a/ChangeLog b/ChangeLog
index 62a33294b4..d9d455ec7b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -22,10 +22,16 @@ phpMyAdmin - ChangeLog
- Patch #3316969 PMA_ajaxShowMessage() does not respect timeout
+ AJAX for Change on multiple rows in table Browse
+ [interface] Improved support for stored routines
++ [display] More options for browsing GIS data
++ [interface] Support for spatial indexes
++ [display] GIS data visualization
++ AJAX for table structure multiple-columns change
3.4.4.0 (not yet released)
+- bug #3323060 [parser] SQL parser breaks AJAX requests if query has unclosed quotes
+- bug #3323101 [parser] Invalid escape sequence in SQL parser
-3.4.3.0 (not yet released)
+3.4.3.0 (2011-06-27)
- bug #3311170 [sync] Missing helper icons in Synchronize
- patch #3304473 [setup] Redefine a lable that was wrong
- bug #3304544 [parser] master is not a reserved word
diff --git a/db_events.php b/db_events.php
index 6b39b83d85..7d22f5d8c5 100644
--- a/db_events.php
+++ b/db_events.php
@@ -1,7 +1,9 @@
databases->build();
}
- if (PMA_MYSQL_INT_VERSION >= 50000) {
- // here I don't use DELIMITER because it's not part of the
- // language; I have to send each statement one by one
+ // here I don't use DELIMITER because it's not part of the
+ // language; I have to send each statement one by one
- // to avoid selecting alternatively the current and new db
- // we would need to modify the CREATE definitions to qualify
- // the db name
- $procedure_names = PMA_DBI_get_procedures_or_functions($db, 'PROCEDURE');
- if ($procedure_names) {
- foreach($procedure_names as $procedure_name) {
- PMA_DBI_select_db($db);
- $tmp_query = PMA_DBI_get_definition($db, 'PROCEDURE', $procedure_name);
- // collect for later display
- $GLOBALS['sql_query'] .= "\n" . $tmp_query;
- PMA_DBI_select_db($newname);
- PMA_DBI_query($tmp_query);
- }
- }
-
- $function_names = PMA_DBI_get_procedures_or_functions($db, 'FUNCTION');
- if ($function_names) {
- foreach($function_names as $function_name) {
- PMA_DBI_select_db($db);
- $tmp_query = PMA_DBI_get_definition($db, 'FUNCTION', $function_name);
- // collect for later display
- $GLOBALS['sql_query'] .= "\n" . $tmp_query;
- PMA_DBI_select_db($newname);
- PMA_DBI_query($tmp_query);
- }
+ // to avoid selecting alternatively the current and new db
+ // we would need to modify the CREATE definitions to qualify
+ // the db name
+ $procedure_names = PMA_DBI_get_procedures_or_functions($db, 'PROCEDURE');
+ if ($procedure_names) {
+ foreach($procedure_names as $procedure_name) {
+ PMA_DBI_select_db($db);
+ $tmp_query = PMA_DBI_get_definition($db, 'PROCEDURE', $procedure_name);
+ // collect for later display
+ $GLOBALS['sql_query'] .= "\n" . $tmp_query;
+ PMA_DBI_select_db($newname);
+ PMA_DBI_query($tmp_query);
}
}
+
+ $function_names = PMA_DBI_get_procedures_or_functions($db, 'FUNCTION');
+ if ($function_names) {
+ foreach($function_names as $function_name) {
+ PMA_DBI_select_db($db);
+ $tmp_query = PMA_DBI_get_definition($db, 'FUNCTION', $function_name);
+ // collect for later display
+ $GLOBALS['sql_query'] .= "\n" . $tmp_query;
+ PMA_DBI_select_db($newname);
+ PMA_DBI_query($tmp_query);
+ }
+ }
+
// go back to current db, just in case
PMA_DBI_select_db($db);
@@ -233,7 +232,7 @@ if (strlen($db) && (! empty($db_rename) || ! empty($db_copy))) {
// to avoid selecting alternatively the current and new db
// we would need to modify the CREATE definitions to qualify
// the db name
- $event_names = PMA_DBI_fetch_result('SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA= \'' . PMA_sqlAddslashes($db,true) . '\';');
+ $event_names = PMA_DBI_fetch_result('SELECT EVENT_NAME FROM information_schema.EVENTS WHERE EVENT_SCHEMA= \'' . PMA_sqlAddSlashes($db,true) . '\';');
if ($event_names) {
foreach($event_names as $event_name) {
PMA_DBI_select_db($db);
@@ -244,9 +243,10 @@ if (strlen($db) && (! empty($db_rename) || ! empty($db_copy))) {
PMA_DBI_query($tmp_query);
}
}
- }
- // go back to current db, just in case
- PMA_DBI_select_db($db);
+ }
+
+ // go back to current db, just in case
+ PMA_DBI_select_db($db);
// Duplicate the bookmarks for this db (done once for each db)
if (! $_error && $db != $newname) {
@@ -586,7 +586,7 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?>
$test_query = '
SELECT *
FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) . '
- WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'';
+ WHERE db_name = \'' . PMA_sqlAddSlashes($db) . '\'';
$test_rs = PMA_query_as_controluser($test_query, null, PMA_DBI_QUERY_STORE);
/*
diff --git a/db_printview.php b/db_printview.php
index 7e3c709935..e25341128f 100644
--- a/db_printview.php
+++ b/db_printview.php
@@ -53,7 +53,7 @@ if ($cfg['SkipLockedTables'] == true) {
if ($result != false && PMA_DBI_num_rows($result) > 0) {
while ($tmp = PMA_DBI_fetch_row($result)) {
if (! isset($sot_cache[$tmp[0]])) {
- $sts_result = PMA_DBI_query('SHOW TABLE STATUS FROM ' . PMA_backquote($db) . ' LIKE \'' . addslashes($tmp[0]) . '\';');
+ $sts_result = PMA_DBI_query('SHOW TABLE STATUS FROM ' . PMA_backquote($db) . ' LIKE \'' . PMA_sqlAddSlashes($tmp[0], true) . '\';');
$sts_tmp = PMA_DBI_fetch_assoc($sts_result);
$tables[] = $sts_tmp;
} else { // table in use
diff --git a/db_routines.php b/db_routines.php
index 1d9523ecd3..b9417fdc18 100644
--- a/db_routines.php
+++ b/db_routines.php
@@ -1,6 +1,8 @@
';
- $message .= sprintf(__('%s row(s) affected by the last statement inside the procedure'), $affected);
+ $message .= sprintf(_ngettext('%d row affected by the last statement inside the procedure', '%d rows affected by the last statement inside the procedure', $affected), $affected);
}
$message = PMA_message::success($message);
// Pass the SQL queries through the "pretty printer"
@@ -187,7 +189,7 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
}
} else {
$output = '';
- $message = PMA_message::error(sprintf(__('Query "%s" failed'), $query) . '
'
+ $message = PMA_message::error(sprintf(__('The following query has failed: "%s"'), $query) . '
'
. __('MySQL said: ') . PMA_DBI_getError(null));
}
// Print/send output
@@ -206,7 +208,7 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
}
} else {
$message = __('Error in processing request') . ' : '
- . sprintf(__('No routine with name %s found in database %s'),
+ . sprintf(__('No routine with name %1$s found in database %2$s'),
htmlspecialchars(PMA_backquote($_REQUEST['routine_name'])),
htmlspecialchars(PMA_backquote($db)));
$message = PMA_message::error($message);
@@ -237,7 +239,12 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
// exit;
}
} else if (($GLOBALS['is_ajax_request'] == true)) {
- PMA_ajaxResponse(PMA_message::error(), false);
+ $message = __('Error in processing request') . ' : '
+ . sprintf(__('No routine with name %1$s found in database %2$s'),
+ htmlspecialchars(PMA_backquote($_REQUEST['routine_name'])),
+ htmlspecialchars(PMA_backquote($db)));
+ $message = PMA_message::error($message);
+ PMA_ajaxResponse($message, false);
}
} else if (! empty($_GET['exportroutine']) && ! empty($_GET['routine_name'])) {
/**
@@ -246,8 +253,8 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
$routine_name = htmlspecialchars(PMA_backquote($_GET['routine_name']));
$routine_type = PMA_DBI_fetch_value("SELECT ROUTINE_TYPE "
. "FROM INFORMATION_SCHEMA.ROUTINES "
- . "WHERE ROUTINE_SCHEMA='" . PMA_sqlAddslashes($db) . "' "
- . "AND SPECIFIC_NAME='" . PMA_sqlAddslashes($_GET['routine_name']) . "';");
+ . "WHERE ROUTINE_SCHEMA='" . PMA_sqlAddSlashes($db) . "' "
+ . "AND SPECIFIC_NAME='" . PMA_sqlAddSlashes($_GET['routine_name']) . "';");
if (! empty($routine_type) && $create_proc = PMA_DBI_get_definition($db, $routine_type, $_GET['routine_name'])) {
$create_proc = '';
if ($GLOBALS['is_ajax_request']) {
@@ -261,7 +268,7 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
}
} else {
$response = __('Error in processing request') . ' : '
- . sprintf(__('No routine with name %s found in database %s'),
+ . sprintf(__('No routine with name %1$s found in database %2$s'),
$routine_name, htmlspecialchars(PMA_backquote($db)));
$response = PMA_message::error($response);
if ($GLOBALS['is_ajax_request']) {
@@ -287,12 +294,12 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
$drop_routine = "DROP {$_REQUEST['routine_original_type']} " . PMA_backquote($_REQUEST['routine_original_name']) . ";\n";
$result = PMA_DBI_try_query($drop_routine);
if (! $result) {
- $routine_errors[] = sprintf(__('Query "%s" failed'), $drop_routine) . ' '
+ $routine_errors[] = sprintf(__('The following query has failed: "%s"'), $drop_routine) . ' '
. __('MySQL said: ') . PMA_DBI_getError(null);
} else {
$result = PMA_DBI_try_query($routine_query);
if (! $result) {
- $routine_errors[] = sprintf(__('Query "%s" failed'), $routine_query) . ' '
+ $routine_errors[] = sprintf(__('The following query has failed: "%s"'), $routine_query) . ' '
. __('MySQL said: ') . PMA_DBI_getError(null);
// We dropped the old routine, but were unable to create the new one
// Try to restore the backup query
@@ -316,7 +323,7 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
// 'Add a new routine' mode
$result = PMA_DBI_try_query($routine_query);
if (! $result) {
- $routine_errors[] = sprintf(__('Query "%s" failed'), $routine_query) . '
'
+ $routine_errors[] = sprintf(__('The following query has failed: "%s"'), $routine_query) . '
'
. __('MySQL said: ') . PMA_DBI_getError(null);
} else {
$message = PMA_Message::success(__('Routine %1$s has been created.'));
@@ -340,7 +347,7 @@ if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['routine_name']))
$extra_data = array();
if ($message->isSuccess()) {
$columns = "`SPECIFIC_NAME`, `ROUTINE_NAME`, `ROUTINE_TYPE`, `DTD_IDENTIFIER`, `ROUTINE_DEFINITION`";
- $where = "ROUTINE_SCHEMA='" . PMA_sqlAddslashes($db) . "' AND ROUTINE_NAME='" . PMA_sqlAddslashes($_REQUEST['routine_name']) . "'";
+ $where = "ROUTINE_SCHEMA='" . PMA_sqlAddSlashes($db) . "' AND ROUTINE_NAME='" . PMA_sqlAddSlashes($_REQUEST['routine_name']) . "'";
$routine = PMA_DBI_fetch_single_row("SELECT $columns FROM `INFORMATION_SCHEMA`.`ROUTINES` WHERE $where;");
$extra_data['name'] = htmlspecialchars(strtoupper($_REQUEST['routine_name']));
$extra_data['new_row'] = PMA_RTN_getRowForRoutinesList($routine, 0, true);
@@ -400,7 +407,7 @@ if (count($routine_errors) || ( empty($_REQUEST['routine_process_addroutine']) &
// exit;
} else {
$message = __('Error in processing request') . ' : '
- . sprintf(__('No routine with name %s found in database %s'),
+ . sprintf(__('No routine with name %1$s found in database %2$s'),
htmlspecialchars(PMA_backquote($_REQUEST['routine_name'])),
htmlspecialchars(PMA_backquote($db)));
$message = PMA_message::error($message);
diff --git a/db_search.php b/db_search.php
index a91bf87521..69350cda47 100644
--- a/db_search.php
+++ b/db_search.php
@@ -5,30 +5,6 @@
*
* @todo make use of UNION when searching multiple tables
* @todo display executed query, optional?
- * @uses $cfg['UseDbSearch']
- * @uses $GLOBALS['db']
- * @uses PMA_DBI_get_tables()
- * @uses PMA_sqlAddslashes()
- * @uses PMA_getSearchSqls()
- * @uses PMA_DBI_fetch_value()
- * @uses PMA_linkOrButton()
- * @uses PMA_generate_common_url()
- * @uses PMA_generate_common_hidden_inputs()
- * @uses PMA_showMySQLDocu()
- * @uses $_REQUEST['search_str']
- * @uses $_REQUEST['submit_search']
- * @uses $_REQUEST['search_option']
- * @uses $_REQUEST['table_select']
- * @uses $_REQUEST['unselectall']
- * @uses $_REQUEST['selectall']
- * @uses $_REQUEST['field_str']
- * @uses is_string()
- * @uses htmlspecialchars()
- * @uses array_key_exists()
- * @uses is_array()
- * @uses array_intersect()
- * @uses sprintf()
- * @uses in_array()
* @package phpMyAdmin
*/
@@ -85,11 +61,11 @@ if (empty($_REQUEST['search_str']) || ! is_string($_REQUEST['search_str'])) {
$searched = htmlspecialchars($_REQUEST['search_str']);
// For "as regular expression" (search option 4), we should not treat
// this as an expression that contains a LIKE (second parameter of
- // PMA_sqlAddslashes()).
+ // PMA_sqlAddSlashes()).
//
// Usage example: If user is seaching for a literal $ in a regexp search,
// he should enter \$ as the value.
- $search_str = PMA_sqlAddslashes($_REQUEST['search_str'], ($search_option == 4 ? false : true));
+ $search_str = PMA_sqlAddSlashes($_REQUEST['search_str'], ($search_option == 4 ? false : true));
}
$tables_selected = array();
@@ -108,7 +84,7 @@ if (isset($_REQUEST['selectall'])) {
if (empty($_REQUEST['field_str']) || ! is_string($_REQUEST['field_str'])) {
unset($field_str);
} else {
- $field_str = PMA_sqlAddslashes($_REQUEST['field_str'], true);
+ $field_str = PMA_sqlAddSlashes($_REQUEST['field_str'], true);
}
/**
@@ -130,7 +106,6 @@ if (isset($_REQUEST['submit_search'])) {
* Builds the SQL search query
*
* @todo can we make use of fulltextsearch IN BOOLEAN MODE for this?
- * @uses PMA_DBI_query
* PMA_backquote
* PMA_DBI_free_result
* PMA_DBI_fetch_assoc
@@ -297,7 +272,7 @@ else {
-
+
@@ -320,7 +295,7 @@ unset($choices);
-
+
' . "\n";
diff --git a/db_tracking.php b/db_tracking.php
index c0421d600d..3a9b6954b7 100644
--- a/db_tracking.php
+++ b/db_tracking.php
@@ -67,7 +67,7 @@ require_once './libraries/db_links.inc.php';
$all_tables_query = ' SELECT table_name, MAX(version) as version FROM ' .
PMA_backquote($GLOBALS['cfg']['Server']['pmadb']) . '.' .
PMA_backquote($GLOBALS['cfg']['Server']['tracking']) .
- ' WHERE ' . PMA_backquote('db_name') . ' = \'' . PMA_sqlAddslashes($_REQUEST['db']) . '\' ' .
+ ' WHERE ' . PMA_backquote('db_name') . ' = \'' . PMA_sqlAddSlashes($_REQUEST['db']) . '\' ' .
' GROUP BY '. PMA_backquote('table_name') .
' ORDER BY '. PMA_backquote('table_name') .' ASC';
@@ -110,7 +110,7 @@ if (PMA_DBI_num_rows($all_tables_result) > 0) {
$table_query = ' SELECT * FROM ' .
PMA_backquote($GLOBALS['cfg']['Server']['pmadb']) . '.' .
PMA_backquote($GLOBALS['cfg']['Server']['tracking']) .
- ' WHERE `db_name` = \'' . PMA_sqlAddslashes($_REQUEST['db']) . '\' AND `table_name` = \'' . PMA_sqlAddslashes($table_name) . '\' AND `version` = \'' . $version_number . '\'';
+ ' WHERE `db_name` = \'' . PMA_sqlAddSlashes($_REQUEST['db']) . '\' AND `table_name` = \'' . PMA_sqlAddSlashes($table_name) . '\' AND `version` = \'' . $version_number . '\'';
$table_result = PMA_query_as_controluser($table_query);
$version_data = PMA_DBI_fetch_array($table_result);
diff --git a/enum_editor.php b/enum_editor.php
index 586e85e7ff..ab86d737cb 100644
--- a/enum_editor.php
+++ b/enum_editor.php
@@ -53,11 +53,11 @@ require_once './libraries/header_meta_style.inc.php';
?>
-
+
diff --git a/import.php b/import.php
index cd56bc2c37..afc513ce3c 100644
--- a/import.php
+++ b/import.php
@@ -3,7 +3,6 @@
/**
* Core script for import, this is just the glue around all other stuff
*
- * @uses PMA_Bookmark_getList()
* @package phpMyAdmin
*/
@@ -108,7 +107,7 @@ if ($import_type == 'table') {
}
$err_url = $goto
. '?' . $common
- . (preg_match('@^tbl_[a-z]*\.php$@', $goto) ? '&table=' . urlencode($table) : '');
+ . (preg_match('@^tbl_[a-z]*\.php$@', $goto) ? '&table=' . htmlspecialchars($table) : '');
$_SESSION['Import_message']['go_back_url'] = $err_url;
}
@@ -154,7 +153,7 @@ if (!empty($id_bookmark)) {
case 0: // bookmarked query that have to be run
$import_text = PMA_Bookmark_get($db, $id_bookmark, 'id', isset($action_bookmark_all));
if (isset($bookmark_variable) && !empty($bookmark_variable)) {
- $import_text = preg_replace('|/\*(.*)\[VARIABLE\](.*)\*/|imsU', '${1}' . PMA_sqlAddslashes($bookmark_variable) . '${2}', $import_text);
+ $import_text = preg_replace('|/\*(.*)\[VARIABLE\](.*)\*/|imsU', '${1}' . PMA_sqlAddSlashes($bookmark_variable) . '${2}', $import_text);
}
// refresh left frame on changes in table or db structure
diff --git a/index.php b/index.php
index 36a611878e..cb5eb63775 100644
--- a/index.php
+++ b/index.php
@@ -3,26 +3,7 @@
/**
* forms frameset
*
- * @uses $GLOBALS['cfg']['QueryHistoryDB']
- * @uses $GLOBALS['cfg']['Server']['user']
- * @uses $GLOBALS['cfg']['DefaultTabServer'] as src for the mainframe
- * @uses $GLOBALS['cfg']['DefaultTabDatabase'] as src for the mainframe
- * @uses $GLOBALS['cfg']['NaviWidth'] for navi frame width
- * @uses $GLOBALS['collation_connection'] from $_REQUEST (grab_globals.lib.php)
* or common.inc.php
- * @uses $GLOBALS['available_languages'] from common.inc.php (select_lang.lib.php)
- * @uses $GLOBALS['db']
- * @uses $GLOBALS['lang']
- * @uses $GLOBALS['text_dir']
- * @uses $_ENV['HTTP_HOST']
- * @uses PMA_getRelationsParam()
- * @uses PMA_purgeHistory()
- * @uses PMA_generate_common_url()
- * @uses PMA_VERSION
- * @uses session_write_close()
- * @uses time()
- * @uses PMA_getenv()
- * @uses header() to send charset
* @package phpMyAdmin
*/
@@ -73,7 +54,7 @@ if (! strlen($GLOBALS['db'])) {
} else {
$_GET['db'] = $GLOBALS['db'];
$_GET['table'] = $GLOBALS['table'];
- $main_target = $GLOBALS['cfg']['DefaultTabTable'];
+ $main_target = isset($GLOBALS['goto']) ? $GLOBALS['goto'] : $GLOBALS['cfg']['DefaultTabTable'];
}
$url_query = PMA_generate_common_url($_GET);
diff --git a/js/db_routines.js b/js/db_routines.js
index 1c03b6b74b..95254ca064 100644
--- a/js/db_routines.js
+++ b/js/db_routines.js
@@ -246,7 +246,7 @@ $(document).ready(function() {
if(data.success == true) {
// Routine created successfully
PMA_ajaxRemoveMessage($msg);
- PMA_slidingMessage($('#js_query_display'), data.message);
+ PMA_slidingMessage(data.message);
$ajaxDialog.dialog('close');
// If we are in 'edit' mode, we must remove the reference to the old row.
if (mode == 'edit') {
@@ -516,7 +516,7 @@ $(document).ready(function() {
if(data.success == true) {
// Routine executed successfully
PMA_ajaxRemoveMessage($msg);
- PMA_slidingMessage($('#js_query_display'), data.message);
+ PMA_slidingMessage(data.message);
$ajaxDialog.dialog('close');
} else {
PMA_ajaxShowMessage(data.error);
@@ -542,7 +542,7 @@ $(document).ready(function() {
$ajaxDialog.find('input[name^=params]').first().focus();
} else {
// Routine executed successfully
- PMA_slidingMessage($('#js_query_display'), data.message);
+ PMA_slidingMessage(data.message);
}
} else {
PMA_ajaxShowMessage(data.error);
diff --git a/js/functions.js b/js/functions.js
index 14f3b46b76..397f2484ca 100644
--- a/js/functions.js
+++ b/js/functions.js
@@ -1300,7 +1300,7 @@ function PMA_ajaxShowMessage(message, timeout) {
* Removes the message shown for an Ajax operation when it's completed
*/
function PMA_ajaxRemoveMessage($this_msgbox) {
- if ($this_msgbox != 'undefined' && $this_msgbox instanceof jQuery) {
+ if ($this_msgbox != undefined && $this_msgbox instanceof jQuery) {
$this_msgbox
.stop(true, true)
.fadeOut('medium');
@@ -1448,7 +1448,7 @@ function PMA_createChart(passedSettings) {
enabled:false
},
xAxis: {
- type: 'datetime',
+ type: 'datetime'
},
yAxis: {
min: 0,
@@ -2013,6 +2013,10 @@ $(document).ready(function() {
* Hides certain table structure actions, replacing them with the word "More". They are displayed
* in a dropdown menu when the user hovers over the word "More."
*/
+ displayMoreTableOpts();
+});
+
+function displayMoreTableOpts() {
// Remove the actions from the table cells (they are available by default for JavaScript-disabled browsers)
// if the table is not a view or information_schema (otherwise there is only one action to hide and there's no point)
if($("input[type='hidden'][name='table_type']").val() == "table") {
@@ -2022,6 +2026,7 @@ $(document).ready(function() {
$table.find("td[class='unique']").remove();
$table.find("td[class='index']").remove();
$table.find("td[class='fulltext']").remove();
+ $table.find("td[class='spatial']").remove();
$table.find("th[class='action']").attr("colspan", 3);
// Display the "more" text
@@ -2080,8 +2085,7 @@ $(document).ready(function() {
}
});
}
-});
-
+}
$(document).ready(initTooltips);
/* Displays tooltips */
@@ -2448,55 +2452,78 @@ $(document).ready(function() {
/**
* Creates a message inside an object with a sliding effect
*
+ * @param msg A string containing the text to display
* @param $obj a jQuery object containing the reference
* to the element where to put the message
- * @param msg A string containing the text to display
+ * This is optional, if no element is
+ * provided, one will be created below the
+ * navigation links at the top of the page
*
* @return bool True on success, false on failure
*/
-function PMA_slidingMessage($obj, msg) {
- if ($obj != 'undefined' && $obj instanceof jQuery) {
- if ($obj.has('div').length > 0) {
- // If there already is a message inside the
- // target object, we must get rid of it
+function PMA_slidingMessage(msg, $obj) {
+ if (msg == undefined || msg.length == 0) {
+ // Don't show an empty message
+ return false;
+ }
+ if ($obj == undefined || ! $obj instanceof jQuery || $obj.length == 0) {
+ // If the second argument was not supplied,
+ // we might have to create a new DOM node.
+ if ($('#PMA_slidingMessage').length == 0) {
+ $('#topmenucontainer')
+ .after('');
+ }
+ $obj = $('#PMA_slidingMessage');
+ }
+ if ($obj.has('div').length > 0) {
+ // If there already is a message inside the
+ // target object, we must get rid of it
+ $obj
+ .find('div')
+ .first()
+ .fadeOut(function () {
+ $obj
+ .children()
+ .remove();
$obj
.append('
' + msg + '
')
+ .animate({
+ height: $obj.find('div').first().height()
+ })
.find('div')
.first()
- .fadeOut(function () {
- $(this).remove();
- $obj.animate({
- height: $obj.find('div').first().height()
- });
+ .fadeIn();
+ });
+ } else {
+ // Object does not already have a message
+ // inside it, so we simply slide it down
+ var h = $obj
+ .width('100%')
+ .html('
' + msg + '
')
+ .find('div')
+ .first()
+ .height();
+ $obj
+ .find('div')
+ .first()
+ .css('height', 0)
+ .show()
+ .animate({
+ height: h
+ }, function() {
+ // Set the height of the parent
+ // to the height of the child
+ $obj
+ .height(
$obj
.find('div')
.first()
- .fadeIn();
- });
- } else {
- // Object does not already have a message
- // inside it, so we simply slide it down
- $obj
- .width('100%')
- .html('
' + msg + '
')
- .find('div')
- .first()
- .slideDown(function() {
- // Set the height of the parent
- // to the height of the child
- $obj
- .height(
- $obj
- .find('div')
- .first()
- .height()
- );
- });
- }
- return true;
- } else {
- return false;
+ .height()
+ );
+ });
}
+ return true;
} // end PMA_slidingMessage()
/**
@@ -2553,7 +2580,7 @@ $(document).ready(function() {
}
// Show the query that we just executed
PMA_ajaxRemoveMessage($msg);
- PMA_slidingMessage($('#js_query_display'), data.sql_query);
+ PMA_slidingMessage(data.sql_query);
} else {
PMA_ajaxShowMessage(PMA_messages['strErrorProcessingRequest'] + " : " + data.error);
}
diff --git a/js/indexes.js b/js/indexes.js
index a15b249150..9b64d92048 100644
--- a/js/indexes.js
+++ b/js/indexes.js
@@ -41,5 +41,98 @@ function checkIndexName()
return true;
} // end of the 'checkIndexName()' function
-
onload = checkIndexName;
+
+/**
+ * Hides/shows the inputs and submits appropriately depending
+ * on whether the index type chosen is 'SPATIAL' or not.
+ */
+function checkIndexType()
+{
+ /**
+ * @var Object Dropdown to select the index type.
+ */
+ $select_index_type = $('#select_index_type');
+ /**
+ * @var Object Table header for the size column.
+ */
+ $size_header = $('thead tr th:nth-child(2)');
+ /**
+ * @var Object Inputs to specify the columns for the index.
+ */
+ $column_inputs = $('select[name="index[columns][names][]"]');
+ /**
+ * @var Object Inputs to specify sizes for columns of the index.
+ */
+ $size_inputs = $('input[name="index[columns][sub_parts][]"]');
+ /**
+ * @var Object Span containg the controllers to add more columns
+ */
+ $add_more = $('#addMoreColumns');
+
+ if ($select_index_type.val() == 'SPATIAL') {
+ // Disable and hide the size column
+ $size_header.hide();
+ $size_inputs.each(function(){
+ $(this)
+ .attr('disabled', true)
+ .parent('td').hide();
+ });
+
+ // Disable and hide the columns of the index other than the first one
+ var initial = true;
+ $column_inputs.each(function() {
+ $column_input = $(this);
+ if (! initial) {
+ $column_input
+ .attr('disabled', true)
+ .parent('td').hide();
+ } else {
+ initial = false;
+ }
+ });
+
+ // Hide controllers to add more columns
+ $add_more.hide();
+ } else {
+ // Enable and show the size column
+ $size_header.show();
+ $size_inputs.each(function() {
+ $(this)
+ .attr('disabled', false)
+ .parent('td').show();
+ });
+
+ // Enable and show the columns of the index
+ $column_inputs.each(function() {
+ $(this)
+ .attr('disabled', false)
+ .parent('td').show();
+ });
+
+ // Show controllers to add more columns
+ $add_more.show();
+ }
+}
+
+/**#@+
+ * @namespace jQuery
+ */
+
+/**
+ * @description
Ajax scripts for table index page
+ *
+ * Actions ajaxified here:
+ *
+ *
Showing/hiding inputs depending on the index type chosen
';
+ }
+ }
+ },
+
+ /* SVG callback after loading - register SVG root. */
+ _registerSVG: function() {
+ for (var i = 0; i < document.embeds.length; i++) { // Check all
+ var container = document.embeds[i].parentNode;
+ if (!$(container).hasClass($.svg.markerClassName) || // Not SVG
+ $.data(container, PROP_NAME)) { // Already done
+ continue;
+ }
+ var svg = null;
+ try {
+ svg = document.embeds[i].getSVGDocument();
+ }
+ catch(e) {
+ setTimeout($.svg._registerSVG, 250); // Renesis takes longer to load
+ return;
+ }
+ svg = (svg ? svg.documentElement : null);
+ if (svg) {
+ $.svg._afterLoad(container, svg);
+ }
+ }
+ },
+
+ /* Post-processing once loaded. */
+ _afterLoad: function(container, svg, settings) {
+ var settings = settings || this._settings[container.id];
+ this._settings[container ? container.id : ''] = null;
+ var wrapper = new this._wrapperClass(svg, container);
+ $.data(container || svg, PROP_NAME, wrapper);
+ try {
+ if (settings.loadURL) { // Load URL
+ wrapper.load(settings.loadURL, settings);
+ }
+ if (settings.settings) { // Additional settings
+ wrapper.configure(settings.settings);
+ }
+ if (settings.onLoad && !settings.loadURL) { // Onload callback
+ settings.onLoad.apply(container || svg, [wrapper]);
+ }
+ }
+ catch (e) {
+ alert(e);
+ }
+ },
+
+ /* Return the SVG wrapper created for a given container.
+ @param container (string) selector for the container or
+ (element) the container for the SVG object or
+ jQuery collection - first entry is the container
+ @return (SVGWrapper) the corresponding SVG wrapper element, or null if not attached */
+ _getSVG: function(container) {
+ container = (typeof container == 'string' ? $(container)[0] :
+ (container.jquery ? container[0] : container));
+ return $.data(container, PROP_NAME);
+ },
+
+ /* Remove the SVG functionality from a div.
+ @param container (element) the container for the SVG object */
+ _destroySVG: function(container) {
+ var $container = $(container);
+ if (!$container.hasClass(this.markerClassName)) {
+ return;
+ }
+ $container.removeClass(this.markerClassName);
+ if (container.namespaceURI != this.svgNS) {
+ $container.empty();
+ }
+ $.removeData(container, PROP_NAME);
+ },
+
+ /* Extend the SVGWrapper object with an embedded class.
+ The constructor function must take a single parameter that is
+ a reference to the owning SVG root object. This allows the
+ extension to access the basic SVG functionality.
+ @param name (string) the name of the SVGWrapper attribute to access the new class
+ @param extClass (function) the extension class constructor */
+ addExtension: function(name, extClass) {
+ this._extensions.push([name, extClass]);
+ }
+});
+
+/* The main SVG interface, which encapsulates the SVG element.
+ Obtain a reference from $().svg('get') */
+function SVGWrapper(svg, container) {
+ this._svg = svg; // The SVG root node
+ this._container = container; // The containing div
+ for (var i = 0; i < $.svg._extensions.length; i++) {
+ var extension = $.svg._extensions[i];
+ this[extension[0]] = new extension[1](this);
+ }
+}
+
+$.extend(SVGWrapper.prototype, {
+
+ /* Retrieve the width of the SVG object. */
+ _width: function() {
+ return (this._container ? this._container.clientWidth : this._svg.width);
+ },
+
+ /* Retrieve the height of the SVG object. */
+ _height: function() {
+ return (this._container ? this._container.clientHeight : this._svg.height);
+ },
+
+ /* Retrieve the root SVG element.
+ @return the top-level SVG element */
+ root: function() {
+ return this._svg;
+ },
+
+ /* Configure the SVG root.
+ @param settings (object) additional settings for the root
+ @param clear (boolean) true to remove existing attributes first,
+ false to add to what is already there (optional)
+ @return (SVGWrapper) this root */
+ configure: function(settings, clear) {
+ if (clear) {
+ for (var i = this._svg.attributes.length - 1; i >= 0; i--) {
+ var attr = this._svg.attributes.item(i);
+ if (!(attr.nodeName == 'onload' || attr.nodeName == 'version' ||
+ attr.nodeName.substring(0, 5) == 'xmlns')) {
+ this._svg.attributes.removeNamedItem(attr.nodeName);
+ }
+ }
+ }
+ for (var attrName in settings) {
+ this._svg.setAttribute(attrName, settings[attrName]);
+ }
+ return this;
+ },
+
+ /* Locate a specific element in the SVG document.
+ @param id (string) the element's identifier
+ @return (element) the element reference, or null if not found */
+ getElementById: function(id) {
+ return this._svg.ownerDocument.getElementById(id);
+ },
+
+ /* Change the attributes for a SVG node.
+ @param element (SVG element) the node to change
+ @param settings (object) the new settings
+ @return (SVGWrapper) this root */
+ change: function(element, settings) {
+ if (element) {
+ for (var name in settings) {
+ if (settings[name] == null) {
+ element.removeAttribute(name);
+ }
+ else {
+ element.setAttribute(name, settings[name]);
+ }
+ }
+ }
+ return this;
+ },
+
+ /* Check for parent being absent and adjust arguments accordingly. */
+ _args: function(values, names, optSettings) {
+ names.splice(0, 0, 'parent');
+ names.splice(names.length, 0, 'settings');
+ var args = {};
+ var offset = 0;
+ if (values[0] != null && values[0].jquery) {
+ values[0] = values[0][0];
+ }
+ if (values[0] != null && !(typeof values[0] == 'object' && values[0].nodeName)) {
+ args['parent'] = null;
+ offset = 1;
+ }
+ for (var i = 0; i < values.length; i++) {
+ args[names[i + offset]] = values[i];
+ }
+ if (optSettings) {
+ $.each(optSettings, function(i, value) {
+ if (typeof args[value] == 'object') {
+ args.settings = args[value];
+ args[value] = null;
+ }
+ });
+ }
+ return args;
+ },
+
+ /* Add a title.
+ @param parent (element or jQuery) the parent node for the new title (optional)
+ @param text (string) the text of the title
+ @param settings (object) additional settings for the title (optional)
+ @return (element) the new title node */
+ title: function(parent, text, settings) {
+ var args = this._args(arguments, ['text']);
+ var node = this._makeNode(args.parent, 'title', args.settings || {});
+ node.appendChild(this._svg.ownerDocument.createTextNode(args.text));
+ return node;
+ },
+
+ /* Add a description.
+ @param parent (element or jQuery) the parent node for the new description (optional)
+ @param text (string) the text of the description
+ @param settings (object) additional settings for the description (optional)
+ @return (element) the new description node */
+ describe: function(parent, text, settings) {
+ var args = this._args(arguments, ['text']);
+ var node = this._makeNode(args.parent, 'desc', args.settings || {});
+ node.appendChild(this._svg.ownerDocument.createTextNode(args.text));
+ return node;
+ },
+
+ /* Add a definitions node.
+ @param parent (element or jQuery) the parent node for the new definitions (optional)
+ @param id (string) the ID of this definitions (optional)
+ @param settings (object) additional settings for the definitions (optional)
+ @return (element) the new definitions node */
+ defs: function(parent, id, settings) {
+ var args = this._args(arguments, ['id'], ['id']);
+ return this._makeNode(args.parent, 'defs', $.extend(
+ (args.id ? {id: args.id} : {}), args.settings || {}));
+ },
+
+ /* Add a symbol definition.
+ @param parent (element or jQuery) the parent node for the new symbol (optional)
+ @param id (string) the ID of this symbol
+ @param x1 (number) the left coordinate for this symbol
+ @param y1 (number) the top coordinate for this symbol
+ @param width (number) the width of this symbol
+ @param height (number) the height of this symbol
+ @param settings (object) additional settings for the symbol (optional)
+ @return (element) the new symbol node */
+ symbol: function(parent, id, x1, y1, width, height, settings) {
+ var args = this._args(arguments, ['id', 'x1', 'y1', 'width', 'height']);
+ return this._makeNode(args.parent, 'symbol', $.extend({id: args.id,
+ viewBox: args.x1 + ' ' + args.y1 + ' ' + args.width + ' ' + args.height},
+ args.settings || {}));
+ },
+
+ /* Add a marker definition.
+ @param parent (element or jQuery) the parent node for the new marker (optional)
+ @param id (string) the ID of this marker
+ @param refX (number) the x-coordinate for the reference point
+ @param refY (number) the y-coordinate for the reference point
+ @param mWidth (number) the marker viewport width
+ @param mHeight (number) the marker viewport height
+ @param orient (string or int) 'auto' or angle (degrees) (optional)
+ @param settings (object) additional settings for the marker (optional)
+ @return (element) the new marker node */
+ marker: function(parent, id, refX, refY, mWidth, mHeight, orient, settings) {
+ var args = this._args(arguments, ['id', 'refX', 'refY',
+ 'mWidth', 'mHeight', 'orient'], ['orient']);
+ return this._makeNode(args.parent, 'marker', $.extend(
+ {id: args.id, refX: args.refX, refY: args.refY, markerWidth: args.mWidth,
+ markerHeight: args.mHeight, orient: args.orient || 'auto'}, args.settings || {}));
+ },
+
+ /* Add a style node.
+ @param parent (element or jQuery) the parent node for the new node (optional)
+ @param styles (string) the CSS styles
+ @param settings (object) additional settings for the node (optional)
+ @return (element) the new style node */
+ style: function(parent, styles, settings) {
+ var args = this._args(arguments, ['styles']);
+ var node = this._makeNode(args.parent, 'style', $.extend(
+ {type: 'text/css'}, args.settings || {}));
+ node.appendChild(this._svg.ownerDocument.createTextNode(args.styles));
+ if ($.browser.opera) {
+ $('head').append('');
+ }
+ return node;
+ },
+
+ /* Add a script node.
+ @param parent (element or jQuery) the parent node for the new node (optional)
+ @param script (string) the JavaScript code
+ @param type (string) the MIME type for the code (optional, default 'text/javascript')
+ @param settings (object) additional settings for the node (optional)
+ @return (element) the new script node */
+ script: function(parent, script, type, settings) {
+ var args = this._args(arguments, ['script', 'type'], ['type']);
+ var node = this._makeNode(args.parent, 'script', $.extend(
+ {type: args.type || 'text/javascript'}, args.settings || {}));
+ node.appendChild(this._svg.ownerDocument.createTextNode(this._escapeXML(args.script)));
+ if (!$.browser.mozilla) {
+ $.globalEval(args.script);
+ }
+ return node;
+ },
+
+ /* Add a linear gradient definition.
+ Specify all of x1, y1, x2, y2 or none of them.
+ @param parent (element or jQuery) the parent node for the new gradient (optional)
+ @param id (string) the ID for this gradient
+ @param stops (string[][]) the gradient stops, each entry is
+ [0] is offset (0.0-1.0 or 0%-100%), [1] is colour,
+ [2] is opacity (optional)
+ @param x1 (number) the x-coordinate of the gradient start (optional)
+ @param y1 (number) the y-coordinate of the gradient start (optional)
+ @param x2 (number) the x-coordinate of the gradient end (optional)
+ @param y2 (number) the y-coordinate of the gradient end (optional)
+ @param settings (object) additional settings for the gradient (optional)
+ @return (element) the new gradient node */
+ linearGradient: function(parent, id, stops, x1, y1, x2, y2, settings) {
+ var args = this._args(arguments,
+ ['id', 'stops', 'x1', 'y1', 'x2', 'y2'], ['x1']);
+ var sets = $.extend({id: args.id},
+ (args.x1 != null ? {x1: args.x1, y1: args.y1, x2: args.x2, y2: args.y2} : {}));
+ return this._gradient(args.parent, 'linearGradient',
+ $.extend(sets, args.settings || {}), args.stops);
+ },
+
+ /* Add a radial gradient definition.
+ Specify all of cx, cy, r, fx, fy or none of them.
+ @param parent (element or jQuery) the parent node for the new gradient (optional)
+ @param id (string) the ID for this gradient
+ @param stops (string[][]) the gradient stops, each entry
+ [0] is offset, [1] is colour, [2] is opacity (optional)
+ @param cx (number) the x-coordinate of the largest circle centre (optional)
+ @param cy (number) the y-coordinate of the largest circle centre (optional)
+ @param r (number) the radius of the largest circle (optional)
+ @param fx (number) the x-coordinate of the gradient focus (optional)
+ @param fy (number) the y-coordinate of the gradient focus (optional)
+ @param settings (object) additional settings for the gradient (optional)
+ @return (element) the new gradient node */
+ radialGradient: function(parent, id, stops, cx, cy, r, fx, fy, settings) {
+ var args = this._args(arguments,
+ ['id', 'stops', 'cx', 'cy', 'r', 'fx', 'fy'], ['cx']);
+ var sets = $.extend({id: args.id}, (args.cx != null ?
+ {cx: args.cx, cy: args.cy, r: args.r, fx: args.fx, fy: args.fy} : {}));
+ return this._gradient(args.parent, 'radialGradient',
+ $.extend(sets, args.settings || {}), args.stops);
+ },
+
+ /* Add a gradient node. */
+ _gradient: function(parent, name, settings, stops) {
+ var node = this._makeNode(parent, name, settings);
+ for (var i = 0; i < stops.length; i++) {
+ var stop = stops[i];
+ this._makeNode(node, 'stop', $.extend(
+ {offset: stop[0], stopColor: stop[1]},
+ (stop[2] != null ? {stopOpacity: stop[2]} : {})));
+ }
+ return node;
+ },
+
+ /* Add a pattern definition.
+ Specify all of vx, vy, xwidth, vheight or none of them.
+ @param parent (element or jQuery) the parent node for the new pattern (optional)
+ @param id (string) the ID for this pattern
+ @param x (number) the x-coordinate for the left edge of the pattern
+ @param y (number) the y-coordinate for the top edge of the pattern
+ @param width (number) the width of the pattern
+ @param height (number) the height of the pattern
+ @param vx (number) the minimum x-coordinate for view box (optional)
+ @param vy (number) the minimum y-coordinate for the view box (optional)
+ @param vwidth (number) the width of the view box (optional)
+ @param vheight (number) the height of the view box (optional)
+ @param settings (object) additional settings for the pattern (optional)
+ @return (element) the new pattern node */
+ pattern: function(parent, id, x, y, width, height, vx, vy, vwidth, vheight, settings) {
+ var args = this._args(arguments, ['id', 'x', 'y', 'width', 'height',
+ 'vx', 'vy', 'vwidth', 'vheight'], ['vx']);
+ var sets = $.extend({id: args.id, x: args.x, y: args.y,
+ width: args.width, height: args.height}, (args.vx != null ?
+ {viewBox: args.vx + ' ' + args.vy + ' ' + args.vwidth + ' ' + args.vheight} : {}));
+ return this._makeNode(args.parent, 'pattern', $.extend(sets, args.settings || {}));
+ },
+
+ /* Add a mask definition.
+ @param parent (element or jQuery) the parent node for the new mask (optional)
+ @param id (string) the ID for this mask
+ @param x (number) the x-coordinate for the left edge of the mask
+ @param y (number) the y-coordinate for the top edge of the mask
+ @param width (number) the width of the mask
+ @param height (number) the height of the mask
+ @param settings (object) additional settings for the mask (optional)
+ @return (element) the new mask node */
+ mask: function(parent, id, x, y, width, height, settings) {
+ var args = this._args(arguments, ['id', 'x', 'y', 'width', 'height']);
+ return this._makeNode(args.parent, 'mask', $.extend(
+ {id: args.id, x: args.x, y: args.y, width: args.width, height: args.height},
+ args.settings || {}));
+ },
+
+ /* Create a new path object.
+ @return (SVGPath) a new path object */
+ createPath: function() {
+ return new SVGPath();
+ },
+
+ /* Create a new text object.
+ @return (SVGText) a new text object */
+ createText: function() {
+ return new SVGText();
+ },
+
+ /* Add an embedded SVG element.
+ Specify all of vx, vy, vwidth, vheight or none of them.
+ @param parent (element or jQuery) the parent node for the new node (optional)
+ @param x (number) the x-coordinate for the left edge of the node
+ @param y (number) the y-coordinate for the top edge of the node
+ @param width (number) the width of the node
+ @param height (number) the height of the node
+ @param vx (number) the minimum x-coordinate for view box (optional)
+ @param vy (number) the minimum y-coordinate for the view box (optional)
+ @param vwidth (number) the width of the view box (optional)
+ @param vheight (number) the height of the view box (optional)
+ @param settings (object) additional settings for the node (optional)
+ @return (element) the new node */
+ svg: function(parent, x, y, width, height, vx, vy, vwidth, vheight, settings) {
+ var args = this._args(arguments, ['x', 'y', 'width', 'height',
+ 'vx', 'vy', 'vwidth', 'vheight'], ['vx']);
+ var sets = $.extend({x: args.x, y: args.y, width: args.width, height: args.height},
+ (args.vx != null ? {viewBox: args.vx + ' ' + args.vy + ' ' +
+ args.vwidth + ' ' + args.vheight} : {}));
+ return this._makeNode(args.parent, 'svg', $.extend(sets, args.settings || {}));
+ },
+
+ /* Create a group.
+ @param parent (element or jQuery) the parent node for the new group (optional)
+ @param id (string) the ID of this group (optional)
+ @param settings (object) additional settings for the group (optional)
+ @return (element) the new group node */
+ group: function(parent, id, settings) {
+ var args = this._args(arguments, ['id'], ['id']);
+ return this._makeNode(args.parent, 'g', $.extend({id: args.id}, args.settings || {}));
+ },
+
+ /* Add a usage reference.
+ Specify all of x, y, width, height or none of them.
+ @param parent (element or jQuery) the parent node for the new node (optional)
+ @param x (number) the x-coordinate for the left edge of the node (optional)
+ @param y (number) the y-coordinate for the top edge of the node (optional)
+ @param width (number) the width of the node (optional)
+ @param height (number) the height of the node (optional)
+ @param ref (string) the ID of the definition node
+ @param settings (object) additional settings for the node (optional)
+ @return (element) the new node */
+ use: function(parent, x, y, width, height, ref, settings) {
+ var args = this._args(arguments, ['x', 'y', 'width', 'height', 'ref']);
+ if (typeof args.x == 'string') {
+ args.ref = args.x;
+ args.settings = args.y;
+ args.x = args.y = args.width = args.height = null;
+ }
+ var node = this._makeNode(args.parent, 'use', $.extend(
+ {x: args.x, y: args.y, width: args.width, height: args.height},
+ args.settings || {}));
+ node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);
+ return node;
+ },
+
+ /* Add a link, which applies to all child elements.
+ @param parent (element or jQuery) the parent node for the new link (optional)
+ @param ref (string) the target URL
+ @param settings (object) additional settings for the link (optional)
+ @return (element) the new link node */
+ link: function(parent, ref, settings) {
+ var args = this._args(arguments, ['ref']);
+ var node = this._makeNode(args.parent, 'a', args.settings);
+ node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);
+ return node;
+ },
+
+ /* Add an image.
+ @param parent (element or jQuery) the parent node for the new image (optional)
+ @param x (number) the x-coordinate for the left edge of the image
+ @param y (number) the y-coordinate for the top edge of the image
+ @param width (number) the width of the image
+ @param height (number) the height of the image
+ @param ref (string) the path to the image
+ @param settings (object) additional settings for the image (optional)
+ @return (element) the new image node */
+ image: function(parent, x, y, width, height, ref, settings) {
+ var args = this._args(arguments, ['x', 'y', 'width', 'height', 'ref']);
+ var node = this._makeNode(args.parent, 'image', $.extend(
+ {x: args.x, y: args.y, width: args.width, height: args.height},
+ args.settings || {}));
+ node.setAttributeNS($.svg.xlinkNS, 'href', args.ref);
+ return node;
+ },
+
+ /* Draw a path.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param path (string or SVGPath) the path to draw
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ path: function(parent, path, settings) {
+ var args = this._args(arguments, ['path']);
+ return this._makeNode(args.parent, 'path', $.extend(
+ {d: (args.path.path ? args.path.path() : args.path)}, args.settings || {}));
+ },
+
+ /* Draw a rectangle.
+ Specify both of rx and ry or neither.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param x (number) the x-coordinate for the left edge of the rectangle
+ @param y (number) the y-coordinate for the top edge of the rectangle
+ @param width (number) the width of the rectangle
+ @param height (number) the height of the rectangle
+ @param rx (number) the x-radius of the ellipse for the rounded corners (optional)
+ @param ry (number) the y-radius of the ellipse for the rounded corners (optional)
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ rect: function(parent, x, y, width, height, rx, ry, settings) {
+ var args = this._args(arguments, ['x', 'y', 'width', 'height', 'rx', 'ry'], ['rx']);
+ return this._makeNode(args.parent, 'rect', $.extend(
+ {x: args.x, y: args.y, width: args.width, height: args.height},
+ (args.rx ? {rx: args.rx, ry: args.ry} : {}), args.settings || {}));
+ },
+
+ /* Draw a circle.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param cx (number) the x-coordinate for the centre of the circle
+ @param cy (number) the y-coordinate for the centre of the circle
+ @param r (number) the radius of the circle
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ circle: function(parent, cx, cy, r, settings) {
+ var args = this._args(arguments, ['cx', 'cy', 'r']);
+ return this._makeNode(args.parent, 'circle', $.extend(
+ {cx: args.cx, cy: args.cy, r: args.r}, args.settings || {}));
+ },
+
+ /* Draw an ellipse.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param cx (number) the x-coordinate for the centre of the ellipse
+ @param cy (number) the y-coordinate for the centre of the ellipse
+ @param rx (number) the x-radius of the ellipse
+ @param ry (number) the y-radius of the ellipse
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ ellipse: function(parent, cx, cy, rx, ry, settings) {
+ var args = this._args(arguments, ['cx', 'cy', 'rx', 'ry']);
+ return this._makeNode(args.parent, 'ellipse', $.extend(
+ {cx: args.cx, cy: args.cy, rx: args.rx, ry: args.ry}, args.settings || {}));
+ },
+
+ /* Draw a line.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param x1 (number) the x-coordinate for the start of the line
+ @param y1 (number) the y-coordinate for the start of the line
+ @param x2 (number) the x-coordinate for the end of the line
+ @param y2 (number) the y-coordinate for the end of the line
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ line: function(parent, x1, y1, x2, y2, settings) {
+ var args = this._args(arguments, ['x1', 'y1', 'x2', 'y2']);
+ return this._makeNode(args.parent, 'line', $.extend(
+ {x1: args.x1, y1: args.y1, x2: args.x2, y2: args.y2}, args.settings || {}));
+ },
+
+ /* Draw a polygonal line.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param points (number[][]) the x-/y-coordinates for the points on the line
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ polyline: function(parent, points, settings) {
+ var args = this._args(arguments, ['points']);
+ return this._poly(args.parent, 'polyline', args.points, args.settings);
+ },
+
+ /* Draw a polygonal shape.
+ @param parent (element or jQuery) the parent node for the new shape (optional)
+ @param points (number[][]) the x-/y-coordinates for the points on the shape
+ @param settings (object) additional settings for the shape (optional)
+ @return (element) the new shape node */
+ polygon: function(parent, points, settings) {
+ var args = this._args(arguments, ['points']);
+ return this._poly(args.parent, 'polygon', args.points, args.settings);
+ },
+
+ /* Draw a polygonal line or shape. */
+ _poly: function(parent, name, points, settings) {
+ var ps = '';
+ for (var i = 0; i < points.length; i++) {
+ ps += points[i].join() + ' ';
+ }
+ return this._makeNode(parent, name, $.extend(
+ {points: $.trim(ps)}, settings || {}));
+ },
+
+ /* Draw text.
+ Specify both of x and y or neither of them.
+ @param parent (element or jQuery) the parent node for the text (optional)
+ @param x (number or number[]) the x-coordinate(s) for the text (optional)
+ @param y (number or number[]) the y-coordinate(s) for the text (optional)
+ @param value (string) the text content or
+ (SVGText) text with spans and references
+ @param settings (object) additional settings for the text (optional)
+ @return (element) the new text node */
+ text: function(parent, x, y, value, settings) {
+ var args = this._args(arguments, ['x', 'y', 'value']);
+ if (typeof args.x == 'string' && arguments.length < 4) {
+ args.value = args.x;
+ args.settings = args.y;
+ args.x = args.y = null;
+ }
+ return this._text(args.parent, 'text', args.value, $.extend(
+ {x: (args.x && isArray(args.x) ? args.x.join(' ') : args.x),
+ y: (args.y && isArray(args.y) ? args.y.join(' ') : args.y)},
+ args.settings || {}));
+ },
+
+ /* Draw text along a path.
+ @param parent (element or jQuery) the parent node for the text (optional)
+ @param path (string) the ID of the path
+ @param value (string) the text content or
+ (SVGText) text with spans and references
+ @param settings (object) additional settings for the text (optional)
+ @return (element) the new text node */
+ textpath: function(parent, path, value, settings) {
+ var args = this._args(arguments, ['path', 'value']);
+ var node = this._text(args.parent, 'textPath', args.value, args.settings || {});
+ node.setAttributeNS($.svg.xlinkNS, 'href', args.path);
+ return node;
+ },
+
+ /* Draw text. */
+ _text: function(parent, name, value, settings) {
+ var node = this._makeNode(parent, name, settings);
+ if (typeof value == 'string') {
+ node.appendChild(node.ownerDocument.createTextNode(value));
+ }
+ else {
+ for (var i = 0; i < value._parts.length; i++) {
+ var part = value._parts[i];
+ if (part[0] == 'tspan') {
+ var child = this._makeNode(node, part[0], part[2]);
+ child.appendChild(node.ownerDocument.createTextNode(part[1]));
+ node.appendChild(child);
+ }
+ else if (part[0] == 'tref') {
+ var child = this._makeNode(node, part[0], part[2]);
+ child.setAttributeNS($.svg.xlinkNS, 'href', part[1]);
+ node.appendChild(child);
+ }
+ else if (part[0] == 'textpath') {
+ var set = $.extend({}, part[2]);
+ set.href = null;
+ var child = this._makeNode(node, part[0], set);
+ child.setAttributeNS($.svg.xlinkNS, 'href', part[2].href);
+ child.appendChild(node.ownerDocument.createTextNode(part[1]));
+ node.appendChild(child);
+ }
+ else { // straight text
+ node.appendChild(node.ownerDocument.createTextNode(part[1]));
+ }
+ }
+ }
+ return node;
+ },
+
+ /* Add a custom SVG element.
+ @param parent (element or jQuery) the parent node for the new element (optional)
+ @param name (string) the name of the element
+ @param settings (object) additional settings for the element (optional)
+ @return (element) the new custom node */
+ other: function(parent, name, settings) {
+ var args = this._args(arguments, ['name']);
+ return this._makeNode(args.parent, args.name, args.settings || {});
+ },
+
+ /* Create a shape node with the given settings. */
+ _makeNode: function(parent, name, settings) {
+ parent = parent || this._svg;
+ var node = this._svg.ownerDocument.createElementNS($.svg.svgNS, name);
+ for (var name in settings) {
+ var value = settings[name];
+ if (value != null && value != null &&
+ (typeof value != 'string' || value != '')) {
+ node.setAttribute($.svg._attrNames[name] || name, value);
+ }
+ }
+ parent.appendChild(node);
+ return node;
+ },
+
+ /* Add an existing SVG node to the diagram.
+ @param parent (element or jQuery) the parent node for the new node (optional)
+ @param node (element) the new node to add or
+ (string) the jQuery selector for the node or
+ (jQuery collection) set of nodes to add
+ @return (SVGWrapper) this wrapper */
+ add: function(parent, node) {
+ var args = this._args((arguments.length == 1 ? [null, parent] : arguments), ['node']);
+ var svg = this;
+ args.parent = args.parent || this._svg;
+ try {
+ if ($.svg._renesis) {
+ throw 'Force traversal';
+ }
+ args.parent.appendChild(args.node.cloneNode(true));
+ }
+ catch (e) {
+ args.node = (args.node.jquery ? args.node : $(args.node));
+ args.node.each(function() {
+ var child = svg._cloneAsSVG(this);
+ if (child) {
+ args.parent.appendChild(child);
+ }
+ });
+ }
+ return this;
+ },
+
+ /* SVG nodes must belong to the SVG namespace, so clone and ensure this is so. */
+ _cloneAsSVG: function(node) {
+ var newNode = null;
+ if (node.nodeType == 1) { // element
+ newNode = this._svg.ownerDocument.createElementNS(
+ $.svg.svgNS, this._checkName(node.nodeName));
+ for (var i = 0; i < node.attributes.length; i++) {
+ var attr = node.attributes.item(i);
+ if (attr.nodeName != 'xmlns' && attr.nodeValue) {
+ if (attr.prefix == 'xlink') {
+ newNode.setAttributeNS($.svg.xlinkNS, attr.localName, attr.nodeValue);
+ }
+ else {
+ newNode.setAttribute(this._checkName(attr.nodeName), attr.nodeValue);
+ }
+ }
+ }
+ for (var i = 0; i < node.childNodes.length; i++) {
+ var child = this._cloneAsSVG(node.childNodes[i]);
+ if (child) {
+ newNode.appendChild(child);
+ }
+ }
+ }
+ else if (node.nodeType == 3) { // text
+ if ($.trim(node.nodeValue)) {
+ newNode = this._svg.ownerDocument.createTextNode(node.nodeValue);
+ }
+ }
+ else if (node.nodeType == 4) { // CDATA
+ if ($.trim(node.nodeValue)) {
+ try {
+ newNode = this._svg.ownerDocument.createCDATASection(node.nodeValue);
+ }
+ catch (e) {
+ newNode = this._svg.ownerDocument.createTextNode(
+ node.nodeValue.replace(/&/g, '&').
+ replace(//g, '>'));
+ }
+ }
+ }
+ return newNode;
+ },
+
+ /* Node names must be lower case and without SVG namespace prefix. */
+ _checkName: function(name) {
+ name = (name.substring(0, 1) >= 'A' && name.substring(0, 1) <= 'Z' ?
+ name.toLowerCase() : name);
+ return (name.substring(0, 4) == 'svg:' ? name.substring(4) : name);
+ },
+
+ /* Load an external SVG document.
+ @param url (string) the location of the SVG document or
+ the actual SVG content
+ @param settings (boolean) see addTo below or
+ (function) see onLoad below or
+ (object) additional settings for the load with attributes below:
+ addTo (boolean) true to add to what's already there,
+ or false to clear the canvas first
+ changeSize (boolean) true to allow the canvas size to change,
+ or false to retain the original
+ onLoad (function) callback after the document has loaded,
+ 'this' is the container, receives SVG object and
+ optional error message as a parameter
+ @return (SVGWrapper) this root */
+ load: function(url, settings) {
+ settings = (typeof settings == 'boolean'? {addTo: settings} :
+ (typeof settings == 'function'? {onLoad: settings} : settings || {}));
+ if (!settings.addTo) {
+ this.clear(false);
+ }
+ var size = [this._svg.getAttribute('width'), this._svg.getAttribute('height')];
+ var wrapper = this;
+ // Report a problem with the load
+ var reportError = function(message) {
+ message = $.svg.local.errorLoadingText + ': ' + message;
+ if (settings.onLoad) {
+ settings.onLoad.apply(wrapper._container || wrapper._svg, [wrapper, message]);
+ }
+ else {
+ wrapper.text(null, 10, 20, message);
+ }
+ };
+ // Create a DOM from SVG content
+ var loadXML4IE = function(data) {
+ var xml = new ActiveXObject('Microsoft.XMLDOM');
+ xml.validateOnParse = false;
+ xml.resolveExternals = false;
+ xml.async = false;
+ xml.loadXML(data);
+ if (xml.parseError.errorCode != 0) {
+ reportError(xml.parseError.reason);
+ return null;
+ }
+ return xml;
+ };
+ // Load the SVG DOM
+ var loadSVG = function(data) {
+ if (!data) {
+ return;
+ }
+ if (data.documentElement.nodeName != 'svg') {
+ var errors = data.getElementsByTagName('parsererror');
+ var messages = (errors.length ? errors[0].getElementsByTagName('div') : []); // Safari
+ reportError(!errors.length ? '???' :
+ (messages.length ? messages[0] : errors[0]).firstChild.nodeValue);
+ return;
+ }
+ var attrs = {};
+ for (var i = 0; i < data.documentElement.attributes.length; i++) {
+ var attr = data.documentElement.attributes.item(i);
+ if (!(attr.nodeName == 'version' || attr.nodeName.substring(0, 5) == 'xmlns')) {
+ attrs[attr.nodeName] = attr.nodeValue;
+ }
+ }
+ wrapper.configure(attrs, true);
+ var nodes = data.documentElement.childNodes;
+ for (var i = 0; i < nodes.length; i++) {
+ try {
+ if ($.svg._renesis) {
+ throw 'Force traversal';
+ }
+ wrapper._svg.appendChild(nodes[i].cloneNode(true));
+ if (nodes[i].nodeName == 'script') {
+ $.globalEval(nodes[i].textContent);
+ }
+ }
+ catch (e) {
+ wrapper.add(null, nodes[i]);
+ }
+ }
+ if (!settings.changeSize) {
+ wrapper.configure({width: size[0], height: size[1]});
+ }
+ if (settings.onLoad) {
+ settings.onLoad.apply(wrapper._container || wrapper._svg, [wrapper]);
+ }
+ };
+ if (url.match('