aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--action.php10
-rw-r--r--admin/batch_manager_global.php8
-rw-r--r--admin/include/configuration_watermark_process.inc.php35
-rw-r--r--admin/include/functions.php119
-rw-r--r--admin/picture_modify.php1
-rw-r--r--admin/themes/default/fontello/README.txt4
-rw-r--r--admin/themes/default/fontello/config.json6
-rw-r--r--admin/themes/default/fontello/css/fontello-codes.css1
-rw-r--r--admin/themes/default/fontello/css/fontello-embedded.css13
-rw-r--r--admin/themes/default/fontello/css/fontello-ie7-codes.css1
-rw-r--r--admin/themes/default/fontello/css/fontello-ie7.css1
-rw-r--r--admin/themes/default/fontello/css/fontello.css14
-rw-r--r--admin/themes/default/fontello/demo.html19
-rw-r--r--admin/themes/default/fontello/font/fontello.eotbin19304 -> 24420 bytes
-rw-r--r--admin/themes/default/fontello/font/fontello.svg204
-rw-r--r--admin/themes/default/fontello/font/fontello.ttfbin19136 -> 24252 bytes
-rw-r--r--admin/themes/default/fontello/font/fontello.woffbin12360 -> 15608 bytes
-rw-r--r--admin/themes/default/fontello/font/fontello.woff2bin0 -> 13244 bytes
-rw-r--r--admin/themes/default/template/permalinks.tpl9
-rw-r--r--admin/themes/default/template/picture_modify.tpl1
-rw-r--r--admin/themes/default/template/rating_user.tpl19
-rw-r--r--admin/themes/default/template/user_list.tpl72
-rw-r--r--identification.php6
-rw-r--r--include/config_default.inc.php10
-rw-r--r--include/constants.php2
-rw-r--r--include/functions.inc.php18
-rw-r--r--include/functions_user.inc.php42
-rw-r--r--include/ws_functions/pwg.users.php52
-rw-r--r--install/config.sql2
-rw-r--r--install/db/149-database.php40
-rw-r--r--install/piwigo_structure-mysql.sql2
-rw-r--r--language/en_UK/common.lang.php3
-rw-r--r--language/en_UK/upgrade.lang.php3
-rw-r--r--language/fr_FR/common.lang.php5
-rw-r--r--language/fr_FR/upgrade.lang.php3
-rw-r--r--themes/default/js/plugins/datatables/css/demo_page.css122
-rw-r--r--themes/default/js/plugins/datatables/css/demo_table.css577
-rw-r--r--themes/default/js/plugins/datatables/css/demo_table_jui.css501
-rw-r--r--themes/default/js/plugins/datatables/css/jquery.dataTables.css604
-rw-r--r--themes/default/js/plugins/datatables/css/jquery.dataTables_themeroller.css244
-rw-r--r--themes/default/js/plugins/datatables/images/Sorting icons.psdbin27490 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/back_disabled.pngbin1361 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/back_enabled.pngbin1379 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/back_enabled_hover.pngbin1375 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/favicon.icobin894 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/forward_disabled.pngbin1363 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/forward_enabled.pngbin1380 -> 0 bytes
-rw-r--r--themes/default/js/plugins/datatables/images/forward_enabled_hover.pngbin1379 -> 0 bytes
-rw-r--r--themes/default/js/plugins/jquery.dataTables.js19077
-rw-r--r--themes/elegant/theme.css17
-rw-r--r--upgrade.php12
51 files changed, 12100 insertions, 9779 deletions
diff --git a/action.php b/action.php
index 5a7ca423e..b87e16054 100644
--- a/action.php
+++ b/action.php
@@ -100,6 +100,14 @@ if ( empty($element_info) )
do_error(404, 'Requested id not found');
}
+// special download action for admins
+$is_admin_download = false;
+if (is_admin() and isset($_GET['pwg_token']) and get_pwg_token() == $_GET['pwg_token'])
+{
+ $is_admin_download = true;
+ $user['enabled_high'] = true;
+}
+
$src_image = new SrcImage($element_info);
// $filter['visible_categories'] and $filter['visible_images']
@@ -118,7 +126,7 @@ SELECT id
).'
LIMIT 1
;';
-if ( pwg_db_num_rows(pwg_query($query))<1 )
+if (!$is_admin_download and pwg_db_num_rows(pwg_query($query))<1 )
{
do_error(401, 'Access denied');
}
diff --git a/admin/batch_manager_global.php b/admin/batch_manager_global.php
index 1d5cb747d..332f3f7a7 100644
--- a/admin/batch_manager_global.php
+++ b/admin/batch_manager_global.php
@@ -123,6 +123,8 @@ DELETE
{
if (isset($_POST['del_tags']) and count($_POST['del_tags']) > 0)
{
+ $taglist_before = get_image_tag_ids($collection);
+
$query = '
DELETE
FROM '.IMAGE_TAG_TABLE.'
@@ -130,7 +132,11 @@ DELETE
AND tag_id IN ('.implode(',', $_POST['del_tags']).')
;';
pwg_query($query);
-
+
+ $taglist_after = get_image_tag_ids($collection);
+ $images_to_update = compare_image_tag_lists($taglist_before, $taglist_after);
+ update_images_lastmodified($images_to_update);
+
if (isset($_SESSION['bulk_manager_filter']['tags']) &&
count(array_intersect($_SESSION['bulk_manager_filter']['tags'], $_POST['del_tags'])))
{
diff --git a/admin/include/configuration_watermark_process.inc.php b/admin/include/configuration_watermark_process.inc.php
index bc1451791..0e07380a7 100644
--- a/admin/include/configuration_watermark_process.inc.php
+++ b/admin/include/configuration_watermark_process.inc.php
@@ -26,6 +26,21 @@ if( !defined("PHPWG_ROOT_PATH") )
die ("Hacking attempt!");
}
+function get_watermark_filename($list, $candidate, $step = 0)
+{
+ global $change_name;
+ $change_name = $candidate;
+ if ($step != 0)
+ {
+ $change_name .= '-'.$step;
+ }
+ if (in_array($change_name, $list))
+ {
+ return get_watermark_filename($list, $candidate, $step+1);
+ }
+ return $change_name.'.png';
+}
+
$errors = array();
$pwatermark = $_POST['w'];
@@ -45,8 +60,22 @@ if (isset($_FILES['watermarkImage']) and !empty($_FILES['watermarkImage']['tmp_n
$upload_dir = PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'watermarks';
if (mkgetdir($upload_dir, MKGETDIR_DEFAULT&~MKGETDIR_DIE_ON_ERROR))
{
- $new_name = get_filename_wo_extension($_FILES['watermarkImage']['name']).'.png';
- $file_path = $upload_dir.'/'.$new_name;
+ // file name may include exotic chars like single quote, we need a safe name
+ $new_name = str2url(get_filename_wo_extension($_FILES['watermarkImage']['name']));
+
+ // we need existing watermarks to avoid overwritting one
+ $watermark_files = array();
+ if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'watermarks/*.png')) !== false)
+ {
+ foreach ($glob as $file)
+ {
+ $watermark_files[] = get_filename_wo_extension(
+ substr($file, strlen(PHPWG_ROOT_PATH.PWG_LOCAL_DIR.'watermarks/'))
+ );
+ }
+ }
+
+ $file_path = $upload_dir.'/'.get_watermark_filename($watermark_files, $new_name);
if (move_uploaded_file($_FILES['watermarkImage']['tmp_name'], $file_path))
{
@@ -183,4 +212,4 @@ else
$template->assign('watermark', $pwatermark);
$template->assign('ferrors', $errors);
}
-?> \ No newline at end of file
+?>
diff --git a/admin/include/functions.php b/admin/include/functions.php
index 9a827d1d6..030e7fa31 100644
--- a/admin/include/functions.php
+++ b/admin/include/functions.php
@@ -1519,6 +1519,8 @@ function add_tags($tags, $images)
return;
}
+ $taglist_before = get_image_tag_ids($images);
+
// we can't insert twice the same {image_id,tag_id} so we must first
// delete lines we'll insert later
$query = '
@@ -1545,6 +1547,11 @@ DELETE
array_keys($inserts[0]),
$inserts
);
+
+ $taglist_after = get_image_tag_ids($images);
+ $images_to_update = compare_image_tag_lists($taglist_before, $taglist_after);
+ update_images_lastmodified($images_to_update);
+
invalidate_user_cache_nb_tags();
}
@@ -1565,6 +1572,15 @@ function delete_tags($tag_ids)
return false;
}
+ // we need the list of impacted images, to update their lastmodified
+ $query = '
+SELECT
+ image_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE tag_id IN ('.implode(',', $tag_ids).')
+;';
+ $image_ids = query2array($query, null, 'image_id');
+
$query = '
DELETE
FROM '.IMAGE_TAG_TABLE.'
@@ -1579,6 +1595,7 @@ DELETE
;';
pwg_query($query);
+ update_images_lastmodified($image_ids);
invalidate_user_cache_nb_tags();
}
@@ -1662,6 +1679,9 @@ function set_tags_of($tags_of)
{
if (count($tags_of) > 0)
{
+ $taglist_before = get_image_tag_ids(array_keys($tags_of));
+ global $logger; $logger->debug('taglist_before', $taglist_before);
+
$query = '
DELETE
FROM '.IMAGE_TAG_TABLE.'
@@ -1691,11 +1711,83 @@ DELETE
);
}
+ $taglist_after = get_image_tag_ids(array_keys($tags_of));
+ global $logger; $logger->debug('taglist_after', $taglist_after);
+ $images_to_update = compare_image_tag_lists($taglist_before, $taglist_after);
+ global $logger; $logger->debug('$images_to_update', $images_to_update);
+
+ update_images_lastmodified($images_to_update);
invalidate_user_cache_nb_tags();
}
}
/**
+ * Get list of tag ids for each image. Returns an empty list if the image has
+ * no tags.
+ *
+ * @since 2.9
+ * @param array $image_ids
+ * @return associative array, image_id => list of tag ids
+ */
+function get_image_tag_ids($image_ids)
+{
+ if (!is_array($image_ids) and is_int($image_ids))
+ {
+ $images_ids = array($image_ids);
+ }
+
+ if (count($image_ids) == 0)
+ {
+ return array();
+ }
+
+ $query = '
+SELECT
+ image_id,
+ tag_id
+ FROM '.IMAGE_TAG_TABLE.'
+ WHERE image_id IN ('.implode(',', $image_ids).')
+;';
+
+ $tags_of = array_fill_keys($image_ids, array());
+ $image_tags = query2array($query);
+ foreach ($image_tags as $image_tag)
+ {
+ $tags_of[ $image_tag['image_id'] ][] = $image_tag['tag_id'];
+ }
+
+ return $tags_of;
+}
+
+/**
+ * Compare the list of tags, for each image. Returns image_ids where tag list has changed.
+ *
+ * @since 2.9
+ * @param array $taglist_before - for each image_id (key), list of tag ids
+ * @param array $taglist_after - for each image_id (key), list of tag ids
+ * @return array - image_ids where the list has changed
+ */
+function compare_image_tag_lists($taglist_before, $taglist_after)
+{
+ $images_to_update = array();
+
+ foreach ($taglist_after as $image_id => $list_after)
+ {
+ sort($list_after);
+
+ $list_before = isset($taglist_before[$image_id]) ? $taglist_before[$image_id] : array();
+ sort($list_before);
+
+ if ($list_after != $list_before)
+ {
+ $images_to_update[] = $image_id;
+ }
+ }
+
+ return $images_to_update;
+}
+
+/**
* Associate a list of images to a list of categories.
* The function will not duplicate links and will preserve ranks.
*
@@ -2887,3 +2979,30 @@ function save_images_order($category_id, $images)
);
mass_updates(IMAGE_CATEGORY_TABLE, $fields, $datas);
}
+
+/**
+ * Force update on images.lastmodified column. Useful when modifying the tag
+ * list.
+ *
+ * @since 2.9
+ * @param array $image_ids
+ */
+function update_images_lastmodified($image_ids)
+{
+ if (!is_array($image_ids) and is_int($image_ids))
+ {
+ $images_ids = array($image_ids);
+ }
+
+ if (count($image_ids) == 0)
+ {
+ return;
+ }
+
+ $query = '
+UPDATE '.IMAGES_TABLE.'
+ SET lastmodified = NOW()
+ WHERE id IN ('.implode(',', $image_ids).')
+;';
+ pwg_query($query);
+}
diff --git a/admin/picture_modify.php b/admin/picture_modify.php
index ab62c5624..af5231ff6 100644
--- a/admin/picture_modify.php
+++ b/admin/picture_modify.php
@@ -234,6 +234,7 @@ $src_image = new SrcImage($row);
$template->assign(
array(
'tag_selection' => $tag_selection,
+ 'U_DOWNLOAD' => 'action.php?id='.$_GET['image_id'].'&amp;part=e&amp;pwg_token='.get_pwg_token().'&amp;download',
'U_SYNC' => $admin_url_start.'&amp;sync_metadata=1',
'U_DELETE' => $admin_url_start.'&amp;delete=1&amp;pwg_token='.get_pwg_token(),
diff --git a/admin/themes/default/fontello/README.txt b/admin/themes/default/fontello/README.txt
index a91438a9a..beaab3366 100644
--- a/admin/themes/default/fontello/README.txt
+++ b/admin/themes/default/fontello/README.txt
@@ -2,14 +2,14 @@ This webfont is generated by http://fontello.com open source project.
================================================================================
-Please, note, that you should obey original font licences, used to make this
+Please, note, that you should obey original font licenses, used to make this
webfont pack. Details available in LICENSE.txt file.
- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
site in "About" section.
- If your project is open-source, usually, it will be ok to make LICENSE.txt
- file publically available in your repository.
+ file publicly available in your repository.
- Fonts, used in Fontello, don't require a clickable link on your site.
But any kind of additional authors crediting is welcome.
diff --git a/admin/themes/default/fontello/config.json b/admin/themes/default/fontello/config.json
index 20c3fe735..5ccdee465 100644
--- a/admin/themes/default/fontello/config.json
+++ b/admin/themes/default/fontello/config.json
@@ -407,6 +407,12 @@
"css": "asl",
"code": 57517,
"src": "elusive"
+ },
+ {
+ "uid": "b08cfe8039de2ce815686aced2caef06",
+ "css": "download",
+ "code": 59430,
+ "src": "entypo"
}
]
} \ No newline at end of file
diff --git a/admin/themes/default/fontello/css/fontello-codes.css b/admin/themes/default/fontello/css/fontello-codes.css
index d31cf3433..87ab60167 100644
--- a/admin/themes/default/fontello/css/fontello-codes.css
+++ b/admin/themes/default/fontello/css/fontello-codes.css
@@ -53,6 +53,7 @@
.icon-plus:before { content: '\e823'; } /* '' */
.icon-ok-circled:before { content: '\e824'; } /* '' */
.icon-heart-broken:before { content: '\e825'; } /* '' */
+.icon-download:before { content: '\e826'; } /* '' */
.icon-zoom-in:before { content: '\e827'; } /* '' */
.icon-zoom-out:before { content: '\e828'; } /* '' */
.icon-tasks:before { content: '\f0ae'; } /* '' */
diff --git a/admin/themes/default/fontello/css/fontello-embedded.css b/admin/themes/default/fontello/css/fontello-embedded.css
index e17d676a8..c10688166 100644
--- a/admin/themes/default/fontello/css/fontello-embedded.css
+++ b/admin/themes/default/fontello/css/fontello-embedded.css
@@ -2,16 +2,16 @@
@font-face {
font-family: 'fontello';
- src: url('../font/fontello.eot?37675340');
- src: url('../font/fontello.eot?37675340#iefix') format('embedded-opentype'),
- url('../font/fontello.svg?37675340#fontello') format('svg');
+ src: url('../font/fontello.eot?12405355');
+ src: url('../font/fontello.eot?12405355#iefix') format('embedded-opentype'),
+ url('../font/fontello.svg?12405355#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'fontello';
- src: url('data:application/octet-stream;base64,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') format('woff'),
- url('data:application/octet-stream;base64,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JUmwAiVhuQgACABjYyMgWGIbIVljuAQAYiCwAFBYsEBgWWawAWNgIy4jICA8ijgjIVktsDossAAWILAIQyAuRyNHI2EgYLAgYGawAmIgsABQWLBAYFlmsAFjIyAgPIo4LbA7LCMgLkawAiVGUlggPFkusSsBFCstsDwsIyAuRrACJUZQWCA8WS6xKwEUKy2wPSwjIC5GsAIlRlJYIDxZIyAuRrACJUZQWCA8WS6xKwEUKy2wPiywNSsjIC5GsAIlRlJYIDxZLrErARQrLbA/LLA2K4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrErARQrsARDLrArKy2wQCywABawBCWwBCYgLkcjRyNhsAlDKyMgPCAuIzixKwEUKy2wQSyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2GwAiVGYTgjIDwjOBshICBGI0ewASsjYTghWbErARQrLbBCLLA1Ky6xKwEUKy2wQyywNishIyAgPLAEI0IjOLErARQrsARDLrArKy2wRCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKxAAAqsQAFQrEACCqxAAVCsQAIKrEABUK5AAAACSqxAAVCuQAAAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbEADCq4Af+FsASNsQIARAA=') format('truetype');
+ src: url('data:application/octet-stream;base64,d09GRgABAAAAADz4AA8AAAAAXrwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADMAAABCsP6z7U9TLzIAAAGMAAAAQwAAAFZ8JWEpY21hcAAAAdAAAAITAAAFmqmZ4C5jdnQgAAAD5AAAABMAAAAgBt3/BGZwZ20AAAP4AAAFkAAAC3CKkZBZZ2FzcAAACYgAAAAIAAAACAAAABBnbHlmAAAJkAAALgUAAEPKlWiSGmhlYWQAADeYAAAAMwAAADYLGKLxaGhlYQAAN8wAAAAgAAAAJAf2BEtobXR4AAA37AAAAJAAAAEU993/omxvY2EAADh8AAAAjAAAAIxd1G5/bWF4cAAAOQgAAAAgAAAAIAG5DLpuYW1lAAA5KAAAAXcAAALNzJ0cHnBvc3QAADqgAAAB2gAAAq0HxCSmcHJlcAAAPHwAAAB6AAAAhuVBK7x4nGNgZGBg4GKQY9BhYHRx8wlh4GBgYYAAkAxjTmZ6IlAMygPKsYBpDiBmg4gCAIojA08AeJxjYGSezjiBgZWBgamKaQ8DA0MPhGZ8wGDIyAQUZWBlZsAKAtJcUxgc1Fj//2cO+p/FEMUcxjANKMwIkgMA9L0MIQB4nN3UyU7bUBjF8b9JCCSQMM9TGGInneeZQodHYNV1n6LvwYJnqKhY8TzZ+dth1GFLj32qSl0gsa6tX25sXcV27jkGRoGa3JE6jHwi0TeSI51NqvM1WtX5etLXcZ+u5o2njfQkvciaWSc7Hp7mrbydd/NBvp+fR7/4VpwVwyIue1dXkI5eO5N/Z95oS3QHt67dv/zdy5kjuve6nrDBGOM09RwTTNKmwxTTzDDLHPMssMgSy6ywyhrrbLDJlp5ymx122aNHSqarDvT7t/V/3OUe93nAQx7xmCc85RnPecFLXvGaN7pw44bP8j9vk+VH7fDP0ccyB1amKm2YVoj0xMoUphdWJjFrWpnQrGNaSbJj05oyPDWtLnnLtM7kbSuTm3dNa08+sPLu8n2jnHtuSgaBKSNEYkoLMWLKDVEzJYiom7JEjJpSRTRM+SLGTEkjxk2ZI5qm9BEtUw6JCVMiiUlTNom2KaVEx5RXYsqUXGLalGFixpRmYtaUa2LOlHBi3pR1YsGUemLRlH9iydQEYtnUCWLF1A5i1dQTYs3UGGLd1B1iw8q3TWya+kRsmZpFdE0dI7ZNbSN2TL0jdk0NJPZMXSR6plYSqamfRGZqKtE3dVbvH1N7Kc5MPaYYmhpNEaZuc9kztTz5Xq/APsmPzxV4S/KTChxonK3AocalCrzTmFXgvcajCnwg+fW1or78BvdCzwMAeJxjYEADEhDIHPY/C4QBEpQD5QB4nK1WaXfTRhQdeUmchCwlCy1qYcTEabBGJmzBgAlBsmMgXZytlaCLFDvpvvGJ3+Bf82Tac+g3flrvGy8kkLTncJqTo3fnzdXM22USWpLYC+uRlJsvxdTWJo3sPAnphk3LUXwoO3shZYrJ3wVREK2W2rcdh0REIlC1rrBEEPseWZpkfOhRRsu2pFdNyi096S5b40G9Vd9+GjrKsTuhpGYzdGg9siVVGFWiSKY9UtKmZaj6K0krvL/CzFfNUMKITiJpvBnG0EjeG2e0ymg1tuMoimyy3ChSJJrhQRR5lNUS5+SKCQzKB82Q8sqnEeXD/Iis2KOcVrBLttP8vi95p3c5P7Ffb1G25EAfyI7s4Ox0JV+EW1th3LST7ShUEXbXd0Js2exU/2aP8ppGA7crMr3QjGCpfIUQKz+hzP4hWS2cT/mSR6NaspETQetlTuxLPoHW44gpcc0YWdDd0QkR1P2SMwz2mD4e/PHeKZYLEwJ4HMt6RyWcCBMpYXM0SdowcmAlZYsqqfWumDjldVrEW8J+7drRl85o41B3YjxbDx1bOVHJ8WhSp5lMndpJzaMpDaKUdCZ4zK8DKD+iSV5tYzWJlUfTOGbGhEQiAi3cS1NBLDuxpCkEzaMZvbkbprl2LVqkyQP13KP39OZWuLnTU9oO9LNGf1anYjrYC9PpaeQv8Wna5SJF6frpGX5M4kHWAjKRLTbDlIMHb/0O0svXlhyF1wbY7u3zK6h91kTwpAH7G9AeT9UpCUyFmFWIVkBirWtZlsnVrBapyNR3Q5pWvqzTBIpyHBfHvoxx/V8zM5aYEr7fidOzIy49c+1LCNMcfJt1PZrXqcVyAXFmeU6nWZbv6zTH8gOd5lme1+kIS1unoyw/1GmB5Uc6HWN5QQuadN/BkIsw5AIOkDCEpQNDWF6CISwVDGG5CENYFmEIyyUYwvJjGMJyGYawvKxl1dRTSePamVgGbEJgYo4eucxF5WoquVRCu2hUakOeEm6VVBTPqn9loF488oY5sBZIl8iaXzHOlY9G5fjWFS1vGjtXwLHqbx+O9jnxUtaLhT8F/9XWVCW9Ys3Dk6vwG4aebCeqNql4dE2Xz1U9uv5fVFRYC/QbSIVYKMqybHBnIoSPOp2GaqCVQ8xszDy063XLmp/D/TcxQhZQ/fg3FBoL3INOWUlZ7eCs1dfbstw7g3I4EyxJMTfz+lb4IiOz0n6RWcqej3wecAWMSmXYagOtFbzZJzEPmd4kzwRxW1E2SNrYzgSJDRzzgHnznQQmYeqqDeRO4YYN+AVhbsF5J1yieqMsh+5F7PMopPxbp+JE9qhojMCz2Rthr+9Cym9xDCQ0+aV+DFQVoakYNRXQNFJuqAZfxtm6bULGDvQjKnbDsqziw8cW95WSbRmEfKSI1aOjn9Zeok6q3H5mFJfvnb4FwSA1MX9733RxkMq7WskyR20DU7calVPXmkPjVYfq5lH1vePsEzlrmm66Jx56X9Oq28HFXCyw9m0O0lImF9T1YYUNosvFpVDqZTRJ77gHGBYY0O9Qio3/q/rYfJ4rVYXRcSTfTtS30edgDPwP2H9H9QPQ92Pocg0uz/eaE59u9OFsma6iF+un6Dcwa625WboG3NB0A+IhR62OuMoNfKcGcXqkuRzpIeBj3RXiAcAmgMXgE921jOZTAKP5jDk+wOfMYdBkDoMt5jDYZs4awA5zGOwyh8Eecxh8wZx1gC+ZwyBkDoOIOQyeMCcAeMocBl8xh8HXzGHwDXPuA3zLHAYxcxgkzGGwr+nWMMwtXtBdoLZBVaADU09Y3MPiUFNlyP6OF4b9vUHM/sEgpv6o6faQ+hMvDPVng5j6i0FM/VXTnSH1N14Y6u8GMfUPg5j6TL8Yy2UGv4x8lwoHlF1sPufvifcP28VAuQABAAH//wAPeJylfAt0G8d57rz2gcVisQAWuyAeSxBvghQIgXhQFEVBFEVSFCTTNMVQD9OMLMkKqVcUVdVNbNexfRwndSUfNfV1lJzUSm3XdZxcR3aTNHXl3sRJexyn13nUTuv0npymN0du0iQ39em1dSXo/rMAJdpOmpxzKWB2Z3ZmsTPz/9///f/MCmGErr5Iz9E6iqNKXQnobpEKiODxLU9bU3N1FWNE0FFEiEo2R+oKZMgRKEP7d/x5zAhRwerBhhcHzKCh4T4siYlMpVwLZHmarlVLnVgw6TnvN4tYDar/95InqOLiN7VOHLrTHVfvwKE4fl31fr35OpPdOpbuvVfyK0zG1te9alDINS2rmRMQ/ObyM7pQHtXrvlg4qHtcsiQKFKu/1cPWXZl0yAr4qGD04Fof9mJLqlmB9qMnE7/m0ckHP/uTxff99LPd3/1uk3fCUn51JxKPJf/+75OP/eTIEXy+3Z/or+tNqz9foDupG8loEW2qj+zb0djAEFurEIzKuYjOKKbjSGDCERFD+RGEMDqCMMVHECX0CHRw/+5dN924eaInn4gH/JJg9uByJqFhs1RNBw3Ri0XJtExD0nAWuiZK8K8HQ/+ymSz0E9JMuTaMa5k+XMDZSrm6Hldr7cJ+6Hi1Bv/W4pLZiU0LhsJq30yCAhuTtdMnp8ns8VkclaUDijuQEwXvlEeStnaEXRLTb5dVPWLdIOrimMkEOad45f2SjBXhgKxZ6VZdeWso7JKp73ZJxd6oOSV4pQmDMVdO0Vz7JQXPr52ZOTEzc5Jf1+1gpCRqYnAKC0MeuRHVFek2lzokiHVb0ES15I1GvFiVnLod4fgqSZWMqRVV3WsFYWO0XTWsg8ggJMIcHGN9dDPMgR9F0IfQl9C/o0/VP/GLfyRMW5ongvztv3o/RcLznzt34qatY8mYC6Nn/3gDJXhtgYjkoQ8TN5XGX/8B0Tbdg92jCpZdgksWDiGRSESUDiENMZfGFpBLRq4ZJHgwwgJaQETEZIZLqkSxtICo201nEKXuOeSm7saP/9ff/s1n/+yB3z98aN+tu3ZW+nvyAcMwAn6vEHamWDRgioVyRsMWP6O8yMalqvT2WU/+ylmvXJ/0dfi3mPSg4dySzzkOGnA7k6fOvRNZkIbWLeEWcDdoDBm4ZX8JWltwc+cG/IF8K9tmsk7bX9sUfndlW/wGF4MTM/hzvQO9OD/Yi9vH78vCrKgGOhnzjLhFoR40RYl5DoiKJ2CNMI84xYSU7JG2CzJUlNy8ojqqiGI9EBIlyitiT8AcETzCjSzkkz0i1MT7bhKVKBnFQjDmVt1SL8WjNKZIN90kKTFa1jHLyz5f1GJkI4m6oLhdOy87tdm7KquKer32skz/KNZLcp2xfJ7sgGRtPv+t2+BhtIAVSYsq842wklscinhkcTvIK2NbvIIg96oh04Nl6Z01FXko7NR0r27VtIOeSEAlcvN/bHPJXm29RkguksbYXcY5QiDvlV3bXC7dw6/YrjIIYs7CWX7Jo7tIP1xrtcryS2o522rkgkby9UZw02uNZITo1auAZxfJ11AHWoM2ozX1asSPKSUBwGZEAMkQoQKZRxSAjIIOYMbwDMKYzSGGWaOyblVHfkAEEU/7uNRqhIttHwbxyPoMs7+rVA1UICtmuxKiZAAa2azWXxqmIDkg0D5DlLoSmZqvXIWqYG78wT/aEU0NRUh42FeYeyjoV0VaBVAhlDLx8otwNn3wOIv74yGVuMO2YQvHD9rl8XL68oupKi6naTVdduOnDN+xyVA0HNYyk8d8RnNGVhQyrWmCQKgqzm+YuZlENUPFiqna+OaZ0HTlyrl0GVdT5JZ0ucxxnjk4v5VcBbQxYGxS6L66BfgpxExNYpR0AJgjigELGFgwP1iwLBKIAPhOCSA9ohz4MbpVxHzApuGA2Xv4iG2J1DPvromOvLvijrofoa54yNK9gEMiEg1JCPZYtawU7A/icjIhYTFo9JdquJq1cLLCjXnWMX7mN0v39E/gW1SBNb/NPALDfdS+2CxepFuNmy/ebKw17zGk/nv6h8YJCGPzOwxSXGDvv9jsex2fjQVvfn13MHiP6di7q1dgHG4Be5dERbShPsyxsBPDCIyDfIBZQ/SQgCmDzxKSoCMSXkBMFNkMYkycQyITG8FgMpVOpgxZiPbgSjkjJSHJJkEegpBYQcOs9UNi9ZeqtQpPgI8kCmQYl2DA/9TUTpvauNfApzUTung9c/7YNy7+7WHxg8+98ZU78eAEXD0NF6C64V2Z6f+dF44ff+FfecLnFfrzVfr38NyrUA1tQFtQvp7dMj6yYWgg4tMkwY02cSZC0AwnH2SO85BGT7e/h4KhDmSqgKgiF2JcK9XMag2OwxjgL1sx4cQCINMoQGVXJtuqa4pwNozhzGifWQDxmSxmmbKrAhBtS6VcXySxpyMyF1N10DQcTq4+meliXetmi6O9wVXNz8wER8TqoBxk+ZJ6kgzVmTwwiB9mI4NYFAZLjDyXjpuRL8eS9ufLQzidZdm03KnHg11qNKMkVo10hMaMjmOHb+/0i0fx2VW5UOrb3b1aBnd22/GuN9cmerMY9YQyXc2+TK6Pq/nVtwAP7kA+lED9aKy+0U0IIzDtMOWIgdAyegiqgfgvgeAShJdANgkTCWCDIDjYIMwhAQsNy0wZwZApCZEe6DJYhRZNEyUbx0FXM1IC5BcsIZAYmHUBbAyLp7hRq/ZiZgEYbL8rceblM4m7tm/5IWY/bP6F7h7bq5v6aBEI5z+4tzXfbP5j881tbvc2LOMMlre58eC9GwY3Hjhz5sDGwQ33Hrv/frwZqu7d5NZ1d3FUfykQ+PDZsx8OZIy7zpJP32mAPPD+/hVLOXwuhsqoWF+V4ciHeV/ZEdBwhI8K0E1EZuBA0BzngI14R/dA2M9hL8ANuk2q2JC4aW3b5xq3hzDZvJAbTlwt80vJVvElMIIkN5wh243mz8y1RvP9QTsfez02EcSnjVeDUBYzgOuIwSBcmYjd4VwIkjk7TzL1lFhs/oMNlYK8cCJ2MZbHNq8Ycwp1H7Rbyy/ZTmmbsz7tYHwa5H19fWhzFKZtPcB5N8fVcQXUeKyEySioL9BUGBPOVYnDbbgGUNIYqQ8PJRNJBgCEDc5RshzsoWeVTLlaWU+qUm2Y1ICQlh2ttQw+ySD+ndQhstwawDhkTa7dw1Ara2PqX3x2qVga2x6MAAphAphCMTA9jW3ahheffeXZxVemRgVVjriYAKSaKHIkuH2sVPyvR+O+nY+ObGpgz8QMfmzrvQ3XaktgisTxk4pwE1sI+fXc8Y3b7m007v3BxhNZzVS63FQAA0xkBTPBWu2apKW+8n2TPfnsSYdPfoH6QQa8qBP1obVoI7oN7a2/9z2biCh3d3X4XFgEUR8XgEECDmN0iIEkSCKSloAqumTNteD1EKAMQBBlcR5Jbrc0gySJE0PJ3di3973zu3bcNH1DY/P4hvVGysjwv6QuxHowGECH54EB5PzN+g35gK/LZ9gYbOUwxv0wqkkRjAKvAxWTYEmzPpgULoFrOTJxMewEhmhiW5FTYAR5cvr66SlFap1KykRTlWWCXyay3PzopQgTviAy/BNFrpbTzSLYxgqv91TW1WueN3vl7OdgIL/UfJ4X4g08/TXnzb3Ed+XnqqEoBtm3QQBsuAl+8crPC6MjBRJwHuLmYBTbxs2Kw0WepufIC6CLPpiFdfXBFEgFqCJ4i9BP4OdIoKIAzJsDEfBzRy05Ws857mOjksr15wMhP7c0AshpAVdtzE8yw0CEA5xtCECygWmADHdxNBomFZpMQD2wPjYRTX4N78sP5kmhWsC/bCvqpKFe+QvJQ7DXJH7fla+DQMn6nmNeg4Q8p12aqBAZk09rphRz2rFM5/IJqTf/2WOQ0YQWMaOmqYkhPUrwS4ZtYlHVVZOClcXQ7xfJ4+R/oxvQ5voYkiUZFBA0gpJDLnBuFdBWCS3A4IH8AQphTOYYXCYNjCY2rR8eqBR6u7PxmGXqHpGhG/ANbu4qc4cDrFTVtIItIaoB0mZaLEzMclQC4LW4SoI1TkhiplZxhKzK+X21ZEoAyCaXoBgYr2yGTKar1bQZDJu42Hla11zB0T2j2ONTjNNdvdhvhiKZajUTDIZDuBA/HVB92tjCGA4quvd0Z9EwQvjM6MIE8fhU/4OdBb/ZEc6UyxnThNsVOh/UNcUY3Vsn/Ha7ugpGxAjxy+GOoIELXbv4ZQRUFF39Z3qJ/CMwVW65b0X70b76np4EkfD+SogSyYIRWucmMh3BgjwkEiaI4zB2AGoSkbhDLoBnjg4BJh4GcaEyoQtIBo4+jbirgQRZmNy399aFXTtnt0/fODE+UM2mAy6OeWDYwXXKcOzSaLYALAb4K/hC2TIgYLW/Vs6WgYiVYuAIVcG7C5peHISRE8waB0oDwDGbAa6TTJt+k09NhSuuc1IN8DGn/ZT+wJ6aV0icsNnhvF6Y2DtQrg+V6uQBlQGusE8RuSAqnxZlIjP5aXX29ubj0QRLhHufiA7ZcmqIyKQ5rMxOsVmx9HSRiLNMeZzIC8rdD07g0vRzIyQjsqEvD2QaMwVZHBucqZf0vSFwhqtg5qKHGdz2gAk/lIevvX/ih6L6ihrtvdsMzY2JfnI+2vzZmyJ7/FHCmJ+YcvM5LLfmA3yHa7i5HuR3e316M5Zca7AgdWMqdIDMbljd1QJQYRy5JBfMhSAdAUUWuJAjsDJAtBzagOZkEZ4G8o2tWzLtv6QCMFlrS3AL2Ermu/LvgMl35gPvyAMELnGIguSVlafSQUlxkld/ZYXC8olSf4af8eQjzQsO2I1A+ndXLvJzEoIUm5df5BlahbRlgz/KvkBLaDXah1bV87tumtq8fl21HNORG3R5nFcBnwATzjnw/ukbB9esLlpBBppccdx9QPMyN6Qt3xtygFfLvrnEiZRlE+ClQasLt2usuK4Rq1UhCNy7QECCswAGLTctY+Q2xYLpHO4MaKs0gQnDi2t6ZoYTlDFtldcXz2Vid+7V/Qa+fIb8d1UgrDS3e7bMwGinVPBMFJywfaUxb1OUTdUaWh0RZOrSPOnVkjfsdgcms5F0MOsf7PFne2qLw4DE0/laJJAf9HcH0pHsZMBbyPl9Q0BmvpvQ48lVcwOkvLsnGdcTRa3Lp4DliA/pLiMkfcQtZRJ5tSPjT/Qqii4q3oTO+fylq2fpv9Mw6gE+P4vmUawe3jFXX8/QKMEtPoP2x6KRsE/nAcSAho0kH7CWNoO7xEeSn9RKVolf4AEzIOYwRMDU+AnOwPCnTQkwkDtY0AUebTF5Xajq3MTENcu55Sdlkfi8TFfH3B4SZ2LehgItY95X/mBYNU7Gp3oTTFYaGssPvonP+o2iqTf3yPKESkp6vHRgoHd6dDSkwdDepapMTWgsM5gRc4T5Lz/RbPyH4S+Y+JIcNb0epvkbqkpCTIwWRc3Wd4pELocGBgsEtFtmxVCpmTL1vObH35fVYfmvDwzPhdXebdvGQoMT5sCA6BfVUaUUzQ2rg6DxV0bfnP2loRf87fjmLtDpFBpHN9d3bkwT0dWHBZFjqwzqzECLZVF2iYckKCWiQA6BMaIioUs8QgAqfQjstyjI4kJbvynlrJmixtimTDpdTWcqRoprNTaCnEo6Ea5rqix6wVJzmtiOaAXaqg46wBF0OTBWq/DIlxPsMumW1L984saHhybc3MJe0kzi2prbW5u8Oytaggrqqhl6q3T66JZW4RFJxakff+LGs7xRCAsUP/SV4TUTbqe5GXVvTecmh5UBj4q/3C7Y2sqLrF3R8Y2vXv0afZ18AYVAq7fWJ3tgQDJgZDgXIXTcJRI0CiMhHkFAY4Ccgt8E3hNZAuRkWGDzDn+Z5vxllqv/ZCATsAJRgztMPBQsdfGE2x0eYeN2mIdIeGCPDxcPm3DPqYsba6Da9KIiX0GKLErnBeEZwatQBJ10zq7wM4AipGhw8a+WJAXzr3he0IXzAiCagnMy1FTFJfjICnyg3OnfW6wDZMGPulFvvRtRRo9wn4/gthEFHux4+txNYKSRHExWK4LQ4SwsOBSrAH7GMOerZjbYlRC9YAdrbRB2usM6/H8Zms1cOReIYt9zodk0uSVg/3WzWDmWfA70F0/2P9M/ifvgu94f+KdoTksYPP1vO8vd/0QlgZBQ/+Rkv3+yFau4fPVO+n06iTxgj+Q/7zSAla3qqVUdIBRrPFqKTcTPHY/E0WN0IjM/raZmT80wsRyiB8PNR09kZmXZb8cTulzPncAvPJlQpuZTM341zMShJ8kT4ebo8cyIqCXsOMDQTOY4nnXiRV8hd1GCelEFDYPuzNW3Z+KdZlAVRaCwigSEVWRUBBIrMFFYgs7JnNyyBRcGQyrMwEHgeiKgRn19rTq6cf14fXztYHW4NlzuL/b15DmEOcTO4EEG+FzzqH0twgZIH6xyP6yVFbhvzQlfoHUwTLx8Qu6aFHu3DRby+XKIjY3tHZuY4Eluz2C55OSvPJsbyOcHcqNRnubwU60j+diUPThbDmmaHi+m7KnS2FhpWwJ/MNHgZ43ES7btN4cKcScbzeUGc7krYzwdzJG51rHtc79FdwLPb/GGWr2cA62RQQWwE3FkVGCc5HMGvLCCHvBAWQMIVyXT70Qcg63oIeexNa4akM+281Y7L63M7zD0yz93wkLU5zXxf5LbO+6cOinWJ5zIk5NibfkKZNp9+Qtmkq8gAxBzDbLrkYIP+oHHndDBEcKXsCrlXNbyM8HsSfNgPuhzDTs6C1pimQamfP2tgGuBYcwD9kCW2kqfbWk+GCIDz/FVkOZlkbNYnUVF8bXXRDz6MtHEhCzil6kqJSUZH4QamhgVhNdeE4QonELtTZDFzDkX4ZLOfvCaqJFtV/oksNRUlcnLcAOFaM1TzcutRq/9AH4LfgLyeou7fAp4XhzlUBltqU/YUUNnRAKcY2QFv4Z5A4RoIYPIIwtkGg6Ezgowj3QSGPLqYl9hVW9PvrsjFJCBV6c5CFQ4t+ERQR4+4DCfzYD0erEZ5GKOgdmk+4NJIC7JSn+lnxx89vIPU4X5sYLMySpjMhN3/8tusIdE0zSiFCbw6EMP7YXPXfXBxRc1NTE2ZJaLarioG0BdMtXjx6tzciJqM1UtVM2hsZ/xug9x/MBXm2D7dgDeFbnvuQqcVRd2RLId6AT4xi34xkjA4H4C8HH3k8fCCWtks4FsypFM3DZajs3SiE1bcQ8+uXwpjgc3act6OUJ5yQr4wDYNLjaKLDezZ3jkRDfziQ2BiEOfv+U9jxydYPWTD++afnjdhL6KfA0M0ip969ZCY/H4YqMwWBGxsJVp4uQUHj3+6c9/+vgo2Cd/oCWbkFwkX0Qm9Gics861/ZmopjLJCXDxJVaGefCZu5Z8gZLuj2Qike5u3gcwyHwuyg4zEi1niSqYTFiV8noCstoGHBDiGFzoKmBczfKavNDiLiOP8oIU+z87QpLpSmd89YTqOVZkxzqeNx9eky7g3Xft3LbuTIgkr/xIM/A/jBY6k6u7kxue8EdOlA57F+8Li1PZ3QN93SRwx1G3XLPWne54IqKMRa3Z/sbBg433VewxJfJEmpEQ3rF4R4D0dA9Pr55SzPvi5PSaSEXm83n56kv0fvJLFEZddRumTeD9fRvPNpJdvlTCmTPTiaJoGNIkj6JUeMIV1aIndcWcIZr+0ktfjWozpqLjB/VeHU/pZvNFTVNxtfmiYeCqqeMzuu5gwh/T09QGTMihIXS0fiiLXQyP6wphLnbEI/EA49GAm7hkLHpd4rzhI14Nq9SrzvsFAu3pND9SNAs1KZrs7g4GEeoe6h5aM1ABPeor9OSDuWAuneqKd9qRsGXCTxlJXyDgC4L1TYO6GA5JAJUBa1sDZ96Hg0mfYy98XI/Kw9iUgkK7LOtzKkJZ0EfqtURvYmAgEQwlML6vFi/GBwbihRS58olavC+OIVOM//zon1qJhPWnR/H/Cdk4bjVd+IajTYWX4f84+kTIJgnziaNHmv/FKfnIERxyipoX22vo0MPT5HNgNV0oXLdcIsPMEcnWYjlMi5/whfF0pSuI074uH/5ucz29ozmKn7t8Dz78nUOHms98p7kFf9HBpq/SBPlX5INZBo7i41yUR/ABmBx9XRklcmK3wLGyTAgB+IBI9znxWb0a53KbXraYlNmhQip+YXL6rgvPYvTXhWbo/qfvhM+2RLQQj1/IX8Doixfumpt8cu7+O8/fdyd/bnR1ETByGhjfVH2rG7sUSXFJhzxYkhVJXhI5kMBjOVwZPksqlkHrkIznkaKoE6B7Hrp5Q33d0No15f5CLzykLx3sChiaQ5TbnC/JgwpOhtsHYFEVZ6JXMMT+CnTHyXDzAhId7L/GD92n3LbxKpgx92nFVk4D2Z141bChdMUFd9S94gKe19y/Tb2Vd+Y87M2rX6Q/pXzRKonet+Vp99RcPQe9Z+gIcB7MYIoFfIS7D6JwKxIpFacRsKQdiIp0a6Se/ZVVhSPvrLmjrvoSAR94+z6fiw+TkOSaCyDbJp98HXI97pf4EnkXdyey/fgNzB698Dj5vt8vRlnKvPyymWJRJf/pPU+el1kO9+Zkdv7JPVR+4yrMafO80us/a4TDxtli6ODd8uHD8t3OXDu4+iiK8RUxzt9AdEUkwnMCyBxxlvd4/A6TaTgQPMfgFDcwCocCPk11K7IkMhTDMZnTuZIzT5X+rARmDiav2l+yMs6aiGlVyZt7H3hg74XFpz9/4M3Jg4sNTSSfOrzzgVAU/+z+F+6/8OMfXzhw5gBRwwcfPjDNHA586epRGqazqAOtQnrd48cgkcBBevKY/5jJya+XcDTno1NzrC33ZqsFgHHNMVHwJeASU9FY2D9SPzF2QIs3VIPosoFJSPOfOrZ4/qDtn9lryzIViClt1LUQuFhk7/Fv+FXlq+rjUaYxXdtjh/HdpbHFzxjG3c/1Lg4bvarp0iVDWK3KOtEQeIno6hn6MM0DD4ygLPjvI2gbuhm9D324fmcqDrSZ7C5m7JDCJIq3FsKACSLaPlSlTDgwvml4ANgdE8a5kwl+5yEZ88i8uAT8GgO/RgJ3sZYQ2DomLSFJOoz4hHAcwHMuZz5iUYxu2/ee2caWtYP9q5Nd0Wwsi7zYq/A4QULKgD0zLTFoWKKUkZwUuDaUYOcazBKUBDI8kMJr9pf4NYs7rqLEI61ZsXULfgG3wjDc66iuw1VqWu09FdnqD02j2Fud3D0ksuGqftgYMkZKmYKMp6Lm4MDMtpOHG7PhxUfuZlpmKGxrofmcfiKjD5YKJ0Xy0IVjO0fVUfD/zVN44SFWHwnVi3uVvWFOhmYPDlYP4zeVcmOyN9Or66JeHGB7EuFjJxbvPrwwXArhopaP2sNaKtwsh+YMJRTtLRjywXu1h7QC087MFktKqrHwdGrywbuJtgd/5/6XzF6/OMDOnDBMJX/lp4rsT0zV8+ojjjqArX2QnqW9MKsB8Bb31G9RgSslEyGLCRK5HsLnzg9bgslyAQoiwmm+KFFxQQFtx2xG4as4c+Av40bQUIB+ZVJxO9JhdAe7dc0VUAICA8yUHT+oCn4pA0S3EmJQ5HHsSpkCz+IrTQCAPFhgwSyBpONvXMC62PyP5r3Nn4lY/WW+QBLxTOFZ+2DZXwxpSj5ctE8M6GVTVVNhmifNN1s1FXw79j9UDEfjvfHBK58vl0OpzCMLg4VwIvHwAQRSyzH/HGA+uNqIR0EDyAJJ7gTU6wdpXutERD+AHq7/0YY0ifk3r0rRQIyMd3WSWMAVm4ngQIfloS454LolZKpUDvok8HxkYd7QwQR53RREm+H5MPb77bk4tm11QlM4PYjOoWjUE9187Oihxdv237pw867Zm7Y2No2uHwb7MbhmgEf6V/d1Z5MJzg949KsjxCUu4Pe1//QEAKYF1oJ/syuO+B1Hem2tni/Tl6p4Rf1a+xqwPedarb0I1QqzVpbd/P72mcWt1/i5c18/f/7ryyn+5DPPvHL+PH7q3LlXnnnmBVVMSQpupZ90il45d86vyEkeSoXk1d7o5Z/F8vnYRDmdSpdfqaaS6TIej+VvOHfuXOr8+fOpc1deOHeJJ6nzuHjOudk5Hv1oZuDauXNLK4p6r1T4rcg3Y/lqulzmyxs8zTu+gCPUj5KHUZBHFL2Yu3QObUTLQUXDMlp7KFsLv8tWt2V66aPeZhw4YvNfvd4GHB/FhyFteIlp6s24F7wbntX1x/ARHY5v0x8T5Gauvn2gSqgQAC0B7REZ1xvQHsA+utQOI4G1QUxEbEF2bM2M7NgazjMaXHOymUQ8FvFpLlMxW1rjWqk1AdCXaoYvE0mGaQU4YiUTgFGcMLS3kEncTHid3WitUujhO3TpOTtDMjaJQhp9Irw4Xc+NssO6vk3VdVk1FSXoFjV2kIlmtPBOxbpiZ6J2JgVfgicSvdsa4fwBxsLRkGGE/KYqeyXZ55KNQZmpuqH7Ew7Xe4suka+Braijkfp6g/cbBoeAp+QMDuGDs+yFYme/3gLPrAxCmsmOdD6ddYJshsmZgrOO3Q5KBS2nyJF4n7MnxSatGeb0qxWmrHGB5uv4eB8IBJbl8xvn5zeelxW+MsezzlaiZ0UiEiK6m992R00eRNQumVE37nMXiF/QwB2Y34jv3zivyG6X6AHPvZpqnoSGjMi4V3M3X1YM7zlTe8Vr4HM8dgkFbbkEbnU/9YOUROohtyOXGsabrollKOpQZzDpGikQUQokKbieNq3SRU2XF+6fGR4cPTG1c++Xd++8fWJgdOcT5ICoqR97yA4X47mR5iPk5cRILjE6WWr93tVfXP09+hO6Ca1B3rrai9Em/iMBzL13EIzMciSNL1G0A22tGLpltpf6uWMvVaqtMr5FRqTv2XTLzr/ZOT96czoZDm/XFVdkpCBrkmtTuMO0Nk6+b883NlbW4Hj3TVPf2vc7v/s7uxf6koQM1HTXpmSXmM6O3vx7H7rjrlsDiuCTCsMRj2tyYX7X/MZJw79p67qHp6Yb762vSyRwdyAwtvno9I73PL7J2cvE/bJFUgS8joBftofv3q2UCKFVGPS5rQToJd+4C6rGg3Rcs5ZA5JhAeCyKcsXCwNMR8qDNN02HI6lsLCxxvxFcRsPGfJHUEY9hXG7tAQFhEvhQgP2v8tW/QHmYwChVaoAWltFJzYyZNDQCZCwjVgBDh0mlXArCrcDF7iPOVk9SXNizZ+Gp7z/FD5//3uebtp/Ju2+/d06zFxZPPihH1Y7YbrL3jb1s56qAz6faKlFEWfV6SWKnfHy0PPJBcXeP3yOoEiOHew/MD+3eXziR2LYtcaKwf/fQ/IHeE/Ft2+KiJovlkmJUm5N+O6WForvtUrVaiu/sCQSDpKj4/Zri1XWSwDvjIyxTTNh2ophhI/bu7oAmG6rZ3mPyFn2EulEBbUT/s64MYUHKABEj41ueVoD/DwEAUSzTQ4hQiZJ9SBKoxGmaSAVO2hgVga0tu2xAZZ24MtdZhhqRlg9RQxzpZHIISaDyDpsA3XdaE6c1MEaJq/w7m////foOcDQMK5itlDKVfsfRCF63c7WuksUdDs7lMsmuNmx0lapO/ISzRDOAr7toy/bBoP75jc0iBw78ctJ2USkiSoR6VGezRTWFX06XhRQwI+b+cHONJ6X9QtPWaSntD/H7IDPswVu+0Go7Mo81FhCjAqbtnRrV1B/IQlIkwH+bg5r2C6e+hzf0wB2uzVWgvW7bU88BROL27k6wIDzWyuP5mDQIsiNmQJGQl3gFbjxW9A86n7F8bxsIk7x/xeOXb7/ew5EF8rVrj8cv7rh2ZR4vvG2fYQqt5ruw7KBKGeGOFXeTiYAWYHJ5vI17VO2AW9AyokaHEwou9+FalcebEl6c5YlodGKJJ2ZpPbZ48vZ9hR9W9u1TlH5whPftc9tKye2Go7uk2HCEwn7l+yu2F/5Q41dj7uVacPr2/B+s3GTIMfrn5HvkPuiLXY8kOnSJ8TcdKL72UgM8t+HEINrOvPSOiG8nrl0PEJPvtSjEoy3KAEf9et7rffRR71GTnzz2mPfdFb0FXsF5precPU5J1F3PgJFkDpM5CnIDyDgDBzCQTrSpkexPJ/uTy3HMLA8xcBNZq7T3jNTeRuioP25ejJtLZhxfBE2AjG0twclFKPgSL33dhNLg69dKefUV8x1ECT7fUsuWr3hlgmC+R5Y6Lhud429PwIRbhuGsL71zqk2+DdZaOcHfgmmJqj+6E5KSorwIcwq294GVe0a/rPNphxow7Z1QAyooW1ZMJB+zr9An4RmzKF1P8F2AfBKvBRC5l3IEYGK/mVqTTvDH8jnOu+TwBh5dMmsBh2JlWjvkpWWqeObAjSMa+MqqStcwJn1VEj1bVYVNr9l428fx+uafdUyGm0/Wif+2M32XVB3r6lMqcWG3S9OVL8XP3DY/0TzU2Yn/cGJlrCUKM/u7f247w9iCy26+1HXEJQMNEnmIFwaUOWH5W5ETYuScaAc3i1sjgAG/sq6zQ/ltVXfUfbFYLBlL+gJO8KWqCNGe9H8WcskK6X5fv4/+7X8ecik2LzcvP37hG++OuLzyxhsYXW3tVef+lgd62gs85Ob6zs4QoSzg5+vOHhGkdyOWcAm7JAEkSQRMp5wGSghLh5ACKK+IC3wtog12EnLJkmsBybJH3rxuqNJvBIJGMmMkk+42yDuyn10p+9fcGkf2LTD1oo2HMXaicGADQPqYTSweAydrFekO7sqo+A7H6bjD2WjHMxMff+khdup7D+BEfsh4fu+Hps4cqJOhg6cefeDoAN30fBB/vNWKr9q2Wt3BHaA7lI7bzpCHXnxYfKB5NpYPPr9pePHBz5w6PMhG9j+09UN7nw+uGCOvsyq9tj7gAtOHcB8kNhYo5ZEqgsXWwhpIM/fABUYFMHvMwzZbSRiFTNLZtLtiPY2v4P7G3jYvOP3EI79tD52u/eZOtd/5ecvZa9TytSOoB/zrW+q7e7HgLmOFRTF1GRho2zhcFZhbmAcsUVxMWUAuwA4X5avtskTkhfY29Xm4qYim+b3nePCuMVgrFfO5TCbdcpBVkIF0V6UrC980d4jfsZoYWDE6tXZeWjFaZC3e3zz7Chm68gLe9+qr2Da1ywutBcZHnMOvzdUnXhl/dfzKxVNOySmvs+zY3v3eWpBs720Hp6LF0d8CLA2BX2SgUD2oUWf98ZpDMJB1HAJTwr72K0Bgr3GGPyT1unzNfzMlyaucVpr/5g+EyIsW+ZylN/+tucdSqPu0WwPNDuirUHu/w/K7IlW+muTiyAy/1HqxreUbv22tFhhTarCfL3zia296cCfAJvwdEL7pBoyyyN1MjWT5Nkzgva1lQOdtkIvt1zo6Ol9sxDOjERIdyXXe8qV4qNr9nXJFTcTdRLVT7sofLviTQ7jQQ6upMv5Gc1OLZfxlNPjRWigcxeGoNXq7+XzvVOxMMuvyg9gZcnbfiGZNp3oGyy173drHth1tq29Bqls94sFuxX3EC0hChfaGBxnoCDqsYYUQZQYOCpkF/1IhkzPTN2xtTG4aWbd2cKDSX+nypfm3n+/vTa/YqGbBJQc02mWOILXLuPewooxL2HXEaTUBLIIxkRyTC/b2PDekleaPKo7NPe+kPMuL27k8eCVvu4xjsV4MJWef4bUgwUtLtvVrMlD1bfm8vWS3fSjAFzdok4BqfDU4iUWpH7xkkRK+URVGi0f+hHG+dVU6IoMPgHlodl7hwax2zI8vdoCXU+BwayRTAd3taJvPhbtaa6fXxq2rvRDX2h90LabUxTf+ADvy8c3NNdxofhK+z5J7HLxckpWmLS9jJs/i1z4q6IrwMSi6Hyp+C74fXbpeA/+zU6uV/Zjg1sSPNtOO/8u4zLM+5/2A1m6F7fXpmAFuAg8JaR7VBVIRDRKgo3xBFXgeA+dRAmdDpA7CEo6wTBAch4K/GMGEhk8v9GSSHZbe6esMBPyyE0XS+IoOdCbYValZON1m2oAs1WybaS+/KVVz9igP7xyGDxm6/LPzO8H7tC/f44AvdazGjaA597ReiLojXSa+VcNkZLZebzYvLT2zA8fOKfKVnQ7+ksdlxX9lZ+vlp8f5Yfkd1wBVob85znz42xx8udShOwQIGuexThigI2l0lBwC3nYEysOCVeLLB6S/ZAuU+8R86zX1T29shjZOM9VvZwa7hExvY3KymJASA7mooYjP3vn0Xezkl0+MbNy2baM9MFIdSIRJyA7Bx8yUB4eH8Ctzd0IdcvuyH/810Nc68oMEvh/9ST20hLGbv5nUTcAfXI2ZMJsFYiC0Xy4u8I3jyAkRMcQXBYDYHFUwyCpfMlhwYQYmkjCYMLdbdW+O1KvIjd1HfmMrStkMHBidg/aMNnbUrcOHFg/smpueGqmvG+LbdvqD1ZAKRB90JFug2WuCXLq+ppcQwdUXstUafzOuvVjnu35VI87+zTZELjcfJjzkMixca4GPJ3oM2Q4XzJYU/z75O2fvV9G2w1lPIZQLpzxdPapuh3tDp519YM7l05HeeFj1d+ihlL/bqo5kWq0j+ZSh+8JRNZUqVOu5VgMyVt7XrWfCKnG05crX23vGuBFQFNPMGOW9HXbeMEhrAxn+abuCnkh1JNalS6PBvnDIxE5rfzzVkVw3HK4Xe+M6bu84a/um5B7QNwmkL1/PCtyaOSunYGwOI2eRAF1bI0gG0tWA7ryH01VxXq0V2ooCStNC0jZmPoNj/F1U/HLcvPK6A4c+oCjED6ePHVw7Q6bWnWtecDwVPAJwd3D/xz++/6DtyNpX6MN0M/DrNWgneq0eHsAuYfsm0PWAm2CJjs/0ENcof3N4rBXpKCKBupiwD7AQHn0f0A8kuTgIIL4U9V7ookjE3YCB8hxQT5Vujmx5OgPtSrwdFVyHeEOA0UO/Rcv6wLsayYgKMl14d2NRdFwpznaIyIMa2ntmLSucs0LhkMH3Y6Ydp0qy+Aog33gI5hgve9DVZY+r7WxlC4RbqRKvW8XXlqRr2ZWhDhOIYSsa3us9rhd0k6qYxE1Z05gZ0D/gXaVPe5snvR8Ad3Va1497Cz6LKsy2XJpGLT9+ozhd+EjhA4XVq4sf6Tve1zfdd1/fsb527rMW3NJvUl30wgMzhZm+gve413ujjv/Y1D+ge6e9vXBLuKOmuWBCmUItX/PUxsKNfcUP9H2kuHo13OS+wnSh73jh/lZuGVtOOHEsGdhUAR2q+zJYYLls0A8cmTMbCqCSh+nqBFRny/vYl9/ibIdOiIf/JwapX13j7SGWHXVXNLnKCDgvk9Wcl8lwqZpeHk/IpVv/F0DG2cdhcaMX5O9Zg2C/Ytgm8Sd0vNi8Jzhorg0G8e3mDH5Yjdx9A38JLj7W4XL9yUGS39LlVWIa1I0bxqXmPYaxzlhr4ttrM/9kprfM4zMvP0gMXfRL83cMkY5VhsJ9rWX/3EBdMA71+joNntmFW3v0QOPBjeJhUkraho79CkMXNINm8rozcX3L/jKvAY9CozZ47CtjM/gNRT7fssz89aSL0x97/IEpMnP/k/fNfmiF/04Sl/hufajxAlR+6aaPgRKf+swpqPmxm972zmfrvaIdVAed8IPvCKji5q/vcpJMedCPR6D4K5/o2hufVkcmxeMzuJzFJlBVIjg7ubBkpA2NFCh3e8gPis1djZ3DR6dKV76DH9uy+6YHpjD5weixR576zNExMnL8009/6kQdL+ycbO4ulaaOvQ8/Vpo6Nb1r19wjx+DyiU89e/bksDh58HH0/wC9uxLxAAAAeJxjYGRgYADiSpnfWfH8Nl8ZuJlfAEUYLq/RXwaj/3/4n8WykTkMyOVgYAKJAgBzGw2qAHicY2BkYGAO+p/FwMCy8f+H/69YNjIARVCAKwCqhwdIeJxNTzEOwjAQi5KMLBl4R/lOfoNQH9GRqRIjX+gX+MSxMQIqGNc9qgyWTxfb8SULIZ0dNYTckQ1Y5lTxSTfC/nuiLFogjtTc112+yvOWh97c8W3CHI9tpvK+4h7z9ufe3wxP8Y45A3lwjTVzL90jHtxbvGNp5otnsaOgznjpJr9V/cqaHU/t/fRX4AfmQ1seAAAAAABSALoBfAMiA64EMgSaBSoFogYUBqQHaAfkCHgJQAnWCoILKgvADDAMiAzGDVwNwg40DqgPEA+QD8wQXBCCEMYRTBG8EgoSYBNGE8AUrhTmFXgV+hYyFpgXRhgcGGYYzBkWGV4ZsBoCGngbCht2HAQcOBykHTodsh4sHnwfah+yIKohLiGUIeUAAQAAAEUA9AAPAAAAAAACAEQAVABzAAAArQtwAAAAAHicdZDLTsJAFIb/kYsKiRpN3DorAzGWSyILEhISDGx0QwxbU0ppS0qHTAcSXsN38GF8CZ/Fn3YwBmKb6XznmzNnTgfANb4hkD9PHDkLnDHK+QSn6Fku0D9bLpJfLJdQxZvlMv275QoeEFiu4gYfrCCK54wW+LQscCUuLZ/gQtxZLtA/Wi6Se5ZLuBWvlsv0nuUKJiK1XMW9+Bqo1VZHQWhkbVCX7WarI6dbqaiixI2luzah0qnsy7lKjB/HyvHUcs9jP1jHrt6H+3ni6zRSiWw5zb0a+YmvXePPdtXTTdA2Zi7nWi3l0GbIlVYL3zNOaMyq22j8PQ8DKKywhUbEqwphIFGjrXNuo4kWOqQpMyQz86wICVzENC7W3BFmKynjPsecUULrMyMmO/D4XR75MSng/phV9NHqYTwh7c6IMi/Zl8PuDrNGpCTLdDM7++09xYantWkNd+261FlXEsODGpL3sVtb0Hj0TnYrhraLBt9//u8H42mETwB4nG1RZ5PTQAzNS9YlhaP33quBoxydox3/Y7OWy3izMlswya/HvszxCc3s00g7epKeRuPR1maj/9sBxphAIEKMBCmmmGGOBY5hB8dxAidxCqdxBmdxDudxARdxCZdxBVdxDddxAzdxC7dxB3dxD/fxAA/xCI/xBBme4hmeYxcv8BKv8Bp7eIO3eIf3+ICP+ITP2McXfMU3fMcPHODnSDgv7WyAjFatX08Ul0n/Mqm9WMlaxy0ZVetFq4PLVG2VpnwinZ7QmoSXpROqkj7yVroqXkrXkI/bsNloSrU0ZZAlxa4ujdRRcGRd5Jm1m0pruesJu5T+9ASmpHnBOiebcd8wWtrgqnmhucuWVhpViRWZIBTnlA40mczzseqi0nJoRcUrmtKvIHW9IRsPY2e7sSNph0L+TXFoNct8sXWZ0hzySUPrxJDv2DY7tSn4aLtsN1VSk8mlHXOzqEi3R1+x5pKDF4WWZeTa2uxNKw621NK5SFWkmvkhbrXcUf3kpP8Vb0MxKDnj5ijdN5DW92tyQybNuTPDjMmGeZXVJj30fc/I99q6uKi1J5sertifKHG1p5VshbLcJm2tfLAkBoUSV4Wi0CQ0q0bo2jRxZ/tTVpEaMtFywNHoL79rwAYAAHicY/DewXAiKGIjI2Nf5AbGnRwMHAzJBRsZWJ02MTAyaIEYm7mYGDkgLD4GMIvNaRfTAaA0J5DN7rSLwQHCZmZw2ajC2BEYscGhI2Ijc4rLRjUQbxdHAwMji0NHckgESEkkEGzmYWLk0drB+L91A0vvRiYGFwAMdiP0AAA=') format('woff'),
+ url('data:application/octet-stream;base64,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') format('truetype');
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
@@ -19,7 +19,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
- src: url('../font/fontello.svg?37675340#fontello') format('svg');
+ src: url('../font/fontello.svg?12405355#fontello') format('svg');
}
}
*/
@@ -106,6 +106,7 @@
.icon-plus:before { content: '\e823'; } /* '' */
.icon-ok-circled:before { content: '\e824'; } /* '' */
.icon-heart-broken:before { content: '\e825'; } /* '' */
+.icon-download:before { content: '\e826'; } /* '' */
.icon-zoom-in:before { content: '\e827'; } /* '' */
.icon-zoom-out:before { content: '\e828'; } /* '' */
.icon-tasks:before { content: '\f0ae'; } /* '' */
diff --git a/admin/themes/default/fontello/css/fontello-ie7-codes.css b/admin/themes/default/fontello/css/fontello-ie7-codes.css
index 708cfdf4a..2136bde1c 100644
--- a/admin/themes/default/fontello/css/fontello-ie7-codes.css
+++ b/admin/themes/default/fontello/css/fontello-ie7-codes.css
@@ -51,6 +51,7 @@
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe823;&nbsp;'); }
.icon-ok-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe824;&nbsp;'); }
.icon-heart-broken { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe825;&nbsp;'); }
+.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe826;&nbsp;'); }
.icon-zoom-in { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe827;&nbsp;'); }
.icon-zoom-out { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe828;&nbsp;'); }
.icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ae;&nbsp;'); }
diff --git a/admin/themes/default/fontello/css/fontello-ie7.css b/admin/themes/default/fontello/css/fontello-ie7.css
index c1e1ec524..6af3d9243 100644
--- a/admin/themes/default/fontello/css/fontello-ie7.css
+++ b/admin/themes/default/fontello/css/fontello-ie7.css
@@ -62,6 +62,7 @@
.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe823;&nbsp;'); }
.icon-ok-circled { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe824;&nbsp;'); }
.icon-heart-broken { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe825;&nbsp;'); }
+.icon-download { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe826;&nbsp;'); }
.icon-zoom-in { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe827;&nbsp;'); }
.icon-zoom-out { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe828;&nbsp;'); }
.icon-tasks { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ae;&nbsp;'); }
diff --git a/admin/themes/default/fontello/css/fontello.css b/admin/themes/default/fontello/css/fontello.css
index 40777f186..93fca2926 100644
--- a/admin/themes/default/fontello/css/fontello.css
+++ b/admin/themes/default/fontello/css/fontello.css
@@ -2,11 +2,12 @@
@font-face {
font-family: 'fontello';
- src: url('../font/fontello.eot?38260160');
- src: url('../font/fontello.eot?38260160#iefix') format('embedded-opentype'),
- url('../font/fontello.woff?38260160') format('woff'),
- url('../font/fontello.ttf?38260160') format('truetype'),
- url('../font/fontello.svg?38260160#fontello') format('svg');
+ src: url('../font/fontello.eot?97633062');
+ src: url('../font/fontello.eot?97633062#iefix') format('embedded-opentype'),
+ url('../font/fontello.woff2?97633062') format('woff2'),
+ url('../font/fontello.woff?97633062') format('woff'),
+ url('../font/fontello.ttf?97633062') format('truetype'),
+ url('../font/fontello.svg?97633062#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -16,7 +17,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
- src: url('../font/fontello.svg?38260160#fontello') format('svg');
+ src: url('../font/fontello.svg?97633062#fontello') format('svg');
}
}
*/
@@ -108,6 +109,7 @@
.icon-plus:before { content: '\e823'; } /* '' */
.icon-ok-circled:before { content: '\e824'; } /* '' */
.icon-heart-broken:before { content: '\e825'; } /* '' */
+.icon-download:before { content: '\e826'; } /* '' */
.icon-zoom-in:before { content: '\e827'; } /* '' */
.icon-zoom-out:before { content: '\e828'; } /* '' */
.icon-tasks:before { content: '\f0ae'; } /* '' */
diff --git a/admin/themes/default/fontello/demo.html b/admin/themes/default/fontello/demo.html
index c7bd65f9e..c68a979dc 100644
--- a/admin/themes/default/fontello/demo.html
+++ b/admin/themes/default/fontello/demo.html
@@ -229,11 +229,11 @@ body {
}
@font-face {
font-family: 'fontello';
- src: url('./font/fontello.eot?58887028');
- src: url('./font/fontello.eot?58887028#iefix') format('embedded-opentype'),
- url('./font/fontello.woff?58887028') format('woff'),
- url('./font/fontello.ttf?58887028') format('truetype'),
- url('./font/fontello.svg?58887028#fontello') format('svg');
+ src: url('./font/fontello.eot?37815632');
+ src: url('./font/fontello.eot?37815632#iefix') format('embedded-opentype'),
+ url('./font/fontello.woff?37815632') format('woff'),
+ url('./font/fontello.ttf?37815632') format('truetype'),
+ url('./font/fontello.svg?37815632#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -279,7 +279,7 @@ body {
<script>
function toggleCodes(on) {
var obj = document.getElementById('icons');
-
+
if (on) {
obj.className += ' codesOn';
} else {
@@ -379,24 +379,25 @@ body {
<div title="Code: 0xe825" class="the-icons span3"><i class="demo-icon icon-heart-broken">&#xe825;</i> <span class="i-name">icon-heart-broken</span><span class="i-code">0xe825</span></div>
</div>
<div class="row">
+ <div title="Code: 0xe826" class="the-icons span3"><i class="demo-icon icon-download">&#xe826;</i> <span class="i-name">icon-download</span><span class="i-code">0xe826</span></div>
<div title="Code: 0xe827" class="the-icons span3"><i class="demo-icon icon-zoom-in">&#xe827;</i> <span class="i-name">icon-zoom-in</span><span class="i-code">0xe827</span></div>
<div title="Code: 0xe828" class="the-icons span3"><i class="demo-icon icon-zoom-out">&#xe828;</i> <span class="i-name">icon-zoom-out</span><span class="i-code">0xe828</span></div>
<div title="Code: 0xf0ae" class="the-icons span3"><i class="demo-icon icon-tasks">&#xf0ae;</i> <span class="i-name">icon-tasks</span><span class="i-code">0xf0ae</span></div>
- <div title="Code: 0xf0b0" class="the-icons span3"><i class="demo-icon icon-filter">&#xf0b0;</i> <span class="i-name">icon-filter</span><span class="i-code">0xf0b0</span></div>
</div>
<div class="row">
+ <div title="Code: 0xf0b0" class="the-icons span3"><i class="demo-icon icon-filter">&#xf0b0;</i> <span class="i-name">icon-filter</span><span class="i-code">0xf0b0</span></div>
<div title="Code: 0xf0e0" class="the-icons span3"><i class="demo-icon icon-mail-alt">&#xf0e0;</i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
<div title="Code: 0xf0e8" class="the-icons span3"><i class="demo-icon icon-sitemap">&#xf0e8;</i> <span class="i-name">icon-sitemap</span><span class="i-code">0xf0e8</span></div>
<div title="Code: 0xf125" class="the-icons span3"><i class="demo-icon icon-crop">&#xf125;</i> <span class="i-name">icon-crop</span><span class="i-code">0xf125</span></div>
- <div title="Code: 0x1f304" class="the-icons span3"><i class="demo-icon icon-picture">&#x1f304;</i> <span class="i-name">icon-picture</span><span class="i-code">0x1f304</span></div>
</div>
<div class="row">
+ <div title="Code: 0x1f304" class="the-icons span3"><i class="demo-icon icon-picture">&#x1f304;</i> <span class="i-name">icon-picture</span><span class="i-code">0x1f304</span></div>
<div title="Code: 0x1f464" class="the-icons span3"><i class="demo-icon icon-user">&#x1f464;</i> <span class="i-name">icon-user</span><span class="i-code">0x1f464</span></div>
<div title="Code: 0x1f500" class="the-icons span3"><i class="demo-icon icon-shuffle">&#x1f500;</i> <span class="i-name">icon-shuffle</span><span class="i-code">0x1f500</span></div>
<div title="Code: 0x1f512" class="the-icons span3"><i class="demo-icon icon-lock">&#x1f512;</i> <span class="i-name">icon-lock</span><span class="i-code">0x1f512</span></div>
- <div title="Code: 0x1f517" class="the-icons span3"><i class="demo-icon icon-link">&#x1f517;</i> <span class="i-name">icon-link</span><span class="i-code">0x1f517</span></div>
</div>
<div class="row">
+ <div title="Code: 0x1f517" class="the-icons span3"><i class="demo-icon icon-link">&#x1f517;</i> <span class="i-name">icon-link</span><span class="i-code">0x1f517</span></div>
<div title="Code: 0x1f527" class="the-icons span3"><i class="demo-icon icon-wrench">&#x1f527;</i> <span class="i-name">icon-wrench</span><span class="i-code">0x1f527</span></div>
<div title="Code: 0x1f554" class="the-icons span3"><i class="demo-icon icon-clock">&#x1f554;</i> <span class="i-name">icon-clock</span><span class="i-code">0x1f554</span></div>
<div title="Code: 0x1f6ab" class="the-icons span3"><i class="demo-icon icon-block">&#x1f6ab;</i> <span class="i-name">icon-block</span><span class="i-code">0x1f6ab</span></div>
diff --git a/admin/themes/default/fontello/font/fontello.eot b/admin/themes/default/fontello/font/fontello.eot
index b0615dac1..032d37771 100644
--- a/admin/themes/default/fontello/font/fontello.eot
+++ b/admin/themes/default/fontello/font/fontello.eot
Binary files differ
diff --git a/admin/themes/default/fontello/font/fontello.svg b/admin/themes/default/fontello/font/fontello.svg
index 77b818669..8490f92e6 100644
--- a/admin/themes/default/fontello/font/fontello.svg
+++ b/admin/themes/default/fontello/font/fontello.svg
@@ -1,78 +1,146 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
-<metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata>
+<metadata>Copyright (C) 2016 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
-<glyph glyph-name="star" unicode="&#x2605;" d="m929 489q0-12-15-27l-203-197 48-279q1-4 1-12 0-11-6-19t-17-9q-10 0-22 7l-251 132-250-132q-13-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
-<glyph glyph-name="star-empty" unicode="&#x2606;" d="m634 290l171 165-235 35-106 213-105-213-236-35 171-165-41-235 211 111 211-111z m295 199q0-12-15-27l-203-197 48-279q1-4 1-12 0-28-23-28-10 0-22 7l-251 132-250-132q-13-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
-<glyph glyph-name="cog" unicode="&#x2699;" d="m571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 15 20 15h124q7 0 13-4t7-12l15-103q28-9 50-21l80 60q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-13 0-6-4-12-9-12-29-38t-30-39q14-28 23-55l102-15q7-1 12-7t4-13z" horiz-adv-x="857.1" />
-<glyph glyph-name="cog-alt" unicode="&#x26ef;" d="m500 350q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m429-286q0 29-22 51t-50 21-50-21-21-51q0-29 21-50t50-21 51 21 21 50z m0 572q0 29-22 50t-50 21-50-21-21-50q0-30 21-51t50-21 51 21 21 51z m-215-235v-103q0-6-4-11t-9-6l-86-14q-6-19-18-42 19-27 50-64 4-6 4-11 0-7-4-11-13-17-46-50t-44-33q-6 0-11 4l-64 50q-21-11-43-17-6-60-13-87-4-13-17-13h-104q-6 0-11 4t-5 10l-13 85q-19 6-42 18l-66-50q-4-4-11-4-6 0-12 4-80 75-80 90 0 5 4 10 5 8 23 30t26 34q-13 24-20 46l-85 13q-5 1-9 5t-4 11v103q0 6 4 11t9 6l86 14q7 19 18 42-19 27-50 64-4 6-4 11 0 7 4 11 12 17 46 50t44 33q6 0 12-4l64-50q19 10 43 18 6 60 13 86 3 13 16 13h104q6 0 11-4t6-10l13-85q19-6 41-17l66 49q5 4 11 4 7 0 12-4 81-75 81-90 0-5-4-10-7-9-24-30t-25-34q13-27 19-46l85-12q5-2 9-6t4-11z m357-298v-78q0-9-83-17-6-15-16-29 28-63 28-77 0-2-2-4-68-40-69-40-5 0-26 27t-29 37q-11-1-17-1t-17 1q-7-11-29-37t-25-27q-1 0-69 40-3 2-3 4 0 14 29 77-10 14-17 29-83 8-83 17v78q0 9 83 18 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 1 17 1t17-1q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-9 83-18z m0 572v-78q0-9-83-18-6-15-16-29 28-63 28-77 0-2-2-4-68-39-69-39-5 0-26 26t-29 38q-11-1-17-1t-17 1q-7-12-29-38t-25-26q-1 0-69 39-3 2-3 4 0 14 29 77-10 14-17 29-83 9-83 18v78q0 9 83 17 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 2 17 2t17-2q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-8 83-17z" horiz-adv-x="1071.4" />
-<glyph glyph-name="mail" unicode="&#x2709;" d="m929 11v428q-18-20-39-37-149-114-238-188-28-24-46-38t-48-27-57-13h-2q-26 0-57 13t-48 27-46 38q-88 74-238 188-21 17-39 37v-428q0-8 6-13t12-5h822q7 0 12 5t6 13z m0 586v14t-1 7-1 7-3 5-5 4-8 2h-822q-7 0-12-6t-6-12q0-94 82-159 108-85 224-177 4-2 20-16t25-21 25-18 28-15 24-5h2q11 0 24 5t28 15 25 18 25 21 20 16q116 92 224 177 30 24 56 65t26 73z m71 21v-607q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v607q0 37 26 63t63 26h822q37 0 63-26t26-63z" horiz-adv-x="1000" />
-<glyph glyph-name="pencil" unicode="&#x270e;" d="m203-7l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
-<glyph glyph-name="plus-circled" unicode="&#x2795;" d="m679 314v72q0 14-11 25t-25 10h-143v143q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-143h-143q-14 0-25-10t-10-25v-72q0-14 10-25t25-11h143v-142q0-15 11-25t25-11h71q15 0 25 11t11 25v142h143q14 0 25 11t11 25z m178 36q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
-<glyph glyph-name="asl" unicode="&#xe0ad;" d="m1 700q-4 37 15 53t45 5 35-38l53-184q-15-29-14-64 6-30 36-204 7-52 32-82t65-29q47 0 74 37 34-39 83-37t74 41q27-37 71-41t73 27q12 16 20 32t11 37 5 33 1 40l0 38q26-10 61 24 49 45 109 78t98 8q33-24 31-59t-35-55q-61-25-104-82t-67-112-58-117-78-93q-21-18-66-18l-350 0q-84 6-105 118z m181-234q-6 45 21 65t56 8 37-48l21-209q4-41-17-63t-46-15-37 45z m155 70q2 41 32 56t62-1 36-49l8-248q0-59-28-76t-57 4-30 62z m175 4q4 43 37 55t62-7 30-52l-6-254q-5-47-34-66t-54 1-29 67z" horiz-adv-x="979" />
-<glyph glyph-name="eye" unicode="&#xe70a;" d="m929 314q-85 132-213 197 34-58 34-125 0-104-73-177t-177-73-177 73-73 177q0 67 34 125-128-65-213-197 75-114 187-182t242-68 242 68 187 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-12 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38-78-129-210-206t-279-77-279 77-210 206q-11 19-11 38t11 39q78 128 210 205t279 78 279-78 210-205q11-20 11-39z" horiz-adv-x="1000" />
-<glyph glyph-name="tags" unicode="&#xe70d;" d="m250 600q0 30-21 51t-50 20-51-20-21-51 21-50 51-21 50 21 21 50z m595-321q0-30-20-51l-274-274q-22-21-51-21-30 0-50 21l-399 399q-21 21-36 57t-15 65v232q0 29 21 50t50 22h233q29 0 65-15t57-36l399-399q20-21 20-50z m215 0q0-30-21-51l-274-274q-22-21-51-21-20 0-33 8t-29 25l262 262q21 21 21 51 0 29-21 50l-399 399q-21 21-57 36t-65 15h125q29 0 65-15t57-36l399-399q21-21 21-50z" horiz-adv-x="1071.4" />
-<glyph glyph-name="chat" unicode="&#xe720;" d="m786 421q0-77-53-143t-143-104-197-38q-48 0-98 9-70-49-155-72-21-5-48-9h-2q-6 0-12 5t-6 12q-1 1-1 3t1 4 1 3l1 3t2 3 2 3 3 3 2 2q3 3 13 14t15 16 12 17 14 21 11 25q-69 40-108 98t-40 125q0 78 53 144t143 104 197 38 197-38 143-104 53-144z m214-142q0-67-40-126t-108-98q5-14 11-25t14-21 13-16 14-17 13-14q0 0 2-2t3-3 2-3 2-3l1-3t1-3 1-4-1-3q-2-8-7-13t-13-4q-27 4-48 9-85 23-155 72-50-9-98-9-151 0-263 74 32-3 49-3 90 0 172 25t148 72q69 52 107 119t37 141q0 43-13 85 72-39 114-99t42-128z" horiz-adv-x="1000" />
-<glyph glyph-name="trash" unicode="&#xe729;" d="m286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15 6-5h464q2 0 6 5t8 15 4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q22 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
-<glyph glyph-name="basket" unicode="&#xe73d;" d="m357-7q0-29-21-50t-50-22-50 22-22 50 22 50 50 21 50-21 21-50z m500 0q0-29-21-50t-50-22-51 22-21 50 21 50 51 21 50-21 21-50z m72 607v-286q0-13-10-23t-22-12l-583-68q7-34 7-40 0-8-13-35h513q15 0 26-11t10-25-10-25-26-11h-571q-14 0-25 11t-11 25q0 6 5 18t9 20 12 22 8 16l-98 460h-114q-15 0-25 10t-11 25 11 26 25 10h143q9 0 16-3t10-9 8-14 4-14 3-17 3-14h670q14 0 25-11t11-25z" horiz-adv-x="928.6" />
-<glyph glyph-name="puzzle" unicode="&#xe7b6;" d="m586 402q-37 0-64-34t-27-82 27-84 64-34q22 0 43 13 26 17 37 11t12-24l0-144q0-31-24-54t-55-23l-143 0q-16 0-23 9t2 31q21 25 21 51 0 38-34 64t-83 26-83-26-35-64q0-22 13-44 17-26 12-37t-25-10l-143 0q-33 0-55 23t-23 54l0 144q0 16 9 23t31-3q24-20 51-20 38 0 65 34t27 84-27 82-65 34q-21 0-42-13-26-17-38-12t-11 25l0 91q0 33 23 56t55 23l143 0q19 0 25 10t-11 37q-14 20-14 44 0 38 35 64t83 26 83-26 34-64q0-26-21-51-10-21-2-31t23-9l143 0q31 0 55-23t24-56l0-91q0-16-10-23t-30 3q-24 20-52 20z" horiz-adv-x="678" />
-<glyph glyph-name="language" unicode="&#xe800;" d="m988 306q30-82-10-176t-134-160q-10 0-12 2t-16 19-16 19q-2 6 2 10 86 60 117 152t-11 148q-16-38-39-76t-59-80-86-65-106-15q-52 6-84 41t-32 93q0 84 60 148 50 50 114 66l-2 100q-140-24-146-24-6-2-10 4 0 2-5 29t-5 31q-2 2 1 4t7 2l156 28q0 110-2 114 0 8 8 8 46 0 52 2 10 0 10-8l0-104q158 22 164 22 8 4 10-6 0-2 4-23t4-25q4-10-4-12l-176-30 0-102 12 0q86 0 148-36t86-100z m-370-160q28-6 62 6l-4 214q-34-12-60-40-44-44-44-108 0-66 46-72z m122 28q28 24 58 68t45 79 7 41q-36 18-96 18-2 0-6-1t-6-1z m-448 382q10-28 53-165t83-261 40-126q0-4-4-4l-86 0q-6 0-6 4l-50 166-176 0q-48-164-50-166 0-4-6-4l-86 0q-4 0-4 4 10 18 176 552 2 8 10 8l96 0q10 0 10-8z m-130-316l144 0-72 264z" horiz-adv-x="1001" />
-<glyph glyph-name="signal" unicode="&#xe801;" d="m143 46v-107q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v107q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 72v-179q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v179q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 143v-322q0-8-5-13t-12-5h-108q-7 0-12 5t-5 13v322q0 8 5 13t12 5h108q7 0 12-5t5-13z m215 214v-536q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v536q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 286v-822q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v822q0 8 5 13t13 5h107q8 0 13-5t5-13z" horiz-adv-x="1000" />
-<glyph glyph-name="users" unicode="&#xe802;" d="m0 49q20 7 60 22t69 26 50 22q37 19 57 56t21 76q0 27-31 64-19 21-33 60-45 18-45 76 0 43 14 61-5 39-9 82-5 55 26 108t84 84 110 32q43 0 85-19t73-50 48-72 14-83q-4-55-8-82 14-19 14-61 0-58-45-76-15-41-32-60-32-37-32-64 0-39 20-76t58-56q29-15 92-38t113-43 66-38q18-17 28-47t10-51l1-21h-878v168z m705 299q0 34 12 50-5 32-7 67-4 45 21 89t69 72 90 27 89-27 69-72 22-89q-2-35-8-67 12-16 12-50 0-48-36-62-12-33-27-50-9-10-13-16t-9-16-4-20q0-32 17-62t47-46q17-9 42-19t60-22 50-19v-135h-254q0 4 0 11t-3 26-7 36-15 38-23 33q-23 25-53 44t-79 37q27 34 27 78 0 10-5 20t-8 16-12 16q-14 15-27 49-37 16-37 63z" horiz-adv-x="1201.2" />
-<glyph glyph-name="tools" unicode="&#xe803;" d="m155 506q-8-8-11-22t-3-25-2-11q-2-2-17-15t-19-17q-16-14-28 4l-70 76q-11 12 2 24 2 2 18 14t20 16q6 6 27 6t37 14q14 14 18 38t10 30q2 0 9 7t26 22 41 31q134 90 186 96 122 0 148-2 12 0-8-8-120-52-152-76-80-56-36-114 34-46 38-48 8-8-2-14-2-2-38-35t-38-35q-14-8-18-4-42 48-71 60t-67-12z m286-26l410-476q18-22-2-38l-48-42q-22-14-38 4l-414 472q-8 8 0 20l72 62q12 8 20-2z m554 202q16-104-16-166-50-88-154-62-56 12-100-32l-82-78-68 78 68 70q24 24 31 53t6 65 5 58q12 56 140 112 12 6 18-3t2-15q-12-12-46-80-14-10-12-35t40-53q58-40 96 22 6 12 26 41t22 33q4 10 13 9t11-17z m-858-684l254 248 76-86-246-242q-20-20-38-4l-46 46q-22 18 0 38z" horiz-adv-x="1000" />
-<glyph glyph-name="arrows-cw" unicode="&#xe804;" d="m843 261q0-3 0-4-36-150-150-243t-267-93q-81 0-157 31t-136 88l-72-72q-11-11-25-11t-25 11-11 25v250q0 14 11 25t25 11h250q14 0 25-11t10-25-10-25l-77-77q40-37 90-57t105-20q74 0 139 37t104 99q6 10 29 66 5 13 17 13h107q8 0 13-6t5-12z m14 446v-250q0-14-10-25t-26-11h-250q-14 0-25 11t-10 25 10 25l77 77q-82 77-194 77-75 0-140-37t-104-99q-6-10-29-66-5-13-17-13h-111q-7 0-13 6t-5 12v4q36 150 151 243t268 93q81 0 158-31t137-88l72 72q11 11 25 11t26-11 10-25z" horiz-adv-x="857.1" />
-<glyph glyph-name="exchange" unicode="&#xe805;" d="m1000 189v-107q0-7-5-12t-13-6h-768v-107q0-7-5-12t-13-6q-6 0-13 6l-178 178q-5 5-5 13 0 8 5 13l179 178q5 5 12 5 8 0 13-5t5-13v-107h768q7 0 13-5t5-13z m0 304q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107h-768q-7 0-13 6t-5 12v107q0 8 5 13t13 5h768v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z" horiz-adv-x="1000" />
-<glyph glyph-name="folder-open" unicode="&#xe806;" d="m1049 319q0-18-18-37l-187-221q-24-28-67-48t-81-20h-607q-19 0-33 7t-15 24q0 17 17 37l188 221q24 28 67 48t80 20h607q19 0 34-7t15-24z m-192 192v-90h-464q-53 0-110-26t-92-67l-188-221-2-3q0 2-1 7t0 7v536q0 51 37 88t88 37h179q51 0 88-37t37-88v-18h303q51 0 88-37t37-88z" horiz-adv-x="1071.4" />
-<glyph glyph-name="brush" unicode="&#xe807;" d="m118 170q38 34 85 29t87-45q42-40 48-87t-30-83q-86-84-228-102-84-12-80 14 0 4 6 10 52 60 64 145t48 119z m840 646q26-26-148-248t-292-338q-38-38-124-104-8-6-16 8-18 34-48 64-32 32-66 48-16 6-8 16 64 84 104 122 118 116 344 287t254 145z" horiz-adv-x="962" />
-<glyph glyph-name="flow-branch" unicode="&#xe808;" d="m640 650q0-80-74-110-6-58-28-101t-61-69-68-38-75-26q-42-14-63-22t-47-24-38-40-16-60q70-30 70-110 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-204q40 30 138 60 58 18 84 29t51 41 29 76q-70 32-70 108 0 50 35 85t85 35 85-35 35-85z m-588 0q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m68-668q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z m400 600q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="640" />
-<glyph glyph-name="menu" unicode="&#xe809;" d="m857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
-<glyph glyph-name="code" unicode="&#xe80a;" d="m344 69l-28-28q-5-5-12-5t-13 5l-260 260q-6 6-6 13t6 13l260 260q5 6 13 6t12-6l28-28q6-5 6-13t-6-12l-219-220 219-219q6-6 6-13t-6-13z m330 596l-208-721q-2-7-9-11t-13-1l-34 9q-8 3-11 9t-2 14l208 720q3 8 9 11t13 2l35-10q7-2 11-9t1-13z m367-364l-260-260q-6-5-13-5t-13 5l-28 28q-5 6-5 13t5 13l219 219-219 220q-5 5-5 12t5 13l28 28q6 6 13 6t13-6l260-260q5-5 5-13t-5-13z" horiz-adv-x="1071.4" />
-<glyph glyph-name="user-add" unicode="&#xe80b;" d="m620 128q180-64 180-122l0-106-800 0 0 202q36 14 82 26 94 34 129 69t35 95q0 22-23 48t-31 74q-2 12-23 25t-25 61q0 16 5 26t9 12l4 4q-8 50-12 88-6 54 40 112t160 58 160-58 42-112l-14-88q18-8 18-42-2-28-9-43t-14-17-14-8-9-18q-10-46-33-73t-23-49q0-60 36-95t130-69z m230 272l150 0 0-100-150 0 0-150-100 0 0 150-150 0 0 100 150 0 0 150 100 0 0-150z" horiz-adv-x="1000" />
-<glyph glyph-name="cw" unicode="&#xe80c;" d="m857 707v-250q0-14-10-25t-26-11h-250q-23 0-32 23-10 22 7 38l77 77q-82 77-194 77-58 0-111-23t-91-61-62-91-22-111 22-111 62-91 91-61 111-23q66 0 125 29t100 82q4 6 13 7 8 0 14-5l76-77q5-4 6-11t-5-13q-60-74-147-114t-182-41q-87 0-167 34t-136 92-92 137-34 166 34 166 92 137 136 92 167 34q82 0 158-31t137-88l72 72q16 18 39 8 22-9 22-33z" horiz-adv-x="857.1" />
-<glyph glyph-name="group" unicode="&#xe80d;" d="m0 106l0 134q0 26 18 32l171 80q-66 39-68 131 0 56 35 103 37 41 90 43 31 0 63-19-49-125 23-237-12-11-25-19l-114-55q-48-23-52-84l0-143-114 0q-25 0-27 34z m193-59l0 168q0 27 22 37l152 70 57 28q-37 23-60 66t-22 94q0 76 46 130t110 54 109-54 45-130q0-106-78-158l61-30 146-70q24-10 24-37l0-168q-2-37-37-41l-541 0q-14 2-24 14t-10 27z m473 330q68 106 22 231 31 19 66 21 49 0 90-43 35-41 35-103 0-82-65-131l168-80q18-10 18-32l0-134q0-32-27-34l-118 0 0 143q0 57-50 84l-110 53q-15 8-29 25z" horiz-adv-x="1000" />
-<glyph glyph-name="home" unicode="&#xe80e;" d="m888 336q16-16 11-27t-27-11l-84 0 0-310q0-14-1-21t-8-13-23-6l-204 0 0 310-204 0 0-310-194 0q-28 0-35 10t-7 30l0 310-84 0q-22 0-27 11t11 27l400 402q16 16 38 16t38-16z" horiz-adv-x="900" />
-<glyph glyph-name="equalizer" unicode="&#xe80f;" d="m576 239l0-112-55 0 0-167q0-23-17-40t-39-17-39 17-17 40l0 167-56 0 0 112 56 0 0 503q0 24 17 39t38 16q24 0 41-16t16-39l0-503 55 0z m335 335l0-112-55 0 0-502q0-23-16-40t-41-17q-23 0-39 17t-16 40l0 502-56 0 0 112 56 0 0 168q0 24 16 39t39 16 41-16 16-39l0-168 55 0z m-670-112l0-111-55 0 0-391q0-23-16-40t-40-17q-23 0-39 17t-17 40l0 391-56 0 0 111 56 0 0 280q0 24 16 39t40 16 40-16 16-39l0-280 55 0z" horiz-adv-x="928" />
-<glyph glyph-name="mail-1" unicode="&#xe810;" d="m467 366l-449 210 0 109 893 0 0-109z m0-123l444 210 0-437-893 0 0 438z" horiz-adv-x="928" />
-<glyph glyph-name="search" unicode="&#xe811;" d="m772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" />
-<glyph glyph-name="move" unicode="&#xe812;" d="m1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-26l-143-142q-10-11-25-11t-25 11l-143 142q-11 11-11 26t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-26 11l-142 143q-11 11-11 25t11 25l142 143q11 11 26 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
-<glyph glyph-name="upload" unicode="&#xe813;" d="m500 776l260-244-164 0 0-256-190 0 0 256-166 0z m468-578q18-10 27-32t3-40l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-10 48 32 72l158 108 98 0-170-130 178 0q8 0 12-8l40-110 300 0 40 110q8 8 12 8l178 0-170 130 98 0z" horiz-adv-x="1000" />
-<glyph glyph-name="upload-cloud" unicode="&#xe814;" d="m760 494q100 0 170-68t70-166-70-166-170-68l-190 0 0 190 106 0-176 230-174-230 104 0 0-190-248 0q-74 0-128 52t-54 124q0 74 53 126t129 52q14 0 20-2-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z" horiz-adv-x="1000" />
-<glyph glyph-name="key" unicode="&#xe815;" d="m774 612q20-116-28-215t-150-117q-66-12-130-2l-118-194-70-12-104-166q-14-28-46-32l-76-14q-12-4-22 4t-12 22l-16 98q-8 30 12 56l258 386q-24 50-38 120-18 106 53 187t185 101q106 20 195-45t107-177z m-126-76q30 44 21 97t-51 83q-42 32-92 22t-80-54q-8-12-12-23t-1-20 5-16 13-17 18-15 22-16 23-17q6-4 22-16t23-16 19-12 19-8 17 1 18 8 16 19z" horiz-adv-x="780" />
-<glyph glyph-name="network" unicode="&#xe816;" d="m224 504q-22 0-42-10-48 52-92 128 54 74 128 118 92-38 152-82-6-16-6-32 0-6 4-22-62-48-116-104-16 4-28 4z m-98-98q0-34 20-60-60-114-80-232-66 106-66 236 0 110 50 206 38-62 84-112-8-24-8-38z m336 318q-28 0-50-14-58 42-114 70 84 30 162 30 120 0 230-62-76-14-162-50-26 26-66 26z m146-460q-160 24-290 112 4 20 4 30 0 24-14 52 38 44 98 90 26-20 56-20 14 0 38 8 94-108 134-240-16-14-26-32z m128-132q38 14 54 60 58 4 108 18-46-146-166-232 6 48 6 98 0 10-1 28t-1 28z m-150 66q-190-96-304-272-90 36-158 110 12 144 80 274 6-2 20-2 30 0 52 14 142-98 310-124z m184 492q150-136 150-340 0-22-4-66-64-18-130-24-24 56-88 60-48 146-148 264 10 20 10 42l0 10q100 42 210 54z m-136-544q14-10 32-18 2-18 2-52 0-80-14-144-86-42-194-42-58 0-112 12 110 160 286 244z" horiz-adv-x="920" />
-<glyph glyph-name="info-circled-1" unicode="&#xe817;" d="m454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m52-152q-42 0-65-24t-23-50q-2-28 15-44t49-16q38 0 61 22t23 54q0 58-60 58z m-120-594q30 0 84 26t106 78l-18 24q-48-36-72-36-14 0-4 38l42 160q26 96-22 96-30 0-89-29t-115-75l16-26q52 34 74 34 12 0 0-34l-36-152q-26-104 34-104z" horiz-adv-x="920" />
-<glyph glyph-name="calendar" unicode="&#xe818;" d="m71-79h161v161h-161v-161z m197 0h178v161h-178v-161z m-197 197h161v178h-161v-178z m197 0h178v178h-178v-178z m-197 214h161v161h-161v-161z m411-411h179v161h-179v-161z m-214 411h178v161h-178v-161z m428-411h161v161h-161v-161z m-214 197h179v178h-179v-178z m-196 482v161q0 7-6 12t-12 6h-36q-7 0-12-6t-6-12v-161q0-7 6-13t12-5h36q7 0 12 5t6 13z m410-482h161v178h-161v-178z m-214 214h179v161h-179v-161z m214 0h161v161h-161v-161z m18 268v161q0 7-5 12t-13 6h-35q-8 0-13-6t-5-12v-161q0-7 5-13t13-5h35q8 0 13 5t5 13z m215 36v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50v715q0 29 21 50t50 21h72v54q0 36 26 63t63 26h36q37 0 63-26t26-63v-54h214v54q0 36 27 63t63 26h35q37 0 63-26t27-63v-54h71q29 0 50-21t22-50z" horiz-adv-x="928.6" />
-<glyph glyph-name="ok" unicode="&#xe819;" d="m932 534q0-22-15-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q15-16 15-38z" horiz-adv-x="1000" />
-<glyph glyph-name="help-circled" unicode="&#xe81a;" d="m454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m-2-740q30 0 49 19t19 47q2 30-17 49t-49 19l-2 0q-28 0-47-18t-21-46q0-30 19-49t47-21l2 0z m166 328q26 34 26 78 0 78-54 116-52 38-134 38-64 0-104-26-68-42-72-146l0-4 110 0 0 4q0 26 16 54 16 24 54 24 40 0 52-20 16-20 16-44 0-18-16-40-8-12-20-20l-6-4q-6-4-16-11t-20-15-21-17-17-17q-14-20-18-78l0-8 108 0 0 4q0 12 4 28 6 20 28 36l28 18q46 34 56 50z" horiz-adv-x="920" />
-<glyph glyph-name="logout" unicode="&#xe81b;" d="m357 46q0-2 1-11t0-14-2-14-5-11-12-3h-178q-67 0-114 47t-47 114v392q0 67 47 114t114 47h178q8 0 13-5t5-13q0-2 1-11t0-15-2-13-5-11-12-3h-178q-37 0-63-27t-27-63v-392q0-37 27-63t63-27h174t6 0 7-2 4-3 4-5 1-8z m518 304q0-14-11-25l-303-304q-11-10-25-10t-25 10-11 25v161h-250q-14 0-25 11t-11 25v214q0 15 11 25t25 11h250v161q0 14 11 25t25 10 25-10l303-304q11-10 11-25z" horiz-adv-x="928.6" />
-<glyph glyph-name="flag" unicode="&#xe81c;" d="m874 616q14 6 22-1t0-19q-96-138-164-213t-110-90-73-2-60 37-63 40-93-4-139-86l90-352-100 0-184 720 92 34q90 66 152 86t98 3 64-51 62-71 79-62 129-20 198 51z" horiz-adv-x="900" />
-<glyph glyph-name="spin6" unicode="&#xe81d;" d="m855 9c-189-190-520-172-705 13-190 190-200 494-28 695 11 13 21 26 35 34 36 23 85 18 117-13 30-31 35-76 16-112-5-9-9-15-16-22-140-151-145-379-8-516 153-153 407-121 542 34 106 122 142 297 77 451-83 198-305 291-510 222l0 1c236 82 492-24 588-252 71-167 37-355-72-493-11-15-23-29-36-42z" horiz-adv-x="1000" />
-<glyph glyph-name="hourglass" unicode="&#xe81e;" d="m560 622q0-44-48-96t-97-99-49-77 49-76 97-97 48-97l0-118q0-34-86-73t-194-39-194 39-86 73l0 118q0 46 48 97t97 97 49 76-49 77-97 99-48 96l0 118q0 32 87 71t193 39 193-39 87-71l0-118z m-482 112l-18-14q-4-8 4-14 92-52 216-52 132 0 220 50 14 10-16 30-96 54-202 54-120 0-204-54z m228-384q0 18 4 33t18 33 20 25 31 31 29 28q92 92 92 122l2 50q-100-54-222-54t-222 54l4-50q0-32 90-122 6-6 22-21t23-22l19-19t17-21 11-20 9-23 3-24q0-10-1-19t-6-18-8-16-11-17l-12-15t-15-16-16-15-18-16-17-16q-90-90-90-122l0-66q8 4 66 23t92 43 34 58q0 30 26 30t26-30q0-34 33-58t94-43 67-23l0 66q0 30-92 122-4 4-21 20t-22 21-18 19-18 22-12 20-9 23-2 23z" horiz-adv-x="560" />
-<glyph glyph-name="check" unicode="&#xe81f;" d="m786 331v-177q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-6-5-13-5-2 0-5 1-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v141q0 8 5 13l36 35q6 6 13 6 3 0 7-2 11-4 11-16z m129 273l-455-454q-13-14-31-14t-32 14l-240 240q-14 13-14 31t14 32l61 62q14 13 32 13t32-13l147-147 361 361q13 13 31 13t32-13l62-61q13-14 13-32t-13-32z" horiz-adv-x="928.6" />
-<glyph glyph-name="check-empty" unicode="&#xe820;" d="m625 707h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v464q0 37-26 63t-63 26z m161-89v-464q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q66 0 114-48t47-113z" horiz-adv-x="785.7" />
-<glyph glyph-name="cancel-circled" unicode="&#xe821;" d="m641 224q0 14-10 25l-101 101 101 101q10 11 10 25 0 15-10 26l-51 50q-10 11-25 11-15 0-25-11l-101-101-101 101q-11 11-26 11-15 0-25-11l-50-50q-11-11-11-26 0-14 11-25l101-101-101-101q-11-11-11-25 0-15 11-26l50-50q10-11 25-11 15 0 26 11l101 101 101-101q10-11 25-11 15 0 25 11l51 50q10 11 10 26z m216 126q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
-<glyph glyph-name="cancel" unicode="&#xe822;" d="m724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
-<glyph glyph-name="plus" unicode="&#xe823;" d="m786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q22 0 38-16t16-38z" horiz-adv-x="785.7" />
-<glyph glyph-name="ok-circled" unicode="&#xe824;" d="m717 440q0 16-11 26l-50 50q-11 11-25 11t-26-11l-227-227-126 126q-11 11-25 11t-26-11l-50-50q-10-10-10-26 0-15 10-25l202-202q10-10 25-10 15 0 25 10l303 303q11 10 11 25z m140-90q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
-<glyph glyph-name="heart-broken" unicode="&#xe825;" d="m0 530q0 103 73 176t177 73q81 0 147-47l-14-250q0-12 9-20t21-9h10q12 0 18-8t3-20l-57-172q-4-12-2-13t9 8l194 267q7 9 4 16t-16 7h-77q-12 0-17 8t-1 18l82 183q57 32 124 32 103 0 177-73t73-176q0-94-63-166l-343-403q-25-30-62-30t-63 30l-342 403q-64 72-64 166z" horiz-adv-x="937.5" />
-<glyph glyph-name="zoom-in" unicode="&#xe827;" d="m571 404v-36q0-7-5-13t-12-5h-125v-125q0-7-6-13t-12-5h-36q-7 0-13 5t-5 13v125h-125q-7 0-12 5t-6 13v36q0 7 6 12t12 5h125v125q0 8 5 13t13 5h36q7 0 12-5t6-13v-125h125q7 0 12-5t5-12z m72-18q0 103-74 176t-176 74-177-74-73-176 73-177 177-73 176 73 74 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 152-31 126-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
-<glyph glyph-name="zoom-out" unicode="&#xe828;" d="m571 404v-36q0-7-5-13t-12-5h-322q-7 0-12 5t-6 13v36q0 7 6 12t12 5h322q7 0 12-5t5-12z m72-18q0 103-74 176t-176 74-177-74-73-176 73-177 177-73 176 73 74 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 152-31 126-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
-<glyph glyph-name="tasks" unicode="&#xf0ae;" d="m571 64h358v72h-358v-72z m-214 286h572v71h-572v-71z m357 286h215v71h-215v-71z m286-465v-142q0-15-11-25t-25-11h-928q-15 0-25 11t-11 25v142q0 15 11 26t25 10h928q15 0 25-10t11-26z m0 286v-143q0-14-11-25t-25-10h-928q-15 0-25 10t-11 25v143q0 15 11 25t25 11h928q15 0 25-11t11-25z m0 286v-143q0-15-11-25t-25-11h-928q-15 0-25 11t-11 25v143q0 14 11 25t25 11h928q15 0 25-11t11-25z" horiz-adv-x="1000" />
-<glyph glyph-name="filter" unicode="&#xf0b0;" d="m783 685q9-23-8-39l-275-275v-414q0-23-22-33-7-3-14-3-15 0-25 11l-143 143q-10 10-10 25v271l-275 275q-18 16-8 39 9 22 33 22h714q23 0 33-22z" horiz-adv-x="785.7" />
-<glyph glyph-name="mail-alt" unicode="&#xf0e0;" d="m1000 454v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-28 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-33-15-27-5h-2q-12 0-27 5t-33 15-29 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 62-26t27-63z" horiz-adv-x="1000" />
-<glyph glyph-name="sitemap" unicode="&#xf0e8;" d="m1000 154v-179q0-22-16-38t-38-16h-178q-22 0-38 16t-16 38v179q0 22 16 38t38 15h53v107h-285v-107h53q23 0 38-15t16-38v-179q0-22-16-38t-38-16h-178q-23 0-38 16t-16 38v179q0 22 16 38t38 15h53v107h-285v-107h53q22 0 38-15t16-38v-179q0-22-16-38t-38-16h-178q-23 0-38 16t-16 38v179q0 22 16 38t38 15h53v107q0 29 21 51t51 21h285v107h-53q-23 0-38 15t-16 38v179q0 22 16 38t38 16h178q23 0 38-16t16-38v-179q0-22-16-38t-38-15h-53v-107h285q29 0 51-21t21-51v-107h53q23 0 38-15t16-38z" horiz-adv-x="1000" />
-<glyph glyph-name="crop" unicode="&#xf125;" d="m311 136h332v332z m-25 25l332 332h-332v-332z m643-43v-107q0-8-5-13t-13-5h-125v-125q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v125h-482q-8 0-13 5t-5 13v482h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125v125q0 7 5 12t13 5h107q8 0 13-5t5-12v-125h475l137 138q5 5 13 5t13-5q5-6 5-13t-5-13l-138-137v-475h125q8 0 13-5t5-13z" horiz-adv-x="928.6" />
-<glyph glyph-name="picture" unicode="&#x1f304;" d="m357 529q0-45-31-76t-76-32-76 32-31 76 31 75 76 32 76-32 31-75z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-8 6-13t12-5h893q7 0 13 5t5 13v678q0 7-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
-<glyph glyph-name="user" unicode="&#x1f464;" d="m786 66q0-67-41-106t-108-39h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q5 0 24-12t41-27 60-27 75-12 74 12 61 27 41 27 24 12q34 0 62-11t48-30 34-45 24-55 15-60 8-61 2-58z m-179 498q0-88-63-151t-151-63-152 63-62 151 62 152 152 63 151-63 63-152z" horiz-adv-x="785.7" />
-<glyph glyph-name="shuffle" unicode="&#x1f500;" d="m372 582q-34-52-77-153-12 25-20 41t-23 35-28 32-36 19-45 8h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125q139 0 229-125z m628-446q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107q-18 0-48 0t-45-1-41 1-39 3-36 6-35 10-32 16-33 22-31 30-31 39q33 52 76 152 12-25 20-40t23-36 28-31 35-20 46-8h143v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z m0 500q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107h-143q-27 0-49-8t-38-25-29-35-25-43q-18-34-43-95-16-37-28-62t-30-59-36-55-41-47-50-38-60-23-71-10h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125q27 0 48 9t39 25 28 34 26 43q17 35 43 96 16 36 28 62t30 58 36 56 41 46 50 38 59 24 72 9h143v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z" horiz-adv-x="1000" />
-<glyph glyph-name="lock" unicode="&#x1f512;" d="m179 421h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
-<glyph glyph-name="link" unicode="&#x1f517;" d="m812 171q0 23-15 38l-116 116q-16 16-38 16-24 0-40-18 1-1 10-10t12-12 9-11 7-14 2-15q0-23-16-38t-38-16q-8 0-15 2t-14 7-11 9-12 12-10 10q-19-17-19-40 0-23 16-38l115-116q15-15 38-15 22 0 38 15l82 81q15 16 15 37z m-392 394q0 22-15 38l-115 115q-16 16-38 16-22 0-38-15l-82-82q-16-15-16-37 0-22 16-38l116-116q15-15 38-15 23 0 40 17-2 2-11 11t-12 12-8 10-7 14-2 16q0 22 15 38t38 15q9 0 16-2t14-7 10-8 12-12 11-11q18 17 18 41z m500-394q0-67-48-113l-82-81q-46-47-113-47-68 0-114 48l-115 115q-46 47-46 114 0 68 49 116l-49 49q-48-49-116-49-67 0-114 47l-116 116q-47 47-47 114t47 113l82 82q47 46 114 46 67 0 114-47l114-116q47-46 47-113 0-69-49-117l49-49q48 49 116 49 67 0 114-47l116-116q47-47 47-114z" horiz-adv-x="928.6" />
-<glyph glyph-name="wrench" unicode="&#x1f527;" d="m214 29q0 14-10 25t-25 10-26-10-10-25 10-26 26-10 25 10 10 26z m360 234l-381-381q-21-20-50-20-29 0-51 20l-59 61q-21 20-21 50 0 29 21 51l380 380q22-55 64-97t97-64z m353 243q0-22-12-59-27-75-92-122t-144-46q-104 0-177 73t-73 177 73 176 177 74q32 0 67-10t60-26q9-6 9-15t-9-16l-163-94v-125l108-60q2 2 44 27t75 45 40 20q8 0 13-5t4-14z" horiz-adv-x="928.6" />
-<glyph glyph-name="clock" unicode="&#x1f554;" d="m500 546v-250q0-7-5-12t-13-5h-178q-8 0-13 5t-5 12v36q0 8 5 13t13 5h125v196q0 8 5 13t12 5h36q8 0 13-5t5-13z m232-196q0 83-41 152t-110 111-152 41-153-41-110-111-41-152 41-152 110-111 153-41 152 41 110 111 41 152z m125 0q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
-<glyph glyph-name="block" unicode="&#x1f6ab;" d="m732 352q0 90-48 164l-421-420q76-50 166-50 62 0 118 25t96 65 65 97 24 119z m-557-167l421 421q-75 50-167 50-83 0-153-40t-110-112-41-152q0-91 50-167z m682 167q0-88-34-168t-91-137-137-92-166-34-167 34-137 92-91 137-34 168 34 167 91 137 137 91 167 34 166-34 137-91 91-137 34-167z" horiz-adv-x="857.1" />
+<glyph glyph-name="star" unicode="&#x2605;" d="M929 489q0-12-15-27l-203-197 48-279q1-4 1-12 0-11-6-19t-17-9q-10 0-22 7l-251 132-250-132q-13-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="star-empty" unicode="&#x2606;" d="M634 290l171 165-235 35-106 213-105-213-236-35 171-165-41-235 211 111 211-111z m295 199q0-12-15-27l-203-197 48-279q1-4 1-12 0-28-23-28-10 0-22 7l-251 132-250-132q-13-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="cog" unicode="&#x2699;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 50-20l80 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q14-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="cog-alt" unicode="&#x26ef;" d="M500 350q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m429-286q0 29-22 51t-50 21-50-21-21-51q0-29 21-50t50-21 51 21 21 50z m0 572q0 29-22 50t-50 21-50-21-21-50q0-30 21-51t50-21 51 21 21 51z m-215-235v-103q0-6-4-11t-9-6l-86-14q-6-19-18-42 19-27 50-64 4-6 4-11 0-7-4-11-13-17-46-50t-44-33q-6 0-11 4l-64 50q-21-11-43-17-6-60-13-87-4-13-17-13h-104q-6 0-11 4t-5 10l-13 85q-19 6-42 18l-66-50q-4-4-11-4-6 0-12 4-80 75-80 90 0 5 4 10 5 8 23 30t26 34q-13 24-20 46l-85 13q-5 1-9 5t-4 11v103q0 6 4 11t9 6l86 14q7 19 18 42-19 27-50 64-4 6-4 11 0 7 4 11 12 17 46 50t44 33q6 0 12-4l64-50q19 10 43 18 6 60 13 86 3 13 16 13h104q6 0 11-4t6-10l13-85q19-6 41-17l66 49q5 4 11 4 7 0 12-4 81-75 81-90 0-5-4-10-7-9-24-30t-25-34q13-27 19-46l85-12q5-2 9-6t4-11z m357-298v-78q0-9-83-17-6-15-16-29 28-63 28-77 0-2-2-4-68-40-69-40-5 0-26 27t-29 37q-11-1-17-1t-17 1q-7-11-29-37t-25-27q-1 0-69 40-3 2-3 4 0 14 29 77-10 14-17 29-83 8-83 17v78q0 9 83 18 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 1 17 1t17-1q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-9 83-18z m0 572v-78q0-9-83-18-6-15-16-29 28-63 28-77 0-2-2-4-68-39-69-39-5 0-26 26t-29 38q-11-1-17-1t-17 1q-7-12-29-38t-25-26q-1 0-69 39-3 2-3 4 0 14 29 77-10 14-17 29-83 9-83 18v78q0 9 83 17 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 2 17 2t17-2q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-8 83-17z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="mail" unicode="&#x2709;" d="M929 11v428q-18-20-39-37-149-114-238-188-28-24-46-38t-48-27-57-13h-2q-26 0-57 13t-48 27-46 38q-88 74-238 188-21 17-39 37v-428q0-8 6-13t12-5h822q7 0 12 5t6 13z m0 586v14t-1 7-1 7-3 5-5 4-8 2h-822q-7 0-12-6t-6-12q0-94 82-159 108-84 224-177 4-2 20-16t25-21 25-17 28-16 24-5h2q11 0 24 5t28 16 25 17 25 21 20 16q116 93 224 177 30 24 56 65t26 73z m71 21v-607q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v607q0 37 26 63t63 26h822q37 0 63-26t26-63z" horiz-adv-x="1000" />
+
+<glyph glyph-name="pencil" unicode="&#x270e;" d="M203-7l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="plus-circled" unicode="&#x2795;" d="M679 314v72q0 14-11 25t-25 10h-143v143q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-143h-143q-14 0-25-10t-10-25v-72q0-14 10-25t25-10h143v-143q0-15 11-25t25-11h71q15 0 25 11t11 25v143h143q14 0 25 10t11 25z m178 36q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="asl" unicode="&#xe0ad;" d="M1 700q-4 37 15 53t45 5 35-38l53-184q-15-29-14-64 6-30 36-204 7-52 32-82t65-29q47 0 74 37 34-39 83-37t74 41q27-37 71-41t73 27q12 16 20 32t11 37 5 33 1 40l0 38q26-10 61 24 49 45 109 78t98 8q33-24 31-59t-35-55q-61-25-104-82t-67-112-58-117-78-93q-21-18-66-18l-350 0q-84 6-105 118z m181-234q-6 45 21 65t56 8 37-48l21-209q4-41-17-63t-46-15-37 45z m155 70q2 41 32 56t62-1 36-49l8-248q0-59-28-76t-57 5-30 61z m175 4q4 43 37 55t62-7 30-52l-6-254q-5-47-34-66t-54 1-29 67z" horiz-adv-x="979" />
+
+<glyph glyph-name="eye" unicode="&#xe70a;" d="M929 314q-85 132-213 197 34-58 34-125 0-104-73-177t-177-73-177 73-73 177q0 67 34 125-128-65-213-197 75-114 187-182t242-68 242 68 187 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38-78-129-210-206t-279-77-279 77-210 206q-11 19-11 38t11 39q78 128 210 205t279 78 279-78 210-205q11-20 11-39z" horiz-adv-x="1000" />
+
+<glyph glyph-name="tags" unicode="&#xe70d;" d="M250 600q0 30-21 51t-50 20-51-20-21-51 21-50 51-21 50 21 21 50z m595-321q0-30-20-51l-274-274q-22-21-51-21-30 0-50 21l-399 399q-21 21-36 57t-15 65v232q0 29 21 50t50 22h233q29 0 65-15t57-36l399-399q20-21 20-50z m215 0q0-30-21-51l-274-274q-22-21-51-21-20 0-33 8t-29 25l262 262q21 21 21 51 0 29-21 50l-399 399q-21 21-57 36t-65 15h125q29 0 65-15t57-36l399-399q21-21 21-50z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="chat" unicode="&#xe720;" d="M786 421q0-77-53-143t-143-104-197-38q-48 0-98 9-70-49-155-72-21-5-48-9h-2q-6 0-12 5t-6 12q-1 1-1 3t1 4 1 3l1 3t2 3 2 3 3 3 2 2q3 3 13 14t15 16 12 17 14 21 11 25q-69 40-108 98t-40 125q0 78 53 144t143 104 197 38 197-38 143-104 53-144z m214-142q0-67-40-126t-108-98q5-14 11-25t14-21 13-16 14-17 13-14q0 0 2-2t3-3 2-3 2-3l1-3t1-3 1-4-1-3q-2-8-7-13t-13-4q-27 4-48 9-85 23-155 72-50-9-98-9-151 0-263 74 32-3 49-3 90 0 172 25t148 72q69 52 107 119t37 141q0 43-13 85 72-39 114-99t42-128z" horiz-adv-x="1000" />
+
+<glyph glyph-name="trash" unicode="&#xe729;" d="M286 439v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m143 0v-321q0-8-5-13t-13-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q8 0 13-5t5-13z m142 0v-321q0-8-5-13t-12-5h-36q-8 0-13 5t-5 13v321q0 8 5 13t13 5h36q7 0 12-5t5-13z m72-404v529h-500v-529q0-12 4-22t8-15 6-5h464q2 0 6 5t8 15 4 22z m-375 601h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q22 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="basket" unicode="&#xe73d;" d="M357-7q0-29-21-50t-50-22-50 22-22 50 22 50 50 21 50-21 21-50z m500 0q0-29-21-50t-50-22-51 22-21 50 21 50 51 21 50-21 21-50z m72 607v-286q0-13-10-23t-22-12l-583-68q7-34 7-40 0-8-13-35h513q15 0 26-11t10-25-10-25-26-11h-571q-14 0-25 11t-11 25q0 6 5 18t9 20 12 22 8 16l-98 460h-114q-15 0-25 10t-11 25 11 26 25 10h143q9 0 16-3t10-9 8-14 4-14 3-17 3-14h670q14 0 25-11t11-25z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="puzzle" unicode="&#xe7b6;" d="M586 402q-37 0-64-34t-27-82 27-84 64-34q22 0 43 13 26 17 37 11t12-24l0-144q0-31-24-54t-55-23l-143 0q-16 0-23 9t2 31q21 25 21 51 0 38-34 64t-83 26-83-26-35-64q0-22 13-44 17-26 12-37t-25-10l-143 0q-33 0-55 23t-23 54l0 144q0 16 9 23t31-3q24-20 51-20 38 0 65 34t27 84-27 82-65 34q-21 0-42-13-26-17-38-12t-11 25l0 91q0 33 23 56t55 23l143 0q19 0 25 10t-11 37q-14 20-14 44 0 38 35 64t83 26 83-26 34-64q0-26-21-51-10-21-2-31t23-9l143 0q31 0 55-23t24-56l0-91q0-16-10-23t-30 3q-24 20-52 20z" horiz-adv-x="678" />
+
+<glyph glyph-name="language" unicode="&#xe800;" d="M988 306q30-82-10-176t-134-160q-10 0-12 2t-16 19-16 19q-2 6 2 10 86 60 117 152t-11 148q-16-38-39-76t-59-80-86-65-106-15q-52 6-84 41t-32 93q0 84 60 148 50 50 114 66l-2 100q-140-24-146-24-6-2-10 4 0 2-5 29t-5 31q-2 2 1 4t7 2l156 28q0 110-2 114 0 8 8 8 46 0 52 2 10 0 10-8l0-104q158 22 164 22 8 4 10-6 0-2 4-23t4-25q4-10-4-12l-176-30 0-102 12 0q86 0 148-36t86-100z m-370-160q28-6 62 6l-4 214q-34-12-60-40-44-44-44-108 0-66 46-72z m122 28q28 24 58 68t45 79 7 41q-36 18-96 18-2 0-6-1t-6-1z m-448 382q10-28 53-165t83-261 40-126q0-4-4-4l-86 0q-6 0-6 4l-50 166-176 0q-48-164-50-166 0-4-6-4l-86 0q-4 0-4 4 10 18 176 552 2 8 10 8l96 0q10 0 10-8z m-130-316l144 0-72 264z" horiz-adv-x="1001" />
+
+<glyph glyph-name="signal" unicode="&#xe801;" d="M143 46v-107q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v107q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 72v-179q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v179q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 143v-322q0-8-5-13t-12-5h-108q-7 0-12 5t-5 13v322q0 8 5 13t12 5h108q7 0 12-5t5-13z m215 214v-536q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v536q0 8 5 13t13 5h107q8 0 13-5t5-13z m214 286v-822q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v822q0 8 5 13t13 5h107q8 0 13-5t5-13z" horiz-adv-x="1000" />
+
+<glyph glyph-name="users" unicode="&#xe802;" d="M0 49q20 7 60 22t69 26 50 22q37 19 57 56t21 76q0 27-31 64-19 21-33 60-45 18-45 76 0 43 14 61-5 39-9 82-5 55 26 108t84 84 110 32q43 0 85-19t73-50 48-72 14-83q-4-55-8-82 14-19 14-61 0-58-45-76-15-41-32-60-32-37-32-64 0-39 20-76t58-56q29-15 92-38t113-43 66-38q18-17 28-47t10-51l1-21h-878v168z m705 299q0 34 12 50-5 32-7 67-4 45 21 89t69 72 90 27 89-27 69-72 22-89q-2-35-8-67 12-16 12-50 0-48-36-62-12-33-27-50-9-10-13-16t-9-16-4-20q0-32 17-62t47-46q17-9 42-19t60-22 50-19v-135h-254q0 4 0 11t-3 26-7 36-15 38-23 33q-23 25-53 44t-79 37q27 34 27 78 0 10-5 20t-8 16-12 16q-14 15-27 49-37 16-37 63z" horiz-adv-x="1201.2" />
+
+<glyph glyph-name="tools" unicode="&#xe803;" d="M155 506q-8-8-11-22t-3-25-2-11q-2-2-17-15t-19-17q-16-14-28 4l-70 76q-11 12 2 24 2 2 18 14t20 16q6 6 27 6t37 14q14 14 18 38t10 30q2 0 9 7t26 22 41 31q134 90 186 96 122 0 148-2 12 0-8-8-120-52-152-76-80-56-36-114 34-46 38-48 8-8-2-14-2-2-38-35t-38-35q-14-8-18-4-42 48-71 60t-67-12z m286-26l410-476q18-22-2-38l-48-42q-22-14-38 4l-414 472q-8 8 0 20l72 62q12 8 20-2z m554 202q16-104-16-166-50-88-154-62-56 12-100-32l-82-78-68 78 68 70q24 24 31 53t6 65 5 58q12 56 140 112 12 6 18-3t2-15q-12-12-46-80-14-10-12-35t40-53q58-40 96 22 6 12 26 41t22 33q4 10 13 9t11-17z m-858-684l254 248 76-86-246-242q-20-20-38-4l-46 46q-22 18 0 38z" horiz-adv-x="1000" />
+
+<glyph glyph-name="arrows-cw" unicode="&#xe804;" d="M843 261q0-3 0-4-36-150-150-243t-267-93q-81 0-157 31t-136 88l-72-72q-11-11-25-11t-25 11-11 25v250q0 14 11 25t25 11h250q14 0 25-11t10-25-10-25l-77-77q40-37 90-57t105-20q74 0 139 37t104 99q6 10 29 66 5 13 17 13h107q8 0 13-6t5-12z m14 446v-250q0-14-10-25t-26-11h-250q-14 0-25 11t-10 25 10 25l77 77q-82 77-194 77-75 0-140-37t-104-99q-6-10-29-66-5-13-17-13h-111q-7 0-13 6t-5 12v4q36 150 151 243t268 93q81 0 158-31t137-88l72 72q11 11 25 11t26-11 10-25z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="exchange" unicode="&#xe805;" d="M1000 189v-107q0-7-5-12t-13-6h-768v-107q0-7-5-12t-13-6q-6 0-13 6l-178 178q-5 5-5 13 0 8 5 13l179 178q5 5 12 5 8 0 13-5t5-13v-107h768q7 0 13-5t5-13z m0 304q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107h-768q-7 0-13 6t-5 12v107q0 8 5 13t13 5h768v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z" horiz-adv-x="1000" />
+
+<glyph glyph-name="folder-open" unicode="&#xe806;" d="M1049 319q0-18-18-37l-187-221q-24-28-67-48t-81-20h-607q-19 0-33 7t-15 24q0 17 17 37l188 221q24 28 67 48t80 20h607q19 0 34-7t15-24z m-192 192v-90h-464q-53 0-110-26t-92-67l-188-221-2-3q0 2-1 7t0 7v536q0 51 37 88t88 37h179q51 0 88-37t37-88v-18h303q51 0 88-37t37-88z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="brush" unicode="&#xe807;" d="M118 170q38 34 85 29t87-45q42-40 48-87t-30-83q-86-84-228-102-84-12-80 14 0 4 6 10 52 60 64 145t48 119z m840 646q26-26-148-248t-292-338q-38-38-124-104-8-6-16 8-18 34-48 64-32 32-66 48-16 6-8 16 64 84 104 122 118 116 344 287t254 145z" horiz-adv-x="962" />
+
+<glyph glyph-name="flow-branch" unicode="&#xe808;" d="M640 650q0-80-74-110-6-58-28-101t-61-69-68-38-75-26q-42-14-63-22t-47-24-38-40-16-60q70-30 70-110 0-50-35-85t-85-35-85 35-35 85q0 78 72 112l0 378q-72 34-72 110 0 50 35 85t85 35 85-35 35-85q0-76-72-110l0-204q40 30 138 60 58 18 84 29t51 41 29 76q-70 32-70 108 0 50 35 85t85 35 85-35 35-85z m-588 0q0-28 20-48t48-20 49 20 21 48q0 30-21 50t-49 20-48-20-20-50z m68-668q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z m400 600q28 0 49 20t21 48q0 30-21 50t-49 20-48-20-20-50q0-28 20-48t48-20z" horiz-adv-x="640" />
+
+<glyph glyph-name="menu" unicode="&#xe809;" d="M857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="code" unicode="&#xe80a;" d="M344 69l-28-28q-5-5-12-5t-13 5l-260 260q-6 6-6 13t6 13l260 260q5 6 13 6t12-6l28-28q6-5 6-13t-6-12l-219-220 219-219q6-6 6-13t-6-13z m330 596l-208-721q-2-7-9-11t-13-1l-34 9q-8 3-11 9t-2 14l208 720q3 8 9 11t13 2l35-10q7-2 11-9t1-13z m367-364l-260-260q-6-5-13-5t-13 5l-28 28q-5 6-5 13t5 13l219 219-219 220q-5 5-5 12t5 13l28 28q6 6 13 6t13-6l260-260q5-5 5-13t-5-13z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="user-add" unicode="&#xe80b;" d="M620 128q180-64 180-122l0-106-800 0 0 202q36 14 82 26 94 34 129 69t35 95q0 22-23 48t-31 74q-2 12-23 25t-25 61q0 16 5 26t9 12l4 4q-8 50-12 88-6 54 40 112t160 58 160-58 42-112l-14-88q18-8 18-42-2-28-9-43t-14-17-14-8-9-18q-10-46-33-73t-23-49q0-60 36-95t130-69z m230 272l150 0 0-100-150 0 0-150-100 0 0 150-150 0 0 100 150 0 0 150 100 0 0-150z" horiz-adv-x="1000" />
+
+<glyph glyph-name="cw" unicode="&#xe80c;" d="M857 707v-250q0-14-10-25t-26-11h-250q-23 0-32 23-10 22 7 38l77 77q-82 77-194 77-58 0-111-23t-91-61-62-91-22-111 22-111 62-91 91-61 111-23q66 0 125 29t100 82q4 6 13 7 8 0 14-5l76-77q5-4 6-11t-5-13q-60-74-147-114t-182-41q-87 0-167 34t-136 92-92 137-34 166 34 166 92 137 136 92 167 34q82 0 158-31t137-88l72 72q16 18 39 8 22-9 22-33z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="group" unicode="&#xe80d;" d="M0 106l0 134q0 26 18 32l171 80q-66 39-68 131 0 56 35 103 37 41 90 43 31 0 63-19-49-125 23-237-12-11-25-19l-114-55q-48-23-52-84l0-143-114 0q-25 0-27 34z m193-59l0 168q0 27 22 37l152 70 57 28q-37 23-60 66t-22 94q0 76 46 130t110 54 109-54 45-130q0-105-78-158l61-30 146-70q24-10 24-37l0-168q-2-37-37-41l-541 0q-14 2-24 14t-10 27z m473 330q68 106 22 231 31 19 66 21 49 0 90-43 35-41 35-103 0-82-65-131l168-80q18-10 18-32l0-134q0-32-27-34l-118 0 0 143q0 57-50 84l-110 53q-15 8-29 25z" horiz-adv-x="1000" />
+
+<glyph glyph-name="home" unicode="&#xe80e;" d="M888 336q16-16 11-27t-27-11l-84 0 0-310q0-14-1-21t-8-13-23-6l-204 0 0 310-204 0 0-310-194 0q-28 0-35 10t-7 30l0 310-84 0q-22 0-27 11t11 27l400 402q16 16 38 16t38-16z" horiz-adv-x="900" />
+
+<glyph glyph-name="equalizer" unicode="&#xe80f;" d="M576 239l0-112-55 0 0-167q0-23-17-40t-39-17-39 17-17 40l0 167-56 0 0 112 56 0 0 503q0 24 17 39t38 16q24 0 41-16t16-39l0-503 55 0z m335 335l0-112-55 0 0-502q0-23-16-40t-41-17q-23 0-39 17t-16 40l0 502-56 0 0 112 56 0 0 168q0 24 16 39t39 16 41-16 16-39l0-168 55 0z m-670-112l0-111-55 0 0-391q0-23-16-40t-40-17q-23 0-39 17t-17 40l0 391-56 0 0 111 56 0 0 280q0 24 16 39t40 16 40-16 16-39l0-280 55 0z" horiz-adv-x="928" />
+
+<glyph glyph-name="mail-1" unicode="&#xe810;" d="M467 366l-449 210 0 109 893 0 0-109z m0-123l444 210 0-437-893 0 0 438z" horiz-adv-x="928" />
+
+<glyph glyph-name="search" unicode="&#xe811;" d="M772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" />
+
+<glyph glyph-name="move" unicode="&#xe812;" d="M1000 350q0-14-11-25l-142-143q-11-11-26-11t-25 11-10 25v72h-215v-215h72q14 0 25-10t11-25-11-26l-143-142q-10-11-25-11t-25 11l-143 142q-11 11-11 26t11 25 25 10h72v215h-215v-72q0-14-10-25t-25-11-26 11l-142 143q-11 11-11 25t11 25l142 143q11 11 26 11t25-11 10-25v-72h215v215h-72q-14 0-25 10t-11 25 11 26l143 142q11 11 25 11t25-11l143-142q11-11 11-26t-11-25-25-10h-72v-215h215v72q0 14 10 25t25 11 26-11l142-143q11-10 11-25z" horiz-adv-x="1000" />
+
+<glyph glyph-name="upload" unicode="&#xe813;" d="M500 776l260-244-164 0 0-256-190 0 0 256-166 0z m468-578q18-10 27-32t3-40l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-10 48 32 72l158 108 98 0-170-130 178 0q8 0 12-8l40-110 300 0 40 110q8 8 12 8l178 0-170 130 98 0z" horiz-adv-x="1000" />
+
+<glyph glyph-name="upload-cloud" unicode="&#xe814;" d="M760 494q100 0 170-68t70-166-70-166-170-68l-190 0 0 190 106 0-176 230-174-230 104 0 0-190-248 0q-74 0-128 52t-54 124q0 74 53 126t129 52q14 0 20-2-2 12-2 38 0 108 78 184t188 76q90 0 160-52t94-134q28 4 40 4z" horiz-adv-x="1000" />
+
+<glyph glyph-name="key" unicode="&#xe815;" d="M774 612q20-116-28-215t-150-117q-66-12-130-2l-118-194-70-12-104-166q-14-28-46-32l-76-14q-12-4-22 4t-12 22l-16 98q-8 30 12 56l258 386q-24 50-38 120-18 106 53 187t185 101q106 20 195-45t107-177z m-126-76q30 44 21 97t-51 83q-42 32-92 22t-80-54q-8-12-12-23t-1-20 5-16 13-17 18-15 22-16 23-17q6-4 22-16t23-16 19-12 19-8 17 1 18 8 16 19z" horiz-adv-x="780" />
+
+<glyph glyph-name="network" unicode="&#xe816;" d="M224 504q-22 0-42-10-48 52-92 128 54 74 128 118 92-38 152-82-6-16-6-32 0-6 4-22-62-48-116-104-16 4-28 4z m-98-98q0-34 20-60-60-114-80-232-66 106-66 236 0 110 50 206 38-62 84-112-8-24-8-38z m336 318q-28 0-50-14-58 42-114 70 84 30 162 30 120 0 230-62-76-14-162-50-26 26-66 26z m146-460q-160 24-290 112 4 20 4 30 0 24-14 52 38 44 98 90 26-20 56-20 14 0 38 8 94-108 134-240-16-14-26-32z m128-132q38 14 54 60 58 4 108 18-46-146-166-232 6 48 6 98 0 10-1 28t-1 28z m-150 66q-190-96-304-272-90 36-158 110 12 144 80 274 6-2 20-2 30 0 52 14 142-98 310-124z m184 492q150-136 150-340 0-22-4-66-64-18-130-24-24 56-88 60-48 146-148 264 10 20 10 42l0 10q100 42 210 54z m-136-544q14-10 32-18 2-18 2-52 0-80-14-144-86-42-194-42-58 0-112 12 110 160 286 244z" horiz-adv-x="920" />
+
+<glyph glyph-name="info-circled-1" unicode="&#xe817;" d="M454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m52-152q-42 0-65-24t-23-50q-2-28 15-44t49-16q38 0 61 22t23 54q0 58-60 58z m-120-594q30 0 84 26t106 78l-18 24q-48-36-72-36-14 0-4 38l42 160q26 96-22 96-30 0-89-29t-115-75l16-26q52 34 74 34 12 0 0-34l-36-152q-26-104 34-104z" horiz-adv-x="920" />
+
+<glyph glyph-name="calendar" unicode="&#xe818;" d="M71-79h161v161h-161v-161z m197 0h178v161h-178v-161z m-197 197h161v178h-161v-178z m197 0h178v178h-178v-178z m-197 214h161v161h-161v-161z m411-411h179v161h-179v-161z m-214 411h178v161h-178v-161z m428-411h161v161h-161v-161z m-214 197h179v178h-179v-178z m-196 482v161q0 7-6 12t-12 6h-36q-7 0-12-6t-6-12v-161q0-7 6-13t12-5h36q7 0 12 5t6 13z m410-482h161v178h-161v-178z m-214 214h179v161h-179v-161z m214 0h161v161h-161v-161z m18 268v161q0 7-5 12t-13 6h-35q-8 0-13-6t-5-12v-161q0-7 5-13t13-5h35q8 0 13 5t5 13z m215 36v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50v715q0 29 21 50t50 21h72v54q0 37 26 63t63 26h36q37 0 63-26t26-63v-54h214v54q0 37 27 63t63 26h35q37 0 63-26t27-63v-54h71q29 0 50-21t22-50z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="ok" unicode="&#xe819;" d="M932 534q0-22-15-38l-404-404-76-76q-16-15-38-15t-38 15l-76 76-202 202q-15 16-15 38t15 38l76 76q16 16 38 16t38-16l164-165 366 367q16 16 38 16t38-16l76-76q15-16 15-38z" horiz-adv-x="1000" />
+
+<glyph glyph-name="help-circled" unicode="&#xe81a;" d="M454 810q190 2 326-130t140-322q2-190-131-327t-323-141q-190-2-327 131t-139 323q-4 190 130 327t324 139z m-2-740q30 0 49 19t19 47q2 30-17 49t-49 19l-2 0q-28 0-47-18t-21-46q0-30 19-49t47-21l2 0z m166 328q26 34 26 78 0 78-54 116-52 38-134 38-64 0-104-26-68-42-72-146l0-4 110 0 0 4q0 26 16 54 16 24 54 24 40 0 52-20 16-20 16-44 0-18-16-40-8-12-20-20l-6-4q-6-4-16-11t-20-15-21-17-17-17q-14-20-18-78l0-8 108 0 0 4q0 12 4 28 6 20 28 36l28 18q46 34 56 50z" horiz-adv-x="920" />
+
+<glyph glyph-name="logout" unicode="&#xe81b;" d="M357 46q0-2 1-11t0-14-2-14-5-11-12-3h-178q-67 0-114 47t-47 114v392q0 67 47 114t114 47h178q8 0 13-5t5-13q0-2 1-11t0-15-2-13-5-11-12-3h-178q-37 0-63-27t-27-63v-392q0-37 27-63t63-27h174t6 0 7-2 4-3 4-5 1-8z m518 304q0-14-11-25l-303-304q-11-10-25-10t-25 10-11 25v161h-250q-14 0-25 11t-11 25v214q0 15 11 25t25 11h250v161q0 14 11 25t25 10 25-10l303-304q11-10 11-25z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="flag" unicode="&#xe81c;" d="M874 616q14 6 22-1t0-19q-96-138-164-213t-110-90-73-2-60 37-63 40-93-4-139-86l90-352-100 0-184 720 92 34q90 66 152 86t98 3 64-51 62-71 79-62 129-20 198 51z" horiz-adv-x="900" />
+
+<glyph glyph-name="spin6" unicode="&#xe81d;" d="M855 9c-189-190-520-172-705 13-190 190-200 494-28 695 11 13 21 26 35 34 36 23 85 18 117-13 30-31 35-76 16-112-5-9-9-15-16-22-140-151-145-379-8-516 153-153 407-121 542 34 106 122 142 297 77 451-83 198-305 291-510 222l0 1c236 82 492-24 588-252 71-167 37-355-72-493-11-15-23-29-36-42z" horiz-adv-x="1000" />
+
+<glyph glyph-name="hourglass" unicode="&#xe81e;" d="M560 622q0-44-48-96t-97-99-49-77 49-76 97-97 48-97l0-118q0-34-86-73t-194-39-194 39-86 73l0 118q0 46 48 97t97 97 49 76-49 77-97 99-48 96l0 118q0 32 87 71t193 39 193-39 87-71l0-118z m-482 112l-18-14q-4-8 4-14 92-52 216-52 132 0 220 50 14 10-16 30-96 54-202 54-120 0-204-54z m228-384q0 18 4 33t18 33 20 25 31 31 29 28q92 92 92 122l2 50q-100-54-222-54t-222 54l4-50q0-32 90-122 6-6 22-21t23-22l19-19t17-21 11-20 9-23 3-24q0-10-1-19t-6-18-8-16-11-17l-12-15t-15-16-16-15-18-16-17-16q-90-90-90-122l0-66q8 4 66 23t92 43 34 58q0 30 26 30t26-30q0-34 33-58t94-43 67-23l0 66q0 30-92 122-4 4-21 20t-22 21-18 19-18 22-12 20-9 23-2 23z" horiz-adv-x="560" />
+
+<glyph glyph-name="check" unicode="&#xe81f;" d="M786 331v-177q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14 9-4 10-13 2-10-5-16l-27-28q-6-5-13-5-2 0-5 1-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v141q0 8 5 13l36 35q6 6 13 6 3 0 7-2 11-4 11-16z m129 273l-455-454q-13-14-31-14t-32 14l-240 240q-14 13-14 31t14 32l61 62q14 13 32 13t32-13l147-147 361 361q13 13 31 13t32-13l62-61q13-14 13-32t-13-32z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="check-empty" unicode="&#xe820;" d="M625 707h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v464q0 37-26 63t-63 26z m161-89v-464q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q66 0 114-48t47-113z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="cancel-circled" unicode="&#xe821;" d="M641 224q0 14-10 25l-101 101 101 101q10 11 10 25 0 15-10 26l-51 50q-10 11-25 11-15 0-25-11l-101-101-101 101q-11 11-26 11-15 0-25-11l-50-50q-11-11-11-26 0-14 11-25l101-101-101-101q-11-11-11-25 0-15 11-26l50-50q10-11 25-11 15 0 26 11l101 101 101-101q10-11 25-11 15 0 25 11l51 50q10 11 10 26z m216 126q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="cancel" unicode="&#xe822;" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="plus" unicode="&#xe823;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q22 0 38-16t16-38z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="ok-circled" unicode="&#xe824;" d="M717 440q0 16-11 26l-50 50q-11 11-25 11t-26-11l-227-227-126 126q-11 11-25 11t-26-11l-50-50q-10-10-10-26 0-15 10-25l202-202q10-10 25-10 15 0 25 10l303 303q11 10 11 25z m140-90q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="heart-broken" unicode="&#xe825;" d="M0 530q0 103 73 176t177 73q81 0 147-47l-14-250q0-12 9-20t21-9h10q12 0 18-8t3-20l-57-172q-4-12-2-13t9 8l194 267q7 9 4 16t-16 7h-77q-12 0-17 8t-1 18l82 183q57 32 124 32 103 0 177-73t73-176q0-94-63-166l-343-403q-25-31-62-31t-63 31l-342 403q-64 72-64 166z" horiz-adv-x="937.5" />
+
+<glyph glyph-name="download" unicode="&#xe826;" d="M968 198q18-10 27-32t3-40l-28-154q-4-20-22-33t-40-13l-816 0q-22 0-40 13t-22 33l-28 154q-10 48 32 72l158 108 98 0-170-130 178 0q8 0 12-8l40-110 300 0 40 110q8 8 12 8l178 0-170 130 98 0z m-208 322l-260-244-260 244 166 0 0 256 190 0 0-256 164 0z" horiz-adv-x="1000" />
+
+<glyph glyph-name="zoom-in" unicode="&#xe827;" d="M571 404v-36q0-7-5-13t-12-5h-125v-125q0-7-6-13t-12-5h-36q-7 0-13 5t-5 13v125h-125q-7 0-12 5t-6 13v36q0 7 6 12t12 5h125v125q0 8 5 13t13 5h36q7 0 12-5t6-13v-125h125q7 0 12-5t5-12z m72-18q0 103-74 176t-176 74-177-74-73-176 73-177 177-73 176 73 74 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 152-31 126-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="zoom-out" unicode="&#xe828;" d="M571 404v-36q0-7-5-13t-12-5h-322q-7 0-12 5t-6 13v36q0 7 6 12t12 5h322q7 0 12-5t5-12z m72-18q0 103-74 176t-176 74-177-74-73-176 73-177 177-73 176 73 74 177z m286-465q0-29-21-50t-51-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 152-31 126-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="tasks" unicode="&#xf0ae;" d="M571 64h358v72h-358v-72z m-214 286h572v71h-572v-71z m357 286h215v71h-215v-71z m286-465v-142q0-15-11-25t-25-11h-928q-15 0-25 11t-11 25v142q0 15 11 26t25 10h928q15 0 25-10t11-26z m0 286v-143q0-14-11-25t-25-10h-928q-15 0-25 10t-11 25v143q0 15 11 25t25 11h928q15 0 25-11t11-25z m0 286v-143q0-15-11-25t-25-11h-928q-15 0-25 11t-11 25v143q0 14 11 25t25 11h928q15 0 25-11t11-25z" horiz-adv-x="1000" />
+
+<glyph glyph-name="filter" unicode="&#xf0b0;" d="M783 685q9-23-8-39l-275-275v-414q0-23-22-33-7-3-14-3-15 0-25 11l-143 143q-10 11-10 25v271l-275 275q-18 16-8 39 9 22 33 22h714q23 0 33-22z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="mail-alt" unicode="&#xf0e0;" d="M1000 454v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-28 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-33-15-27-5h-2q-12 0-27 5t-33 15-29 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 62-26t27-63z" horiz-adv-x="1000" />
+
+<glyph glyph-name="sitemap" unicode="&#xf0e8;" d="M1000 154v-179q0-22-16-38t-38-16h-178q-22 0-38 16t-16 38v179q0 22 16 38t38 15h53v107h-285v-107h53q23 0 38-15t16-38v-179q0-22-16-38t-38-16h-178q-23 0-38 16t-16 38v179q0 22 16 38t38 15h53v107h-285v-107h53q22 0 38-15t16-38v-179q0-22-16-38t-38-16h-178q-23 0-38 16t-16 38v179q0 22 16 38t38 15h53v107q0 29 21 51t51 21h285v107h-53q-23 0-38 15t-16 38v179q0 22 16 38t38 16h178q23 0 38-16t16-38v-179q0-22-16-38t-38-15h-53v-107h285q29 0 51-21t21-51v-107h53q23 0 38-15t16-38z" horiz-adv-x="1000" />
+
+<glyph glyph-name="crop" unicode="&#xf125;" d="M311 136h332v332z m-25 25l332 332h-332v-332z m643-43v-107q0-8-5-13t-13-5h-125v-125q0-8-5-13t-13-5h-107q-8 0-13 5t-5 13v125h-482q-8 0-13 5t-5 13v482h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125v125q0 8 5 13t13 5h107q8 0 13-5t5-13v-125h475l137 138q5 5 13 5t13-5q5-6 5-13t-5-13l-138-137v-475h125q8 0 13-5t5-13z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="picture" unicode="&#x1f304;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 75 76 32 76-32 31-75z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
+
+<glyph glyph-name="user" unicode="&#x1f464;" d="M786 66q0-67-41-106t-108-39h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q5 0 24-12t41-27 60-27 75-12 74 12 61 27 41 27 24 12q34 0 62-11t48-30 34-45 24-55 15-60 8-61 2-58z m-179 498q0-88-63-151t-151-63-152 63-62 151 62 152 152 63 151-63 63-152z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="shuffle" unicode="&#x1f500;" d="M372 582q-34-52-77-153-12 25-20 41t-23 35-28 32-36 19-45 8h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125q139 0 229-125z m628-446q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107q-18 0-48 0t-45-1-41 1-39 3-36 6-35 10-32 16-33 22-31 30-31 39q33 52 76 152 12-25 20-40t23-36 28-31 35-20 46-8h143v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z m0 500q0-8-5-13l-179-179q-5-5-12-5-8 0-13 6t-5 12v107h-143q-27 0-49-8t-38-25-29-35-25-43q-18-34-43-95-16-37-28-62t-30-59-36-55-41-47-50-38-60-23-71-10h-125q-8 0-13 5t-5 13v107q0 8 5 13t13 5h125q27 0 48 9t39 25 28 34 26 43q17 35 43 96 16 36 28 62t30 58 36 56 41 46 50 38 59 24 72 9h143v107q0 8 5 13t13 5q6 0 13-5l178-178q5-5 5-13z" horiz-adv-x="1000" />
+
+<glyph glyph-name="lock" unicode="&#x1f512;" d="M179 421h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
+
+<glyph glyph-name="link" unicode="&#x1f517;" d="M812 171q0 23-15 38l-116 116q-16 16-38 16-24 0-40-18 1-1 10-10t12-12 9-11 7-14 2-15q0-23-16-38t-38-16q-8 0-15 2t-14 7-11 9-12 12-10 10q-19-17-19-40 0-23 16-38l115-116q15-15 38-15 22 0 38 15l82 81q15 16 15 37z m-392 394q0 22-15 38l-115 115q-16 16-38 16-22 0-38-15l-82-82q-16-15-16-37 0-22 16-38l116-116q15-15 38-15 23 0 40 17-2 2-11 11t-12 12-8 10-7 14-2 16q0 22 15 38t38 15q9 0 16-2t14-7 10-8 12-12 11-11q18 17 18 41z m500-394q0-67-48-113l-82-81q-46-47-113-47-68 0-114 48l-115 115q-46 47-46 114 0 68 49 116l-49 49q-48-49-116-49-67 0-114 47l-116 116q-47 47-47 114t47 113l82 82q47 46 114 46 67 0 114-47l114-116q47-46 47-113 0-69-49-117l49-49q48 49 116 49 67 0 114-47l116-116q47-47 47-114z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="wrench" unicode="&#x1f527;" d="M214 29q0 14-10 25t-25 10-26-10-10-25 10-26 26-10 25 10 10 26z m360 234l-381-381q-21-20-50-20-29 0-51 20l-59 61q-21 20-21 50 0 29 21 51l380 380q22-55 64-97t97-64z m353 243q0-22-12-59-27-75-92-122t-144-46q-104 0-177 73t-73 177 73 176 177 74q32 0 67-10t60-26q9-6 9-15t-9-16l-163-94v-125l108-60q2 2 44 27t75 45 40 20q8 0 13-5t4-14z" horiz-adv-x="928.6" />
+
+<glyph glyph-name="clock" unicode="&#x1f554;" d="M500 546v-250q0-7-5-12t-13-5h-178q-8 0-13 5t-5 12v36q0 8 5 13t13 5h125v196q0 8 5 13t12 5h36q8 0 13-5t5-13z m232-196q0 83-41 152t-110 111-152 41-153-41-110-111-41-152 41-152 110-111 153-41 152 41 110 111 41 152z m125 0q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="block" unicode="&#x1f6ab;" d="M732 352q0 90-48 164l-421-420q76-50 166-50 62 0 118 25t96 65 65 97 24 119z m-557-167l421 421q-75 50-167 50-83 0-153-40t-110-111-41-153q0-91 50-167z m682 167q0-88-34-168t-91-137-137-92-166-34-167 34-137 92-91 137-34 168 34 167 91 137 137 91 167 34 166-34 137-91 91-137 34-167z" horiz-adv-x="857.1" />
</font>
</defs>
</svg> \ No newline at end of file
diff --git a/admin/themes/default/fontello/font/fontello.ttf b/admin/themes/default/fontello/font/fontello.ttf
index 2ea76648b..a1b4692dc 100644
--- a/admin/themes/default/fontello/font/fontello.ttf
+++ b/admin/themes/default/fontello/font/fontello.ttf
Binary files differ
diff --git a/admin/themes/default/fontello/font/fontello.woff b/admin/themes/default/fontello/font/fontello.woff
index 0da2b5825..a84f2ff1e 100644
--- a/admin/themes/default/fontello/font/fontello.woff
+++ b/admin/themes/default/fontello/font/fontello.woff
Binary files differ
diff --git a/admin/themes/default/fontello/font/fontello.woff2 b/admin/themes/default/fontello/font/fontello.woff2
new file mode 100644
index 000000000..399313792
--- /dev/null
+++ b/admin/themes/default/fontello/font/fontello.woff2
Binary files differ
diff --git a/admin/themes/default/template/permalinks.tpl b/admin/themes/default/template/permalinks.tpl
index 8ec0f3a6d..15857e3ea 100644
--- a/admin/themes/default/template/permalinks.tpl
+++ b/admin/themes/default/template/permalinks.tpl
@@ -20,6 +20,13 @@ form fieldset p.actionButtons {margin-bottom:0}
</style>
{/literal}
+{html_style}
+[name="permalink"] {
+ width : 100%;
+ max-width : 600px;
+}
+{/html_style}
+
<div class="titrePage">
<h2>{'Permalinks'|@translate}</h2>
</div>
@@ -96,4 +103,4 @@ form fieldset p.actionButtons {margin-bottom:0}
</tr>
{/foreach}
</table>
-</fieldset> \ No newline at end of file
+</fieldset>
diff --git a/admin/themes/default/template/picture_modify.tpl b/admin/themes/default/template/picture_modify.tpl
index 71b3d2ba4..4a66cafe4 100644
--- a/admin/themes/default/template/picture_modify.tpl
+++ b/admin/themes/default/template/picture_modify.tpl
@@ -73,6 +73,7 @@ jQuery("a.preview-box").colorbox({
{if isset($U_JUMPTO) }
<li><a class="icon-eye" href="{$U_JUMPTO}">{'jump to photo'|@translate} →</a></li>
{/if}
+ <li><a class="icon-download" href="{$U_DOWNLOAD}">{'Download'|translate}</a></li>
{if !url_is_remote($PATH)}
<li><a class="icon-arrows-cw" href="{$U_SYNC}">{'Synchronize metadata'|@translate}</a></li>
diff --git a/admin/themes/default/template/rating_user.tpl b/admin/themes/default/template/rating_user.tpl
index bc2b29ad6..e1493832a 100644
--- a/admin/themes/default/template/rating_user.tpl
+++ b/admin/themes/default/template/rating_user.tpl
@@ -65,14 +65,14 @@ body .ui-tooltip {
{combine_script id='core.scripts' load='async' path='themes/default/js/scripts.js'}
{combine_script id='jquery.geoip' load='async' path='admin/themes/default/js/jquery.geoip.js'}
{footer_script}
-var oTable = jQuery('#rateTable').dataTable({
- sDom : '<"dtBar"filp>rt<"dtBar"ilp>',
- iDisplayLength: 100,
- aLengthMenu: [ [25, 50, 100, 500, -1], [25, 50, 100, 500, "All"]],
- aaSorting: [], //[[1,'desc']],
- bAutoWidth: false,
- bSortClasses: false,
- aoColumnDefs: [
+jQuery('#rateTable').dataTable({
+ dom : '<"dtBar"filp>rt<"dtBar"ilp>',
+ pageLength: 100,
+ lengthMenu: [ [25, 50, 100, 500, -1], [25, 50, 100, 500, "All"]],
+ sorting: [], //[[1,'desc']],
+ autoWidth: false,
+ sortClasses: false,
+ columnDefs: [
{
aTargets: ["dtc_user"],
sType: "string",
@@ -108,6 +108,7 @@ var oTable = jQuery('#rateTable').dataTable({
]
});
+var oTable = jQuery('#rateTable').DataTable();
function uidFromCell(cell){
var tr = cell;
@@ -135,7 +136,7 @@ $(document).ready( function(){
onFailure: function(num, text) { tr.stop(); tr.fadeTo(0,1); alert(num + " " + text); },
onSuccess: function(result){
if (result)
- oTable.fnDeleteRow(tr[0]);
+ oTable.row(tr[0]).remove().draw();
else
alert(result);
}
diff --git a/admin/themes/default/template/user_list.tpl b/admin/themes/default/template/user_list.tpl
index dedf59cf6..c25ae0df6 100644
--- a/admin/themes/default/template/user_list.tpl
+++ b/admin/themes/default/template/user_list.tpl
@@ -76,7 +76,7 @@ jQuery(document).ready(function() {
jQuery("#addUserForm .loading").show();
},
success:function(data) {
- oTable.fnDraw();
+ oTable.draw();
jQuery("#addUserForm .loading").hide();
var data = jQuery.parseJSON(data);
@@ -172,7 +172,7 @@ jQuery(document).ready(function() {
/* Formating function for row details */
function fnFormatDetails(oTable, nTr) {
- var userId = oTable.fnGetData(nTr)[0];
+ var userId = oTable.api().row(nTr).data()[0];
console.log("userId = "+userId);
var sOut = null;
@@ -567,7 +567,7 @@ jQuery(document).on('click', '.close-user-details', function(e) {
title:"{/literal}{'Edit user'|translate}{literal}",
href:".user_form_popin",
onClosed: function() {
- oTable.fnDraw();
+ oTable.draw();
}
});
@@ -578,10 +578,10 @@ jQuery(document).on('click', '.close-user-details', function(e) {
/* first column must be prefixed with the open/close icon */
var aoColumns = [
{
- 'bVisible':false
+ visible:false
},
{
- "mRender": function(data, type, full) {
+ render: function(data, type, full) {
return '<label><input type="checkbox" data-user_id="'+full[0]+'"> '+data+'</label> <a title="{/literal}{'Open user details'|translate|escape:'javascript'}{literal}" class="icon-pencil openUserDetails">{/literal}{'edit'|translate}{literal}</a>';
}
}
@@ -592,35 +592,36 @@ jQuery(document).on('click', '.close-user-details', function(e) {
}
var oTable = jQuery('#userList').dataTable({
- "iDisplayLength": 10,
- "bDeferRender": true,
- "bProcessing": true,
- "bServerSide": true,
- "sServerMethod": "POST",
- "sAjaxSource": "admin/user_list_backend.php",
- "oLanguage": {
- "sProcessing": "{/literal}{'Loading...'|translate|escape:'javascript'}{literal}",
- "sLengthMenu": sprintf("{/literal}{'Show %s users'|translate|escape:'javascript'}{literal}", '_MENU_'),
- "sZeroRecords": "{/literal}{'No matching user found'|translate|escape:'javascript'}{literal}",
- "sInfo": sprintf("{/literal}{'Showing %s to %s of %s users'|translate|escape:'javascript'}{literal}", '_START_', '_END_', '_TOTAL_'),
- "sInfoEmpty": "{/literal}{'No matching user found'|translate|escape:'javascript'}{literal}",
- "sInfoFiltered": sprintf("{/literal}{'(filtered from %s total users)'|translate|escape:'javascript'}{literal}", '_MAX_'),
- "sSearch": '<span class="icon-search"></span>'+"{/literal}{'Search'|translate|escape:'javascript'}{literal}",
- "sLoadingRecords": "{/literal}{'Loading...'|translate|escape:'javascript'}{literal}",
- "oPaginate": {
- "sFirst": "{/literal}{'First'|translate|escape:'javascript'}{literal}",
- "sPrevious": '← '+"{/literal}{'Previous'|translate|escape:'javascript'}{literal}",
- "sNext": "{/literal}{'Next'|translate|escape:'javascript'}{literal}"+' →',
- "sLast": "{/literal}{'Last'|translate|escape:'javascript'}{literal}",
+ pageLength: 10,
+ deferRender: true,
+ processing: true,
+ serverSide: true,
+ serverMethod: "POST",
+ ajaxSource: "admin/user_list_backend.php",
+ pagingType: "simple",
+ language: {
+ processing: "{/literal}{'Loading...'|translate|escape:'javascript'}{literal}",
+ lengthMenu: sprintf("{/literal}{'Show %s users'|translate|escape:'javascript'}{literal}", '_MENU_'),
+ zeroRecords: "{/literal}{'No matching user found'|translate|escape:'javascript'}{literal}",
+ info: sprintf("{/literal}{'Showing %s to %s of %s users'|translate|escape:'javascript'}{literal}", '_START_', '_END_', '_TOTAL_'),
+ infoEmpty: "{/literal}{'No matching user found'|translate|escape:'javascript'}{literal}",
+ infoFiltered: sprintf("{/literal}{'(filtered from %s total users)'|translate|escape:'javascript'}{literal}", '_MAX_'),
+ search: '<span class="icon-search"></span>'+"{/literal}{'Search'|translate|escape:'javascript'}{literal}",
+ loadingRecords: "{/literal}{'Loading...'|translate|escape:'javascript'}{literal}",
+ paginate: {
+ first: "{/literal}{'First'|translate|escape:'javascript'}{literal}",
+ previous: '← '+"{/literal}{'Previous'|translate|escape:'javascript'}{literal}",
+ next: "{/literal}{'Next'|translate|escape:'javascript'}{literal}"+' →',
+ last: "{/literal}{'Last'|translate|escape:'javascript'}{literal}",
}
},
- "fnDrawCallback": function( oSettings ) {
+ "drawCallback": function( oSettings ) {
jQuery("#userList input[type=checkbox]").each(function() {
var user_id = jQuery(this).data("user_id");
jQuery(this).prop('checked', (selection.indexOf(user_id) != -1));
});
},
- "aoColumns": aoColumns
+ columns: aoColumns
});
/**
@@ -807,7 +808,7 @@ jQuery(document).on('click', '.close-user-details', function(e) {
jQuery("#applyActionLoading").show();
},
success:function(data) {
- oTable.fnDraw();
+ oTable.draw();
jQuery("#applyActionLoading").hide();
jQuery("#applyActionBlock .infos").show();
@@ -840,11 +841,18 @@ jQuery(document).on('click', '.close-user-details', function(e) {
.dataTables_wrapper, .dataTables_info {clear:none;}
table.dataTable {clear:right;padding-top:10px;}
.dataTable td img {margin-bottom: -6px;margin-left: -6px;}
-.paginate_enabled_previous, .paginate_enabled_previous:hover, .paginate_disabled_previous, .paginate_enabled_next, .paginate_enabled_next:hover, .paginate_disabled_next {background:none;}
-.paginate_enabled_previous, .paginate_enabled_next {color:#005E89 !important;}
-.paginate_enabled_previous:hover, .paginate_enabled_next:hover {color:#D54E21 !important; text-decoration:underline !important;}
-.paginate_disabled_next, .paginate_enabled_next {padding-right:3px;}
+.paginate_button, .paginate_button:hover {background:none !important;}
+.dataTables_wrapper .dataTables_paginate .paginate_button {color:#005E89 !important;}
+.dataTables_wrapper .dataTables_paginate .paginate_button:hover {color:#D54E21 !important; text-decoration:underline !important; border-color:transparent;}
+
+.paginate_button.next {padding-right:3px !important;}
+
+table.dataTable tbody th,
+table.dataTable tbody td {
+ padding: 3px 5px;
+}
+
.bulkAction {margin-top:10px;}
#addUserForm p {margin-left:0;}
#applyActionBlock .actionButtons {margin-left:0;}
diff --git a/identification.php b/identification.php
index be22eb571..3e5ab1560 100644
--- a/identification.php
+++ b/identification.php
@@ -50,7 +50,7 @@ if (isset($_POST['login']))
$page['errors'][] = l10n('Cookies are blocked or not supported by your browser. You must enable cookies to connect.');
}
else
- {
+ {
if ($conf['insensitive_case_logon'] == true)
{
$_POST['username'] = search_case_username($_POST['username']);
@@ -58,14 +58,14 @@ if (isset($_POST['login']))
$redirect_to = isset($_POST['redirect']) ? urldecode($_POST['redirect']) : '';
$remember_me = isset($_POST['remember_me']) and $_POST['remember_me']==1;
-
+
if ( try_log_user($_POST['username'], $_POST['password'], $remember_me) )
{
redirect(empty($redirect_to) ? get_gallery_home_url() : $redirect_to);
}
else
{
- $page['errors'][] = l10n('Invalid password!');
+ $page['errors'][] = l10n('Invalid username or password!');
}
}
}
diff --git a/include/config_default.inc.php b/include/config_default.inc.php
index 87baa29ab..f66dab486 100644
--- a/include/config_default.inc.php
+++ b/include/config_default.inc.php
@@ -121,7 +121,7 @@ $conf['paginate_pages_around'] = 2;
// show_version : shall the version of Piwigo be displayed at the
// bottom of each page ?
-$conf['show_version'] = false;
+$conf['show_version'] = true;
// meta_ref to reference multiple sets of incorporated pages or elements
// Set it false to avoid referencing in google, and other search engines.
@@ -238,7 +238,7 @@ $conf['available_permission_levels'] = array(0,1,2,4,8);
//
// This configuration parameter is set to true in BSF branch and to false
// elsewhere.
-$conf['check_upgrade_feed'] = false;
+$conf['check_upgrade_feed'] = true;
// rate_items: available rates for a picture
$conf['rate_items'] = array(0,1,2,3,4,5);
@@ -430,7 +430,7 @@ $conf['session_use_ip_address'] = true;
$conf['show_queries'] = false;
// show_gt : display generation time at the bottom of each page
-$conf['show_gt'] = false;
+$conf['show_gt'] = true;
// debug_l10n : display a warning message each time an unset language key is
// accessed
@@ -443,7 +443,7 @@ $conf['debug_template'] = false;
$conf['debug_mail'] = false;
// die_on_sql_error: if an SQL query fails, should everything stop?
-$conf['die_on_sql_error'] = false;
+$conf['die_on_sql_error'] = true;
// if true, some language strings are replaced during template compilation
// (instead of template output). this results in better performance. however
@@ -828,7 +828,7 @@ $conf['log_dir'] = '/logs';
// Log level (OFF, CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG)
// development = DEBUG, production = ERROR
-$conf['log_level'] = 'ERROR';
+$conf['log_level'] = 'DEBUG';
// Keep logs file during X days
$conf['log_archive_days'] = 30;
diff --git a/include/constants.php b/include/constants.php
index 153c20d34..64b783da6 100644
--- a/include/constants.php
+++ b/include/constants.php
@@ -22,7 +22,7 @@
// +-----------------------------------------------------------------------+
// Default settings
-define('PHPWG_VERSION', '2.8.3');
+define('PHPWG_VERSION', '2.9.0beta1');
define('PHPWG_DEFAULT_LANGUAGE', 'en_UK');
define('PHPWG_DEFAULT_TEMPLATE', 'elegant');
diff --git a/include/functions.inc.php b/include/functions.inc.php
index 018747817..a7135a4f4 100644
--- a/include/functions.inc.php
+++ b/include/functions.inc.php
@@ -411,6 +411,24 @@ function pwg_log($image_id = null, $image_type = null, $format_id = null)
{
global $conf, $user, $page;
+ $update_last_visit = false;
+ if (empty($user['last_visit']) or strtotime($user['last_visit']) < time()-$conf['session_length'])
+ {
+ $update_last_visit = true;
+ }
+ $update_last_visit = trigger_change('pwg_log_update_last_visit', $update_last_visit);
+
+ if ($update_last_visit)
+ {
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET last_visit = NOW(),
+ lastmodified = lastmodified
+ WHERE user_id = '.$user['id'].'
+';
+ pwg_query($query);
+ }
+
$do_log = $conf['log'];
if (is_admin())
{
diff --git a/include/functions_user.inc.php b/include/functions_user.inc.php
index de8f87c7f..6a910c511 100644
--- a/include/functions_user.inc.php
+++ b/include/functions_user.inc.php
@@ -1614,4 +1614,46 @@ UPDATE '.USER_AUTH_KEYS_TABLE.'
;';
pwg_query($query);
}
+
+/**
+ * Gets the last visit (datetime) of a user, based on history table
+ *
+ * @since 2.9
+ * @param int $user_id
+ * @param boolean $save_in_user_infos to store result in user_infos.last_visit
+ * @return string date & time of last visit
+ */
+function get_user_last_visit_from_history($user_id, $save_in_user_infos=false)
+{
+ $last_visit = null;
+
+ $query = '
+SELECT
+ date,
+ time
+FROM '.HISTORY_TABLE.'
+ WHERE user_id = '.$user_id.'
+ ORDER BY id DESC
+ LIMIT 1
+;';
+ $result = pwg_query($query);
+ while ($row = pwg_db_fetch_assoc($result))
+ {
+ $last_visit = $row['date'].' '.$row['time'];
+ }
+
+ if ($save_in_user_infos)
+ {
+ $query = '
+UPDATE '.USER_INFOS_TABLE.'
+ SET last_visit = '.(is_null($last_visit) ? 'NULL' : "'".$last_visit."'").',
+ last_visit_from_history = \'true\',
+ lastmodified = lastmodified
+ WHERE user_id = '.$user_id.'
+';
+ pwg_query($query);
+ }
+
+ return $last_visit;
+}
?>
diff --git a/include/ws_functions/pwg.users.php b/include/ws_functions/pwg.users.php
index eaa96c9c1..1a55fcf90 100644
--- a/include/ws_functions/pwg.users.php
+++ b/include/ws_functions/pwg.users.php
@@ -123,7 +123,8 @@ function ws_users_getList($params, &$service)
$ui_fields = array(
'status','level','language','theme','nb_image_page','recent_period','expand',
- 'show_nb_comments','show_nb_hits','enabled_high','registration_date'
+ 'show_nb_comments','show_nb_hits','enabled_high','registration_date',
+ 'last_visit'
);
foreach ($ui_fields as $field)
{
@@ -154,6 +155,12 @@ SELECT DISTINCT ';
$query.= '"" AS groups';
}
+ if (isset($display['ui.last_visit']))
+ {
+ if (!$first) $query.= ', ';
+ $query.= 'ui.last_visit_from_history AS last_visit_from_history';
+ }
+
$query.= '
FROM '. USERS_TABLE .' AS u
INNER JOIN '. USER_INFOS_TABLE .' AS ui
@@ -210,42 +217,25 @@ SELECT user_id, group_id
if (isset($params['display']['last_visit']))
{
- $query = '
-SELECT
- MAX(id) as history_id
- FROM '.HISTORY_TABLE.'
- WHERE user_id IN ('.implode(',', array_keys($users)).')
- GROUP BY user_id
-;';
- $history_ids = array_from_query($query, 'history_id');
-
- if (count($history_ids) == 0)
- {
- $history_ids[] = -1;
- }
-
- $query = '
-SELECT
- user_id,
- date,
- time
- FROM '.HISTORY_TABLE.'
- WHERE id IN ('.implode(',', $history_ids).')
-;';
- $result = pwg_query($query);
- while ($row = pwg_db_fetch_assoc($result))
+ foreach ($users as $cur_user)
{
- $last_visit = $row['date'].' '.$row['time'];
- $users[ $row['user_id'] ]['last_visit'] = $last_visit;
-
+ $last_visit = $cur_user['last_visit'];
+ $users[ $cur_user['id'] ]['last_visit'] = $last_visit;
+
+ if (!get_boolean($cur_user['last_visit_from_history']) and empty($last_visit))
+ {
+ $last_visit = get_user_last_visit_from_history($cur_user['id'], true);
+ $users[ $cur_user['id'] ]['last_visit'] = $last_visit;
+ }
+
if (isset($params['display']['last_visit_string']))
{
- $users[ $row['user_id'] ]['last_visit_string'] = format_date($last_visit, array('day', 'month', 'year'));
+ $users[ $cur_user['id'] ]['last_visit_string'] = format_date($last_visit, array('day', 'month', 'year'));
}
if (isset($params['display']['last_visit_since']))
{
- $users[ $row['user_id'] ]['last_visit_since'] = time_since($last_visit, 'day');
+ $users[ $cur_user['id'] ]['last_visit_since'] = time_since($last_visit, 'day');
}
}
}
@@ -632,4 +622,4 @@ SELECT
));
}
-?> \ No newline at end of file
+?>
diff --git a/install/config.sql b/install/config.sql
index d247b93c0..b1f8e17fe 100644
--- a/install/config.sql
+++ b/install/config.sql
@@ -15,7 +15,7 @@ INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment_
INSERT INTO piwigo_config (param,value,comment) VALUES ('email_admin_on_comment_deletion','false','Send an email to the administrators when a comment is deleted');
INSERT INTO piwigo_config (param,value,comment) VALUES ('gallery_locked','false','Lock your gallery temporary for non admin users');
INSERT INTO piwigo_config (param,value,comment) VALUES ('gallery_title','','Title at top of each page and for RSS feed');
-INSERT INTO piwigo_config (param,value,comment) VALUES ('rate','true','Rating pictures feature is enabled');
+INSERT INTO piwigo_config (param,value,comment) VALUES ('rate','false','Rating pictures feature is enabled');
INSERT INTO piwigo_config (param,value,comment) VALUES ('rate_anonymous','true','Rating pictures feature is also enabled for visitors');
INSERT INTO piwigo_config (param,value,comment) VALUES ('page_banner','','html displayed on the top each page of your gallery');
INSERT INTO piwigo_config (param,value,comment) VALUES ('history_admin','false','keep a history of administrator visits on your website');
diff --git a/install/db/149-database.php b/install/db/149-database.php
new file mode 100644
index 000000000..a42af507a
--- /dev/null
+++ b/install/db/149-database.php
@@ -0,0 +1,40 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Piwigo - a PHP based photo gallery |
+// +-----------------------------------------------------------------------+
+// | Copyright(C) 2008-2016 Piwigo Team http://piwigo.org |
+// | Copyright(C) 2003-2008 PhpWebGallery Team http://phpwebgallery.net |
+// | Copyright(C) 2002-2003 Pierrick LE GALL http://le-gall.net/pierrick |
+// +-----------------------------------------------------------------------+
+// | This program is free software; you can redistribute it and/or modify |
+// | it under the terms of the GNU General Public License as published by |
+// | the Free Software Foundation |
+// | |
+// | This program is distributed in the hope that it will be useful, but |
+// | WITHOUT ANY WARRANTY; without even the implied warranty of |
+// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+// | General Public License for more details. |
+// | |
+// | You should have received a copy of the GNU General Public License |
+// | along with this program; if not, write to the Free Software |
+// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
+// | USA. |
+// +-----------------------------------------------------------------------+
+
+if (!defined('PHPWG_ROOT_PATH'))
+{
+ die('Hacking attempt!');
+}
+
+$upgrade_description = 'add last_visit+last_visit_from_history in user_infos table';
+
+// we use PREFIX_TABLE, in case Piwigo uses an external user table
+pwg_query('
+ALTER TABLE `'.PREFIX_TABLE.'user_infos`
+ ADD COLUMN `last_visit` datetime default NULL,
+ ADD COLUMN `last_visit_from_history` enum(\'true\',\'false\') NOT NULL default \'false\'
+;');
+
+echo "\n".$upgrade_description."\n";
+
+?>
diff --git a/install/piwigo_structure-mysql.sql b/install/piwigo_structure-mysql.sql
index 1c7853edc..76e9fe90b 100644
--- a/install/piwigo_structure-mysql.sql
+++ b/install/piwigo_structure-mysql.sql
@@ -460,6 +460,8 @@ CREATE TABLE `piwigo_user_infos` (
`level` tinyint unsigned NOT NULL default '0',
`activation_key` varchar(255) default NULL,
`activation_key_expire` datetime default NULL,
+ `last_visit` datetime default NULL,
+ `last_visit_from_history` enum('true','false') NOT NULL default 'false',
`lastmodified` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
KEY `lastmodified` (`lastmodified`)
diff --git a/language/en_UK/common.lang.php b/language/en_UK/common.lang.php
index 51ce58551..282a289b1 100644
--- a/language/en_UK/common.lang.php
+++ b/language/en_UK/common.lang.php
@@ -426,4 +426,5 @@ $lang['Album name, A &rarr; Z'] = 'Album name, A &rarr; Z';
$lang['Album name, Z &rarr; A'] = 'Album name, Z &rarr; A';
$lang['Link: %s'] = 'Link: %s';
$lang['Your authentication key is no longer valid.'] = 'Your authentication key is no longer valid.';
-?> \ No newline at end of file
+$lang['Invalid username or password!'] = 'Invalid username or password!';
+?>
diff --git a/language/en_UK/upgrade.lang.php b/language/en_UK/upgrade.lang.php
index f279fe618..221336482 100644
--- a/language/en_UK/upgrade.lang.php
+++ b/language/en_UK/upgrade.lang.php
@@ -40,4 +40,5 @@ $lang['User permissions and group permissions have been erased'] = "Users and gr
$lang['Only thumbnails prefix and webmaster mail address have been saved from previous configuration'] = "Only thumbnails prefix and webmaster mail address have been saved from previous configuration";
$lang['In <i>%s</i>, before <b>?></b>, insert:'] = 'In <i>%s</i>, before <b>?></b>, insert:';
$lang['As a precaution, following themes have been deactivated. You must check for themes upgrade before reactiving them:'] = 'As a precaution, the following themes have been deactivated. You must check for upgradable themes before reactivating them:';
-?> \ No newline at end of file
+$lang['Discover what\'s new in Piwigo %s'] = 'Discover what\'s new in Piwigo %s';
+?>
diff --git a/language/fr_FR/common.lang.php b/language/fr_FR/common.lang.php
index c70dbf183..bf890246e 100644
--- a/language/fr_FR/common.lang.php
+++ b/language/fr_FR/common.lang.php
@@ -268,7 +268,7 @@ $lang['Quick connect'] = 'Connexion rapide';
$lang['Quick search'] = 'Recherche rapide';
$lang['Random photos'] = 'Photos au hasard';
$lang['Rank'] = 'Rang';
-$lang['Rate this photo'] = 'Votez pour cette photo';
+$lang['Rate this photo'] = 'Notez cette photo';
$lang['Rating score'] = 'Score';
$lang['Recent albums'] = 'Albums récents';
$lang['Recent period must be a positive integer value'] = 'La période de nouveautés doit être un entier positif';
@@ -425,4 +425,5 @@ $lang['Photo description'] = 'Description de la photo';
$lang['Album name, A &rarr; Z'] = 'Nom de l\'album, A &rarr; Z';
$lang['Album name, Z &rarr; A'] = 'Nom de l\'album, Z &rarr; A';
$lang['Link: %s'] = 'Lien: %s';
-$lang['Your authentication key is no longer valid.'] = 'Votre clef d\'identification n\'est plus valide.'; \ No newline at end of file
+$lang['Your authentication key is no longer valid.'] = 'Votre clef d\'identification n\'est plus valide.';
+$lang['Invalid username or password!'] = 'Nom d\'utilisateur ou mot de passe invalide !';
diff --git a/language/fr_FR/upgrade.lang.php b/language/fr_FR/upgrade.lang.php
index 6c2d6b556..f831a65dc 100644
--- a/language/fr_FR/upgrade.lang.php
+++ b/language/fr_FR/upgrade.lang.php
@@ -41,4 +41,5 @@ $lang['User permissions and group permissions have been erased'] = "Les permissi
$lang['Only thumbnails prefix and webmaster mail address have been saved from previous configuration'] = "Seuls le préfixe des miniatures et l'adresse email du webmestre ont étés conservés de la précédente configuration";
$lang['In <i>%s</i>, before <b>?></b>, insert:'] = 'Dans le fichier <i>%s</i>, avant <b>?></b>, insérez:';
$lang['As a precaution, following themes have been deactivated. You must check for themes upgrade before reactiving them:'] = 'Par précaution, les thèmes suivants ont été désactivés. Vérifiez s\'il existe des mises à jour avant de les réactiver:';
-?> \ No newline at end of file
+$lang['Discover what\'s new in Piwigo %s'] = 'Quoi de neuf dans cette version %s ?';
+?>
diff --git a/themes/default/js/plugins/datatables/css/demo_page.css b/themes/default/js/plugins/datatables/css/demo_page.css
deleted file mode 100644
index ba5b2a6c3..000000000
--- a/themes/default/js/plugins/datatables/css/demo_page.css
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * General page setup
- */
-#dt_example {
- font: 80%/1.45em "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
- margin: 0;
- padding: 0;
- color: #333;
- background-color: #fff;
-}
-
-
-#dt_example #container {
- width: 800px;
- margin: 30px auto;
- padding: 0;
-}
-
-
-#dt_example #footer {
- margin: 50px auto 0 auto;
- padding: 0;
-}
-
-#dt_example #demo {
- margin: 30px auto 0 auto;
-}
-
-#dt_example .demo_jui {
- margin: 30px auto 0 auto;
-}
-
-#dt_example .big {
- font-size: 1.3em;
- font-weight: bold;
- line-height: 1.6em;
- color: #4E6CA3;
-}
-
-#dt_example .spacer {
- height: 20px;
- clear: both;
-}
-
-#dt_example .clear {
- clear: both;
-}
-
-#dt_example pre {
- padding: 15px;
- background-color: #F5F5F5;
- border: 1px solid #CCCCCC;
-}
-
-#dt_example h1 {
- margin-top: 2em;
- font-size: 1.3em;
- font-weight: normal;
- line-height: 1.6em;
- color: #4E6CA3;
- border-bottom: 1px solid #B0BED9;
- clear: both;
-}
-
-#dt_example h2 {
- font-size: 1.2em;
- font-weight: normal;
- line-height: 1.6em;
- color: #4E6CA3;
- clear: both;
-}
-
-#dt_example a {
- color: #0063DC;
- text-decoration: none;
-}
-
-#dt_example a:hover {
- text-decoration: underline;
-}
-
-#dt_example ul {
- color: #4E6CA3;
-}
-
-.css_right {
- float: right;
-}
-
-.css_left {
- float: left;
-}
-
-.demo_links {
- float: left;
- width: 50%;
- margin-bottom: 1em;
-}
-
-#demo_info {
- padding: 5px;
- border: 1px solid #B0BED9;
- height: 100px;
- width: 100%;
- overflow: auto;
-}
-
-#dt_example code {
- font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
- padding: 2px 4px !important;
- white-space: nowrap;
- font-size: 0.9em;
-
- color: #D14;
- background-color: #F7F7F9;
-
- border: 1px solid #E1E1E8;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- border-radius: 3px;
-}
diff --git a/themes/default/js/plugins/datatables/css/demo_table.css b/themes/default/js/plugins/datatables/css/demo_table.css
deleted file mode 100644
index 12f352dac..000000000
--- a/themes/default/js/plugins/datatables/css/demo_table.css
+++ /dev/null
@@ -1,577 +0,0 @@
-/*
- * File: demo_table.css
- * CVS: $Id$
- * Description: CSS descriptions for DataTables demo pages
- * Author: Allan Jardine
- * Created: Tue May 12 06:47:22 BST 2009
- * Modified: $Date$ by $Author$
- * Language: CSS
- * Project: DataTables
- *
- * Copyright 2009 Allan Jardine. All Rights Reserved.
- *
- * ***************************************************************************
- * DESCRIPTION
- *
- * The styles given here are suitable for the demos that are used with the standard DataTables
- * distribution (see www.datatables.net). You will most likely wish to modify these styles to
- * meet the layout requirements of your site.
- *
- * Common issues:
- * 'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
- * no conflict between the two pagination types. If you want to use full_numbers pagination
- * ensure that you either have "example_alt_pagination" as a body class name, or better yet,
- * modify that selector.
- * Note that the path used for Images is relative. All images are by default located in
- * ../images/ - relative to this CSS file.
- */
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables features
- */
-
-.dataTables_wrapper {
- position: relative;
- clear: both;
- zoom: 1; /* Feeling sorry for IE */
-}
-
-.dataTables_processing {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 250px;
- height: 30px;
- margin-left: -125px;
- margin-top: -15px;
- padding: 14px 0 2px 0;
- border: 1px solid #ddd;
- text-align: center;
- color: #999;
- font-size: 14px;
- background-color: white;
-}
-
-.dataTables_length {
- width: 40%;
- float: left;
-}
-
-.dataTables_filter {
- width: 50%;
- float: right;
- text-align: right;
-}
-
-.dataTables_info {
- width: 60%;
- float: left;
-}
-
-.dataTables_paginate {
- float: right;
- text-align: right;
-}
-
-/* Pagination nested */
-.paginate_disabled_previous, .paginate_enabled_previous,
-.paginate_disabled_next, .paginate_enabled_next {
- height: 19px;
- float: left;
- cursor: pointer;
- *cursor: hand;
- color: #111 !important;
-}
-.paginate_disabled_previous:hover, .paginate_enabled_previous:hover,
-.paginate_disabled_next:hover, .paginate_enabled_next:hover {
- text-decoration: none !important;
-}
-.paginate_disabled_previous:active, .paginate_enabled_previous:active,
-.paginate_disabled_next:active, .paginate_enabled_next:active {
- outline: none;
-}
-
-.paginate_disabled_previous,
-.paginate_disabled_next {
- color: #666 !important;
-}
-.paginate_disabled_previous, .paginate_enabled_previous {
- padding-left: 23px;
-}
-.paginate_disabled_next, .paginate_enabled_next {
- padding-right: 23px;
- margin-left: 10px;
-}
-
-.paginate_disabled_previous {
- background: url('../images/back_disabled.png') no-repeat top left;
-}
-
-.paginate_enabled_previous {
- background: url('../images/back_enabled.png') no-repeat top left;
-}
-.paginate_enabled_previous:hover {
- background: url('../images/back_enabled_hover.png') no-repeat top left;
-}
-
-.paginate_disabled_next {
- background: url('../images/forward_disabled.png') no-repeat top right;
-}
-
-.paginate_enabled_next {
- background: url('../images/forward_enabled.png') no-repeat top right;
-}
-.paginate_enabled_next:hover {
- background: url('../images/forward_enabled_hover.png') no-repeat top right;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables display
- */
-table.display {
- margin: 0 auto;
- clear: both;
- width: 100%;
-
- /* Note Firefox 3.5 and before have a bug with border-collapse
- * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 )
- * border-spacing: 0; is one possible option. Conditional-css.com is
- * useful for this kind of thing
- *
- * Further note IE 6/7 has problems when calculating widths with border width.
- * It subtracts one px relative to the other browsers from the first column, and
- * adds one to the end...
- *
- * If you want that effect I'd suggest setting a border-top/left on th/td's and
- * then filling in the gaps with other borders.
- */
-}
-
-table.display thead th {
- padding: 3px 18px 3px 10px;
- border-bottom: 1px solid black;
- font-weight: bold;
- cursor: pointer;
- * cursor: hand;
-}
-
-table.display tfoot th {
- padding: 3px 18px 3px 10px;
- border-top: 1px solid black;
- font-weight: bold;
-}
-
-table.display tr.heading2 td {
- border-bottom: 1px solid #aaa;
-}
-
-table.display td {
- padding: 3px 10px;
-}
-
-table.display td.center {
- text-align: center;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables sorting
- */
-
-.sorting_asc {
- background: url('../images/sort_asc.png') no-repeat center right;
-}
-
-.sorting_desc {
- background: url('../images/sort_desc.png') no-repeat center right;
-}
-
-.sorting {
- background: url('../images/sort_both.png') no-repeat center right;
-}
-
-.sorting_asc_disabled {
- background: url('../images/sort_asc_disabled.png') no-repeat center right;
-}
-
-.sorting_desc_disabled {
- background: url('../images/sort_desc_disabled.png') no-repeat center right;
-}
-
-table.display thead th:active,
-table.display thead td:active {
- outline: none;
-}
-
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables row classes
- */
-table.display tr.odd.gradeA {
- background-color: #ddffdd;
-}
-
-table.display tr.even.gradeA {
- background-color: #eeffee;
-}
-
-table.display tr.odd.gradeC {
- background-color: #ddddff;
-}
-
-table.display tr.even.gradeC {
- background-color: #eeeeff;
-}
-
-table.display tr.odd.gradeX {
- background-color: #ffdddd;
-}
-
-table.display tr.even.gradeX {
- background-color: #ffeeee;
-}
-
-table.display tr.odd.gradeU {
- background-color: #ddd;
-}
-
-table.display tr.even.gradeU {
- background-color: #eee;
-}
-
-
-tr.odd {
- background-color: #E2E4FF;
-}
-
-tr.even {
- background-color: white;
-}
-
-
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Misc
- */
-.dataTables_scroll {
- clear: both;
-}
-
-.dataTables_scrollBody {
- *margin-top: -1px;
- -webkit-overflow-scrolling: touch;
-}
-
-.top, .bottom {
- padding: 15px;
- background-color: #F5F5F5;
- border: 1px solid #CCCCCC;
-}
-
-.top .dataTables_info {
- float: none;
-}
-
-.clear {
- clear: both;
-}
-
-.dataTables_empty {
- text-align: center;
-}
-
-tfoot input {
- margin: 0.5em 0;
- width: 100%;
- color: #444;
-}
-
-tfoot input.search_init {
- color: #999;
-}
-
-td.group {
- background-color: #d1cfd0;
- border-bottom: 2px solid #A19B9E;
- border-top: 2px solid #A19B9E;
-}
-
-td.details {
- background-color: #d1cfd0;
- border: 2px solid #A19B9E;
-}
-
-
-.example_alt_pagination div.dataTables_info {
- width: 40%;
-}
-
-.paging_full_numbers {
- width: 400px;
- height: 22px;
- line-height: 22px;
-}
-
-.paging_full_numbers a:active {
- outline: none
-}
-
-.paging_full_numbers a:hover {
- text-decoration: none;
-}
-
-.paging_full_numbers a.paginate_button,
- .paging_full_numbers a.paginate_active {
- border: 1px solid #aaa;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- padding: 2px 5px;
- margin: 0 3px;
- cursor: pointer;
- *cursor: hand;
- color: #333 !important;
-}
-
-.paging_full_numbers a.paginate_button {
- background-color: #ddd;
-}
-
-.paging_full_numbers a.paginate_button:hover {
- background-color: #ccc;
- text-decoration: none !important;
-}
-
-.paging_full_numbers a.paginate_active {
- background-color: #99B3FF;
-}
-
-table.display tr.even.row_selected td {
- background-color: #B0BED9;
-}
-
-table.display tr.odd.row_selected td {
- background-color: #9FAFD1;
-}
-
-
-/*
- * Sorting classes for columns
- */
-/* For the standard odd/even */
-tr.odd td.sorting_1 {
- background-color: #D3D6FF;
-}
-
-tr.odd td.sorting_2 {
- background-color: #DADCFF;
-}
-
-tr.odd td.sorting_3 {
- background-color: #E0E2FF;
-}
-
-tr.even td.sorting_1 {
- background-color: #EAEBFF;
-}
-
-tr.even td.sorting_2 {
- background-color: #F2F3FF;
-}
-
-tr.even td.sorting_3 {
- background-color: #F9F9FF;
-}
-
-
-/* For the Conditional-CSS grading rows */
-/*
- Colour calculations (based off the main row colours)
- Level 1:
- dd > c4
- ee > d5
- Level 2:
- dd > d1
- ee > e2
- */
-tr.odd.gradeA td.sorting_1 {
- background-color: #c4ffc4;
-}
-
-tr.odd.gradeA td.sorting_2 {
- background-color: #d1ffd1;
-}
-
-tr.odd.gradeA td.sorting_3 {
- background-color: #d1ffd1;
-}
-
-tr.even.gradeA td.sorting_1 {
- background-color: #d5ffd5;
-}
-
-tr.even.gradeA td.sorting_2 {
- background-color: #e2ffe2;
-}
-
-tr.even.gradeA td.sorting_3 {
- background-color: #e2ffe2;
-}
-
-tr.odd.gradeC td.sorting_1 {
- background-color: #c4c4ff;
-}
-
-tr.odd.gradeC td.sorting_2 {
- background-color: #d1d1ff;
-}
-
-tr.odd.gradeC td.sorting_3 {
- background-color: #d1d1ff;
-}
-
-tr.even.gradeC td.sorting_1 {
- background-color: #d5d5ff;
-}
-
-tr.even.gradeC td.sorting_2 {
- background-color: #e2e2ff;
-}
-
-tr.even.gradeC td.sorting_3 {
- background-color: #e2e2ff;
-}
-
-tr.odd.gradeX td.sorting_1 {
- background-color: #ffc4c4;
-}
-
-tr.odd.gradeX td.sorting_2 {
- background-color: #ffd1d1;
-}
-
-tr.odd.gradeX td.sorting_3 {
- background-color: #ffd1d1;
-}
-
-tr.even.gradeX td.sorting_1 {
- background-color: #ffd5d5;
-}
-
-tr.even.gradeX td.sorting_2 {
- background-color: #ffe2e2;
-}
-
-tr.even.gradeX td.sorting_3 {
- background-color: #ffe2e2;
-}
-
-tr.odd.gradeU td.sorting_1 {
- background-color: #c4c4c4;
-}
-
-tr.odd.gradeU td.sorting_2 {
- background-color: #d1d1d1;
-}
-
-tr.odd.gradeU td.sorting_3 {
- background-color: #d1d1d1;
-}
-
-tr.even.gradeU td.sorting_1 {
- background-color: #d5d5d5;
-}
-
-tr.even.gradeU td.sorting_2 {
- background-color: #e2e2e2;
-}
-
-tr.even.gradeU td.sorting_3 {
- background-color: #e2e2e2;
-}
-
-
-/*
- * Row highlighting example
- */
-.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
- background-color: #ECFFB3;
-}
-
-.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
- background-color: #E6FF99;
-}
-
-.ex_highlight_row #example tr.even:hover {
- background-color: #ECFFB3;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_1 {
- background-color: #DDFF75;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_2 {
- background-color: #E7FF9E;
-}
-
-.ex_highlight_row #example tr.even:hover td.sorting_3 {
- background-color: #E2FF89;
-}
-
-.ex_highlight_row #example tr.odd:hover {
- background-color: #E6FF99;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_1 {
- background-color: #D6FF5C;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_2 {
- background-color: #E0FF84;
-}
-
-.ex_highlight_row #example tr.odd:hover td.sorting_3 {
- background-color: #DBFF70;
-}
-
-
-/*
- * KeyTable
- */
-table.KeyTable td {
- border: 3px solid transparent;
-}
-
-table.KeyTable td.focus {
- border: 3px solid #3366FF;
-}
-
-table.display tr.gradeA {
- background-color: #eeffee;
-}
-
-table.display tr.gradeC {
- background-color: #ddddff;
-}
-
-table.display tr.gradeX {
- background-color: #ffdddd;
-}
-
-table.display tr.gradeU {
- background-color: #ddd;
-}
-
-div.box {
- height: 100px;
- padding: 10px;
- overflow: auto;
- border: 1px solid #8080FF;
- background-color: #E5E5FF;
-}
diff --git a/themes/default/js/plugins/datatables/css/demo_table_jui.css b/themes/default/js/plugins/datatables/css/demo_table_jui.css
deleted file mode 100644
index a210af51a..000000000
--- a/themes/default/js/plugins/datatables/css/demo_table_jui.css
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * File: demo_table_jui.css
- * CVS: $Id$
- * Description: CSS descriptions for DataTables demo pages
- * Author: Allan Jardine
- * Created: Tue May 12 06:47:22 BST 2009
- * Modified: $Date$ by $Author$
- * Language: CSS
- * Project: DataTables
- *
- * Copyright 2009 Allan Jardine. All Rights Reserved.
- *
- * ***************************************************************************
- * DESCRIPTION
- *
- * The styles given here are suitable for the demos that are used with the standard DataTables
- * distribution (see www.datatables.net). You will most likely wish to modify these styles to
- * meet the layout requirements of your site.
- *
- * Common issues:
- * 'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
- * no conflict between the two pagination types. If you want to use full_numbers pagination
- * ensure that you either have "example_alt_pagination" as a body class name, or better yet,
- * modify that selector.
- * Note that the path used for Images is relative. All images are by default located in
- * ../images/ - relative to this CSS file.
- */
-
-
-/*
- * jQuery UI specific styling
- */
-
-.paging_two_button .ui-button {
- float: left;
- cursor: pointer;
- * cursor: hand;
-}
-
-.paging_full_numbers .ui-button {
- padding: 2px 6px;
- margin: 0;
- cursor: pointer;
- * cursor: hand;
- color: #333 !important;
-}
-
-.dataTables_paginate .ui-button {
- margin-right: -0.1em !important;
-}
-
-.paging_full_numbers {
- width: 350px !important;
-}
-
-.dataTables_wrapper .ui-toolbar {
- padding: 5px;
-}
-
-.dataTables_paginate {
- width: auto;
-}
-
-.dataTables_info {
- padding-top: 3px;
-}
-
-table.display thead th {
- padding: 3px 0px 3px 10px;
- cursor: pointer;
- * cursor: hand;
-}
-
-div.dataTables_wrapper .ui-widget-header {
- font-weight: normal;
-}
-
-
-/*
- * Sort arrow icon positioning
- */
-table.display thead th div.DataTables_sort_wrapper {
- position: relative;
- padding-right: 20px;
-}
-
-table.display thead th div.DataTables_sort_wrapper span {
- position: absolute;
- top: 50%;
- margin-top: -8px;
- right: 0;
-}
-
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * Everything below this line is the same as demo_table.css. This file is
- * required for 'cleanliness' of the markup
- *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables features
- */
-
-.dataTables_wrapper {
- position: relative;
- clear: both;
-}
-
-.dataTables_processing {
- position: absolute;
- top: 0px;
- left: 50%;
- width: 250px;
- margin-left: -125px;
- border: 1px solid #ddd;
- text-align: center;
- color: #999;
- font-size: 11px;
- padding: 2px 0;
-}
-
-.dataTables_length {
- width: 40%;
- float: left;
-}
-
-.dataTables_filter {
- width: 50%;
- float: right;
- text-align: right;
-}
-
-.dataTables_info {
- width: 50%;
- float: left;
-}
-
-.dataTables_paginate {
- float: right;
- text-align: right;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables display
- */
-table.display {
- margin: 0 auto;
- width: 100%;
- clear: both;
- border-collapse: collapse;
-}
-
-table.display tfoot th {
- padding: 3px 0px 3px 10px;
- font-weight: bold;
- font-weight: normal;
-}
-
-table.display tr.heading2 td {
- border-bottom: 1px solid #aaa;
-}
-
-table.display td {
- padding: 3px 10px;
-}
-
-table.display td.center {
- text-align: center;
-}
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables sorting
- */
-
-.sorting_asc {
- background: url('../images/sort_asc.png') no-repeat center right;
-}
-
-.sorting_desc {
- background: url('../images/sort_desc.png') no-repeat center right;
-}
-
-.sorting {
- background: url('../images/sort_both.png') no-repeat center right;
-}
-
-.sorting_asc_disabled {
- background: url('../images/sort_asc_disabled.png') no-repeat center right;
-}
-
-.sorting_desc_disabled {
- background: url('../images/sort_desc_disabled.png') no-repeat center right;
-}
-
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables row classes
- */
-table.display tr.odd.gradeA {
- background-color: #ddffdd;
-}
-
-table.display tr.even.gradeA {
- background-color: #eeffee;
-}
-
-
-
-
-table.display tr.odd.gradeA {
- background-color: #ddffdd;
-}
-
-table.display tr.even.gradeA {
- background-color: #eeffee;
-}
-
-table.display tr.odd.gradeC {
- background-color: #ddddff;
-}
-
-table.display tr.even.gradeC {
- background-color: #eeeeff;
-}
-
-table.display tr.odd.gradeX {
- background-color: #ffdddd;
-}
-
-table.display tr.even.gradeX {
- background-color: #ffeeee;
-}
-
-table.display tr.odd.gradeU {
- background-color: #ddd;
-}
-
-table.display tr.even.gradeU {
- background-color: #eee;
-}
-
-
-tr.odd {
- background-color: #E2E4FF;
-}
-
-tr.even {
- background-color: white;
-}
-
-
-
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Misc
- */
-.dataTables_scroll {
- clear: both;
-}
-
-.dataTables_scrollBody {
- -webkit-overflow-scrolling: touch;
-}
-
-.top, .bottom {
- padding: 15px;
- background-color: #F5F5F5;
- border: 1px solid #CCCCCC;
-}
-
-.top .dataTables_info {
- float: none;
-}
-
-.clear {
- clear: both;
-}
-
-.dataTables_empty {
- text-align: center;
-}
-
-tfoot input {
- margin: 0.5em 0;
- width: 100%;
- color: #444;
-}
-
-tfoot input.search_init {
- color: #999;
-}
-
-td.group {
- background-color: #d1cfd0;
- border-bottom: 2px solid #A19B9E;
- border-top: 2px solid #A19B9E;
-}
-
-td.details {
- background-color: #d1cfd0;
- border: 2px solid #A19B9E;
-}
-
-
-.example_alt_pagination div.dataTables_info {
- width: 40%;
-}
-
-.paging_full_numbers a.paginate_button,
- .paging_full_numbers a.paginate_active {
- border: 1px solid #aaa;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- padding: 2px 5px;
- margin: 0 3px;
- cursor: pointer;
- *cursor: hand;
- color: #333 !important;
-}
-
-.paging_full_numbers a.paginate_button {
- background-color: #ddd;
-}
-
-.paging_full_numbers a.paginate_button:hover {
- background-color: #ccc;
- text-decoration: none !important;
-}
-
-.paging_full_numbers a.paginate_active {
- background-color: #99B3FF;
-}
-
-table.display tr.even.row_selected td {
- background-color: #B0BED9;
-}
-
-table.display tr.odd.row_selected td {
- background-color: #9FAFD1;
-}
-
-
-/*
- * Sorting classes for columns
- */
-/* For the standard odd/even */
-tr.odd td.sorting_1 {
- background-color: #D3D6FF;
-}
-
-tr.odd td.sorting_2 {
- background-color: #DADCFF;
-}
-
-tr.odd td.sorting_3 {
- background-color: #E0E2FF;
-}
-
-tr.even td.sorting_1 {
- background-color: #EAEBFF;
-}
-
-tr.even td.sorting_2 {
- background-color: #F2F3FF;
-}
-
-tr.even td.sorting_3 {
- background-color: #F9F9FF;
-}
-
-
-/* For the Conditional-CSS grading rows */
-/*
- Colour calculations (based off the main row colours)
- Level 1:
- dd > c4
- ee > d5
- Level 2:
- dd > d1
- ee > e2
- */
-tr.odd.gradeA td.sorting_1 {
- background-color: #c4ffc4;
-}
-
-tr.odd.gradeA td.sorting_2 {
- background-color: #d1ffd1;
-}
-
-tr.odd.gradeA td.sorting_3 {
- background-color: #d1ffd1;
-}
-
-tr.even.gradeA td.sorting_1 {
- background-color: #d5ffd5;
-}
-
-tr.even.gradeA td.sorting_2 {
- background-color: #e2ffe2;
-}
-
-tr.even.gradeA td.sorting_3 {
- background-color: #e2ffe2;
-}
-
-tr.odd.gradeC td.sorting_1 {
- background-color: #c4c4ff;
-}
-
-tr.odd.gradeC td.sorting_2 {
- background-color: #d1d1ff;
-}
-
-tr.odd.gradeC td.sorting_3 {
- background-color: #d1d1ff;
-}
-
-tr.even.gradeC td.sorting_1 {
- background-color: #d5d5ff;
-}
-
-tr.even.gradeC td.sorting_2 {
- background-color: #e2e2ff;
-}
-
-tr.even.gradeC td.sorting_3 {
- background-color: #e2e2ff;
-}
-
-tr.odd.gradeX td.sorting_1 {
- background-color: #ffc4c4;
-}
-
-tr.odd.gradeX td.sorting_2 {
- background-color: #ffd1d1;
-}
-
-tr.odd.gradeX td.sorting_3 {
- background-color: #ffd1d1;
-}
-
-tr.even.gradeX td.sorting_1 {
- background-color: #ffd5d5;
-}
-
-tr.even.gradeX td.sorting_2 {
- background-color: #ffe2e2;
-}
-
-tr.even.gradeX td.sorting_3 {
- background-color: #ffe2e2;
-}
-
-tr.odd.gradeU td.sorting_1 {
- background-color: #c4c4c4;
-}
-
-tr.odd.gradeU td.sorting_2 {
- background-color: #d1d1d1;
-}
-
-tr.odd.gradeU td.sorting_3 {
- background-color: #d1d1d1;
-}
-
-tr.even.gradeU td.sorting_1 {
- background-color: #d5d5d5;
-}
-
-tr.even.gradeU td.sorting_2 {
- background-color: #e2e2e2;
-}
-
-tr.even.gradeU td.sorting_3 {
- background-color: #e2e2e2;
-}
-
-
-/*
- * Row highlighting example
- */
-.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
- background-color: #ECFFB3;
-}
-
-.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
- background-color: #E6FF99;
-} \ No newline at end of file
diff --git a/themes/default/js/plugins/datatables/css/jquery.dataTables.css b/themes/default/js/plugins/datatables/css/jquery.dataTables.css
index 7da7faec8..e29a70c80 100644
--- a/themes/default/js/plugins/datatables/css/jquery.dataTables.css
+++ b/themes/default/js/plugins/datatables/css/jquery.dataTables.css
@@ -1,221 +1,453 @@
-
/*
- * Table
+ * Table styles
*/
table.dataTable {
- margin: 0 auto;
- clear: both;
- width: 100%;
-}
-
-table.dataTable thead th {
- padding: 3px 18px 3px 10px;
- border-bottom: 1px solid black;
- font-weight: bold;
- cursor: pointer;
- *cursor: hand;
+ width: 100%;
+ margin: 0 auto;
+ clear: both;
+ border-collapse: separate;
+ border-spacing: 0;
+ /*
+ * Header and footer styles
+ */
+ /*
+ * Body styles
+ */
}
-
+table.dataTable thead th,
table.dataTable tfoot th {
- padding: 3px 18px 3px 10px;
- border-top: 1px solid black;
- font-weight: bold;
+ font-weight: bold;
}
-
-table.dataTable td {
- padding: 3px 10px;
+table.dataTable thead th,
+table.dataTable thead td {
+ padding: 10px 18px;
+ border-bottom: 1px solid #111;
}
-
-table.dataTable td.center,
+table.dataTable thead th:active,
+table.dataTable thead td:active {
+ outline: none;
+}
+table.dataTable tfoot th,
+table.dataTable tfoot td {
+ padding: 10px 18px 6px 18px;
+ border-top: 1px solid #111;
+}
+table.dataTable thead .sorting,
+table.dataTable thead .sorting_asc,
+table.dataTable thead .sorting_desc {
+ cursor: pointer;
+ *cursor: hand;
+}
+table.dataTable thead .sorting,
+table.dataTable thead .sorting_asc,
+table.dataTable thead .sorting_desc,
+table.dataTable thead .sorting_asc_disabled,
+table.dataTable thead .sorting_desc_disabled {
+ background-repeat: no-repeat;
+ background-position: center right;
+}
+table.dataTable thead .sorting {
+ background-image: url("../images/sort_both.png");
+}
+table.dataTable thead .sorting_asc {
+ background-image: url("../images/sort_asc.png");
+}
+table.dataTable thead .sorting_desc {
+ background-image: url("../images/sort_desc.png");
+}
+table.dataTable thead .sorting_asc_disabled {
+ background-image: url("../images/sort_asc_disabled.png");
+}
+table.dataTable thead .sorting_desc_disabled {
+ background-image: url("../images/sort_desc_disabled.png");
+}
+table.dataTable tbody tr {
+ background-color: #ffffff;
+}
+table.dataTable tbody tr.selected {
+ background-color: #B0BED9;
+}
+table.dataTable tbody th,
+table.dataTable tbody td {
+ padding: 8px 10px;
+}
+table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
+ border-top: 1px solid #ddd;
+}
+table.dataTable.row-border tbody tr:first-child th,
+table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
+table.dataTable.display tbody tr:first-child td {
+ border-top: none;
+}
+table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
+ border-top: 1px solid #ddd;
+ border-right: 1px solid #ddd;
+}
+table.dataTable.cell-border tbody tr th:first-child,
+table.dataTable.cell-border tbody tr td:first-child {
+ border-left: 1px solid #ddd;
+}
+table.dataTable.cell-border tbody tr:first-child th,
+table.dataTable.cell-border tbody tr:first-child td {
+ border-top: none;
+}
+table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
+ background-color: #f9f9f9;
+}
+table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
+ background-color: #acbad4;
+}
+table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
+ background-color: #f6f6f6;
+}
+table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
+ background-color: #aab7d1;
+}
+table.dataTable.order-column tbody tr > .sorting_1,
+table.dataTable.order-column tbody tr > .sorting_2,
+table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
+table.dataTable.display tbody tr > .sorting_2,
+table.dataTable.display tbody tr > .sorting_3 {
+ background-color: #fafafa;
+}
+table.dataTable.order-column tbody tr.selected > .sorting_1,
+table.dataTable.order-column tbody tr.selected > .sorting_2,
+table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
+table.dataTable.display tbody tr.selected > .sorting_2,
+table.dataTable.display tbody tr.selected > .sorting_3 {
+ background-color: #acbad5;
+}
+table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
+ background-color: #f1f1f1;
+}
+table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
+ background-color: #f3f3f3;
+}
+table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
+ background-color: whitesmoke;
+}
+table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
+ background-color: #a6b4cd;
+}
+table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
+ background-color: #a8b5cf;
+}
+table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
+ background-color: #a9b7d1;
+}
+table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
+ background-color: #fafafa;
+}
+table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
+ background-color: #fcfcfc;
+}
+table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
+ background-color: #fefefe;
+}
+table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
+ background-color: #acbad5;
+}
+table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
+ background-color: #aebcd6;
+}
+table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
+ background-color: #afbdd8;
+}
+table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
+ background-color: #eaeaea;
+}
+table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
+ background-color: #ececec;
+}
+table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
+ background-color: #efefef;
+}
+table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
+ background-color: #a2aec7;
+}
+table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
+ background-color: #a3b0c9;
+}
+table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
+ background-color: #a5b2cb;
+}
+table.dataTable.no-footer {
+ border-bottom: 1px solid #111;
+}
+table.dataTable.nowrap th, table.dataTable.nowrap td {
+ white-space: nowrap;
+}
+table.dataTable.compact thead th,
+table.dataTable.compact thead td {
+ padding: 4px 17px 4px 4px;
+}
+table.dataTable.compact tfoot th,
+table.dataTable.compact tfoot td {
+ padding: 4px;
+}
+table.dataTable.compact tbody th,
+table.dataTable.compact tbody td {
+ padding: 4px;
+}
+table.dataTable th.dt-left,
+table.dataTable td.dt-left {
+ text-align: left;
+}
+table.dataTable th.dt-center,
+table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
- text-align: center;
+ text-align: center;
+}
+table.dataTable th.dt-right,
+table.dataTable td.dt-right {
+ text-align: right;
+}
+table.dataTable th.dt-justify,
+table.dataTable td.dt-justify {
+ text-align: justify;
+}
+table.dataTable th.dt-nowrap,
+table.dataTable td.dt-nowrap {
+ white-space: nowrap;
+}
+table.dataTable thead th.dt-head-left,
+table.dataTable thead td.dt-head-left,
+table.dataTable tfoot th.dt-head-left,
+table.dataTable tfoot td.dt-head-left {
+ text-align: left;
+}
+table.dataTable thead th.dt-head-center,
+table.dataTable thead td.dt-head-center,
+table.dataTable tfoot th.dt-head-center,
+table.dataTable tfoot td.dt-head-center {
+ text-align: center;
+}
+table.dataTable thead th.dt-head-right,
+table.dataTable thead td.dt-head-right,
+table.dataTable tfoot th.dt-head-right,
+table.dataTable tfoot td.dt-head-right {
+ text-align: right;
+}
+table.dataTable thead th.dt-head-justify,
+table.dataTable thead td.dt-head-justify,
+table.dataTable tfoot th.dt-head-justify,
+table.dataTable tfoot td.dt-head-justify {
+ text-align: justify;
+}
+table.dataTable thead th.dt-head-nowrap,
+table.dataTable thead td.dt-head-nowrap,
+table.dataTable tfoot th.dt-head-nowrap,
+table.dataTable tfoot td.dt-head-nowrap {
+ white-space: nowrap;
+}
+table.dataTable tbody th.dt-body-left,
+table.dataTable tbody td.dt-body-left {
+ text-align: left;
+}
+table.dataTable tbody th.dt-body-center,
+table.dataTable tbody td.dt-body-center {
+ text-align: center;
+}
+table.dataTable tbody th.dt-body-right,
+table.dataTable tbody td.dt-body-right {
+ text-align: right;
+}
+table.dataTable tbody th.dt-body-justify,
+table.dataTable tbody td.dt-body-justify {
+ text-align: justify;
+}
+table.dataTable tbody th.dt-body-nowrap,
+table.dataTable tbody td.dt-body-nowrap {
+ white-space: nowrap;
}
-table.dataTable tr.odd { background-color: #E2E4FF; }
-table.dataTable tr.even { background-color: white; }
-
-table.dataTable tr.odd td.sorting_1 { background-color: #D3D6FF; }
-table.dataTable tr.odd td.sorting_2 { background-color: #DADCFF; }
-table.dataTable tr.odd td.sorting_3 { background-color: #E0E2FF; }
-table.dataTable tr.even td.sorting_1 { background-color: #EAEBFF; }
-table.dataTable tr.even td.sorting_2 { background-color: #F2F3FF; }
-table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
-
+table.dataTable,
+table.dataTable th,
+table.dataTable td {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
/*
- * Table wrapper
+ * Control feature layout
*/
.dataTables_wrapper {
- position: relative;
- clear: both;
- *zoom: 1;
+ position: relative;
+ clear: both;
+ *zoom: 1;
+ zoom: 1;
}
-
-
-/*
- * Page length menu
- */
-.dataTables_length {
- float: left;
+.dataTables_wrapper .dataTables_length {
+ float: left;
}
-
-
-/*
- * Filter
- */
-.dataTables_filter {
- float: right;
- text-align: right;
+.dataTables_wrapper .dataTables_filter {
+ float: right;
+ text-align: right;
}
-
-
-/*
- * Table information
- */
-.dataTables_info {
- clear: both;
- float: left;
+.dataTables_wrapper .dataTables_filter input {
+ margin-left: 0.5em;
}
-
-
-/*
- * Pagination
- */
-.dataTables_paginate {
- float: right;
- text-align: right;
+.dataTables_wrapper .dataTables_info {
+ clear: both;
+ float: left;
+ padding-top: 0.755em;
}
-
-/* Two button pagination - previous / next */
-.paginate_disabled_previous,
-.paginate_enabled_previous,
-.paginate_disabled_next,
-.paginate_enabled_next {
- height: 19px;
- float: left;
- cursor: pointer;
- *cursor: hand;
- color: #111 !important;
-}
-.paginate_disabled_previous:hover,
-.paginate_enabled_previous:hover,
-.paginate_disabled_next:hover,
-.paginate_enabled_next:hover {
- text-decoration: none !important;
-}
-.paginate_disabled_previous:active,
-.paginate_enabled_previous:active,
-.paginate_disabled_next:active,
-.paginate_enabled_next:active {
- outline: none;
+.dataTables_wrapper .dataTables_paginate {
+ float: right;
+ text-align: right;
+ padding-top: 0.25em;
}
-
-.paginate_disabled_previous,
-.paginate_disabled_next {
- color: #666 !important;
+.dataTables_wrapper .dataTables_paginate .paginate_button {
+ box-sizing: border-box;
+ display: inline-block;
+ min-width: 1.5em;
+ padding: 0.5em 1em;
+ margin-left: 2px;
+ text-align: center;
+ text-decoration: none !important;
+ cursor: pointer;
+ *cursor: hand;
+ color: #333 !important;
+ border: 1px solid transparent;
+ border-radius: 2px;
}
-.paginate_disabled_previous,
-.paginate_enabled_previous {
- padding-left: 23px;
+.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
+ color: #333 !important;
+ border: 1px solid #979797;
+ background-color: white;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc));
+ /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%);
+ /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%);
+ /* FF3.6+ */
+ background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%);
+ /* IE10+ */
+ background: -o-linear-gradient(top, white 0%, #dcdcdc 100%);
+ /* Opera 11.10+ */
+ background: linear-gradient(to bottom, white 0%, #dcdcdc 100%);
+ /* W3C */
}
-.paginate_disabled_next,
-.paginate_enabled_next {
- padding-right: 23px;
- margin-left: 10px;
+.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
+ cursor: default;
+ color: #666 !important;
+ border: 1px solid transparent;
+ background: transparent;
+ box-shadow: none;
}
-
-.paginate_enabled_previous { background: url('../images/back_enabled.png') no-repeat top left; }
-.paginate_enabled_previous:hover { background: url('../images/back_enabled_hover.png') no-repeat top left; }
-.paginate_disabled_previous { background: url('../images/back_disabled.png') no-repeat top left; }
-
-.paginate_enabled_next { background: url('../images/forward_enabled.png') no-repeat top right; }
-.paginate_enabled_next:hover { background: url('../images/forward_enabled_hover.png') no-repeat top right; }
-.paginate_disabled_next { background: url('../images/forward_disabled.png') no-repeat top right; }
-
-/* Full number pagination */
-.paging_full_numbers {
- height: 22px;
- line-height: 22px;
+.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
+ color: white !important;
+ border: 1px solid #111;
+ background-color: #585858;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
+ /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
+ /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, #585858 0%, #111 100%);
+ /* FF3.6+ */
+ background: -ms-linear-gradient(top, #585858 0%, #111 100%);
+ /* IE10+ */
+ background: -o-linear-gradient(top, #585858 0%, #111 100%);
+ /* Opera 11.10+ */
+ background: linear-gradient(to bottom, #585858 0%, #111 100%);
+ /* W3C */
}
-.paging_full_numbers a:active {
- outline: none
+.dataTables_wrapper .dataTables_paginate .paginate_button:active {
+ outline: none;
+ background-color: #2b2b2b;
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
+ /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ /* Chrome10+,Safari5.1+ */
+ background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ /* FF3.6+ */
+ background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ /* IE10+ */
+ background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
+ /* Opera 11.10+ */
+ background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
+ /* W3C */
+ box-shadow: inset 0 0 3px #111;
}
-.paging_full_numbers a:hover {
- text-decoration: none;
+.dataTables_wrapper .dataTables_paginate .ellipsis {
+ padding: 0 1em;
}
-
-.paging_full_numbers a.paginate_button,
-.paging_full_numbers a.paginate_active {
- border: 1px solid #aaa;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- border-radius: 5px;
- padding: 2px 5px;
- margin: 0 3px;
- cursor: pointer;
- *cursor: hand;
- color: #333 !important;
+.dataTables_wrapper .dataTables_processing {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 100%;
+ height: 40px;
+ margin-left: -50%;
+ margin-top: -25px;
+ padding-top: 20px;
+ text-align: center;
+ font-size: 1.2em;
+ background-color: white;
+ background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
+ background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
+ background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
}
-
-.paging_full_numbers a.paginate_button {
- background-color: #ddd;
+.dataTables_wrapper .dataTables_length,
+.dataTables_wrapper .dataTables_filter,
+.dataTables_wrapper .dataTables_info,
+.dataTables_wrapper .dataTables_processing,
+.dataTables_wrapper .dataTables_paginate {
+ color: #333;
}
-
-.paging_full_numbers a.paginate_button:hover {
- background-color: #ccc;
- text-decoration: none !important;
+.dataTables_wrapper .dataTables_scroll {
+ clear: both;
}
-
-.paging_full_numbers a.paginate_active {
- background-color: #99B3FF;
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
+ *margin-top: -1px;
+ -webkit-overflow-scrolling: touch;
}
-
-
-/*
- * Processing indicator
- */
-.dataTables_processing {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 250px;
- height: 30px;
- margin-left: -125px;
- margin-top: -15px;
- padding: 14px 0 2px 0;
- border: 1px solid #ddd;
- text-align: center;
- color: #999;
- font-size: 14px;
- background-color: white;
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td {
+ vertical-align: middle;
}
-
-
-/*
- * Sorting
- */
-.sorting { background: url('../images/sort_both.png') no-repeat center right; }
-.sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; }
-.sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; }
-
-.sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; }
-.sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; }
-
-table.dataTable thead th:active,
-table.dataTable thead td:active {
- outline: none;
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
+.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
+ height: 0;
+ overflow: hidden;
+ margin: 0 !important;
+ padding: 0 !important;
}
-
-
-/*
- * Scrolling
- */
-.dataTables_scroll {
- clear: both;
+.dataTables_wrapper.no-footer .dataTables_scrollBody {
+ border-bottom: 1px solid #111;
}
-
-.dataTables_scrollBody {
- *margin-top: -1px;
- -webkit-overflow-scrolling: touch;
+.dataTables_wrapper.no-footer div.dataTables_scrollHead table,
+.dataTables_wrapper.no-footer div.dataTables_scrollBody table {
+ border-bottom: none;
+}
+.dataTables_wrapper:after {
+ visibility: hidden;
+ display: block;
+ content: "";
+ clear: both;
+ height: 0;
}
+@media screen and (max-width: 767px) {
+ .dataTables_wrapper .dataTables_info,
+ .dataTables_wrapper .dataTables_paginate {
+ float: none;
+ text-align: center;
+ }
+ .dataTables_wrapper .dataTables_paginate {
+ margin-top: 0.5em;
+ }
+}
+@media screen and (max-width: 640px) {
+ .dataTables_wrapper .dataTables_length,
+ .dataTables_wrapper .dataTables_filter {
+ float: none;
+ text-align: center;
+ }
+ .dataTables_wrapper .dataTables_filter {
+ margin-top: 0.5em;
+ }
+}
diff --git a/themes/default/js/plugins/datatables/css/jquery.dataTables_themeroller.css b/themes/default/js/plugins/datatables/css/jquery.dataTables_themeroller.css
deleted file mode 100644
index cf1d4ed79..000000000
--- a/themes/default/js/plugins/datatables/css/jquery.dataTables_themeroller.css
+++ /dev/null
@@ -1,244 +0,0 @@
-
-
-/*
- * Table
- */
-table.dataTable {
- margin: 0 auto;
- clear: both;
- width: 100%;
- border-collapse: collapse;
-}
-
-table.dataTable thead th {
- padding: 3px 0px 3px 10px;
- cursor: pointer;
- *cursor: hand;
-}
-
-table.dataTable tfoot th {
- padding: 3px 10px;
-}
-
-table.dataTable td {
- padding: 3px 10px;
-}
-
-table.dataTable td.center,
-table.dataTable td.dataTables_empty {
- text-align: center;
-}
-
-table.dataTable tr.odd { background-color: #E2E4FF; }
-table.dataTable tr.even { background-color: white; }
-
-table.dataTable tr.odd td.sorting_1 { background-color: #D3D6FF; }
-table.dataTable tr.odd td.sorting_2 { background-color: #DADCFF; }
-table.dataTable tr.odd td.sorting_3 { background-color: #E0E2FF; }
-table.dataTable tr.even td.sorting_1 { background-color: #EAEBFF; }
-table.dataTable tr.even td.sorting_2 { background-color: #F2F3FF; }
-table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
-
-
-/*
- * Table wrapper
- */
-.dataTables_wrapper {
- position: relative;
- clear: both;
- *zoom: 1;
-}
-.dataTables_wrapper .ui-widget-header {
- font-weight: normal;
-}
-.dataTables_wrapper .ui-toolbar {
- padding: 5px;
-}
-
-
-/*
- * Page length menu
- */
-.dataTables_length {
- float: left;
-}
-
-
-/*
- * Filter
- */
-.dataTables_filter {
- float: right;
- text-align: right;
-}
-
-
-/*
- * Table information
- */
-.dataTables_info {
- padding-top: 3px;
- clear: both;
- float: left;
-}
-
-
-/*
- * Pagination
- */
-.dataTables_paginate {
- float: right;
- text-align: right;
-}
-
-.dataTables_paginate .ui-button {
- margin-right: -0.1em !important;
-}
-
-.paging_two_button .ui-button {
- float: left;
- cursor: pointer;
- * cursor: hand;
-}
-
-.paging_full_numbers .ui-button {
- padding: 2px 6px;
- margin: 0;
- cursor: pointer;
- * cursor: hand;
- color: #333 !important;
-}
-
-/* Two button pagination - previous / next */
-.paginate_disabled_previous,
-.paginate_enabled_previous,
-.paginate_disabled_next,
-.paginate_enabled_next {
- height: 19px;
- float: left;
- cursor: pointer;
- *cursor: hand;
- color: #111 !important;
-}
-.paginate_disabled_previous:hover,
-.paginate_enabled_previous:hover,
-.paginate_disabled_next:hover,
-.paginate_enabled_next:hover {
- text-decoration: none !important;
-}
-.paginate_disabled_previous:active,
-.paginate_enabled_previous:active,
-.paginate_disabled_next:active,
-.paginate_enabled_next:active {
- outline: none;
-}
-
-.paginate_disabled_previous,
-.paginate_disabled_next {
- color: #666 !important;
-}
-.paginate_disabled_previous,
-.paginate_enabled_previous {
- padding-left: 23px;
-}
-.paginate_disabled_next,
-.paginate_enabled_next {
- padding-right: 23px;
- margin-left: 10px;
-}
-
-.paginate_enabled_previous { background: url('../images/back_enabled.png') no-repeat top left; }
-.paginate_enabled_previous:hover { background: url('../images/back_enabled_hover.png') no-repeat top left; }
-.paginate_disabled_previous { background: url('../images/back_disabled.png') no-repeat top left; }
-
-.paginate_enabled_next { background: url('../images/forward_enabled.png') no-repeat top right; }
-.paginate_enabled_next:hover { background: url('../images/forward_enabled_hover.png') no-repeat top right; }
-.paginate_disabled_next { background: url('../images/forward_disabled.png') no-repeat top right; }
-
-/* Full number pagination */
-.paging_full_numbers a:active {
- outline: none
-}
-.paging_full_numbers a:hover {
- text-decoration: none;
-}
-
-.paging_full_numbers a.paginate_button,
-.paging_full_numbers a.paginate_active {
- border: 1px solid #aaa;
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- border-radius: 5px;
- padding: 2px 5px;
- margin: 0 3px;
- cursor: pointer;
- *cursor: hand;
- color: #333 !important;
-}
-
-.paging_full_numbers a.paginate_button {
- background-color: #ddd;
-}
-
-.paging_full_numbers a.paginate_button:hover {
- background-color: #ccc;
- text-decoration: none !important;
-}
-
-.paging_full_numbers a.paginate_active {
- background-color: #99B3FF;
-}
-
-
-/*
- * Processing indicator
- */
-.dataTables_processing {
- position: absolute;
- top: 50%;
- left: 50%;
- width: 250px;
- height: 30px;
- margin-left: -125px;
- margin-top: -15px;
- padding: 14px 0 2px 0;
- border: 1px solid #ddd;
- text-align: center;
- color: #999;
- font-size: 14px;
- background-color: white;
-}
-
-
-/*
- * Sorting
- */
-table.dataTable thead th div.DataTables_sort_wrapper {
- position: relative;
- padding-right: 20px;
-}
-
-table.dataTable thead th div.DataTables_sort_wrapper span {
- position: absolute;
- top: 50%;
- margin-top: -8px;
- right: 0;
-}
-
-table.dataTable th:active {
- outline: none;
-}
-
-
-/*
- * Scrolling
- */
-.dataTables_scroll {
- clear: both;
-}
-
-.dataTables_scrollBody {
- *margin-top: -1px;
- -webkit-overflow-scrolling: touch;
-}
-
diff --git a/themes/default/js/plugins/datatables/images/Sorting icons.psd b/themes/default/js/plugins/datatables/images/Sorting icons.psd
deleted file mode 100644
index 53b2e0685..000000000
--- a/themes/default/js/plugins/datatables/images/Sorting icons.psd
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/back_disabled.png b/themes/default/js/plugins/datatables/images/back_disabled.png
deleted file mode 100644
index 881de7976..000000000
--- a/themes/default/js/plugins/datatables/images/back_disabled.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/back_enabled.png b/themes/default/js/plugins/datatables/images/back_enabled.png
deleted file mode 100644
index c608682b0..000000000
--- a/themes/default/js/plugins/datatables/images/back_enabled.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/back_enabled_hover.png b/themes/default/js/plugins/datatables/images/back_enabled_hover.png
deleted file mode 100644
index d300f1064..000000000
--- a/themes/default/js/plugins/datatables/images/back_enabled_hover.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/favicon.ico b/themes/default/js/plugins/datatables/images/favicon.ico
deleted file mode 100644
index 6eeaa2a0d..000000000
--- a/themes/default/js/plugins/datatables/images/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/forward_disabled.png b/themes/default/js/plugins/datatables/images/forward_disabled.png
deleted file mode 100644
index 6a6ded7de..000000000
--- a/themes/default/js/plugins/datatables/images/forward_disabled.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/forward_enabled.png b/themes/default/js/plugins/datatables/images/forward_enabled.png
deleted file mode 100644
index a4e6b5384..000000000
--- a/themes/default/js/plugins/datatables/images/forward_enabled.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/datatables/images/forward_enabled_hover.png b/themes/default/js/plugins/datatables/images/forward_enabled_hover.png
deleted file mode 100644
index fc46c5ebf..000000000
--- a/themes/default/js/plugins/datatables/images/forward_enabled_hover.png
+++ /dev/null
Binary files differ
diff --git a/themes/default/js/plugins/jquery.dataTables.js b/themes/default/js/plugins/jquery.dataTables.js
index 1d8a220ba..380ab1e44 100644
--- a/themes/default/js/plugins/jquery.dataTables.js
+++ b/themes/default/js/plugins/jquery.dataTables.js
@@ -1,4942 +1,5385 @@
+/*! DataTables 1.10.11
+ * ©2008-2015 SpryMedia Ltd - datatables.net/license
+ */
+
/**
* @summary DataTables
- * @description Paginate, search and sort HTML tables
- * @version 1.9.4
+ * @description Paginate, search and order HTML tables
+ * @version 1.10.11
* @file jquery.dataTables.js
- * @author Allan Jardine (www.sprymedia.co.uk)
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
* @contact www.sprymedia.co.uk/contact
+ * @copyright Copyright 2008-2015 SpryMedia Ltd.
*
- * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved.
+ * This source file is free software, available under the following license:
+ * MIT license - http://datatables.net/license
*
- * This source file is free software, under either the GPL v2 license or a
- * BSD style license, available at:
- * http://datatables.net/license_gpl2
- * http://datatables.net/license_bsd
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * This source file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
+ *
* For details please refer to: http://www.datatables.net
*/
/*jslint evil: true, undef: true, browser: true */
-/*globals $, jQuery,define,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/
-
-(/** @lends <global> */function( window, document, undefined ) {
+/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
(function( factory ) {
"use strict";
- // Define as an AMD module if possible
- if ( typeof define === 'function' && define.amd )
- {
- define( ['jquery'], factory );
+ if ( typeof define === 'function' && define.amd ) {
+ // AMD
+ define( ['jquery'], function ( $ ) {
+ return factory( $, window, document );
+ } );
}
- /* Define using browser globals otherwise
- * Prevent multiple instantiations if the script is loaded twice
- */
- else if ( jQuery && !jQuery.fn.dataTable )
- {
- factory( jQuery );
+ else if ( typeof exports === 'object' ) {
+ // CommonJS
+ module.exports = function (root, $) {
+ if ( ! root ) {
+ // CommonJS environments without a window global must pass a
+ // root. This will give an error otherwise
+ root = window;
+ }
+
+ if ( ! $ ) {
+ $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
+ require('jquery') :
+ require('jquery')( root );
+ }
+
+ return factory( $, root, root.document );
+ };
+ }
+ else {
+ // Browser
+ factory( jQuery, window, document );
}
}
-(/** @lends <global> */function( $ ) {
+(function( $, window, document, undefined ) {
"use strict";
- /**
- * DataTables is a plug-in for the jQuery Javascript library. It is a
- * highly flexible tool, based upon the foundations of progressive
- * enhancement, which will add advanced interaction controls to any
- * HTML table. For a full list of features please refer to
- * <a href="http://datatables.net">DataTables.net</a>.
+
+ /**
+ * DataTables is a plug-in for the jQuery Javascript library. It is a highly
+ * flexible tool, based upon the foundations of progressive enhancement,
+ * which will add advanced interaction controls to any HTML table. For a
+ * full list of features please refer to
+ * [DataTables.net](href="http://datatables.net).
*
- * Note that the <i>DataTable</i> object is not a global variable but is
- * aliased to <i>jQuery.fn.DataTable</i> and <i>jQuery.fn.dataTable</i> through which
- * it may be accessed.
+ * Note that the `DataTable` object is not a global variable but is aliased
+ * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
+ * be accessed.
*
* @class
- * @param {object} [oInit={}] Configuration object for DataTables. Options
+ * @param {object} [init={}] Configuration object for DataTables. Options
* are defined by {@link DataTable.defaults}
- * @requires jQuery 1.3+
- *
+ * @requires jQuery 1.7+
+ *
* @example
* // Basic initialisation
* $(document).ready( function {
* $('#example').dataTable();
* } );
- *
+ *
* @example
* // Initialisation with configuration options - in this case, disable
* // pagination and sorting.
* $(document).ready( function {
* $('#example').dataTable( {
- * "bPaginate": false,
- * "bSort": false
+ * "paginate": false,
+ * "sort": false
* } );
* } );
*/
- var DataTable = function( oInit )
- {
-
-
- /**
- * Add a column to the list used for the table with default values
- * @param {object} oSettings dataTables settings object
- * @param {node} nTh The th element for this column
- * @memberof DataTable#oApi
- */
- function _fnAddColumn( oSettings, nTh )
- {
- var oDefaults = DataTable.defaults.columns;
- var iCol = oSettings.aoColumns.length;
- var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
- "sSortingClass": oSettings.oClasses.sSortable,
- "sSortingClassJUI": oSettings.oClasses.sSortJUI,
- "nTh": nTh ? nTh : document.createElement('th'),
- "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
- "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
- "mData": oDefaults.mData ? oDefaults.oDefaults : iCol
- } );
- oSettings.aoColumns.push( oCol );
-
- /* Add a column specific filter */
- if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null )
- {
- oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch );
+ var DataTable;
+
+
+ /*
+ * It is useful to have variables which are scoped locally so only the
+ * DataTables functions can access them and they don't leak into global space.
+ * At the same time these functions are often useful over multiple files in the
+ * core and API, so we list, or at least document, all variables which are used
+ * by DataTables as private variables here. This also ensures that there is no
+ * clashing of variable names and that they can easily referenced for reuse.
+ */
+
+
+ // Defined else where
+ // _selector_run
+ // _selector_opts
+ // _selector_first
+ // _selector_row_indexes
+
+ var _ext; // DataTable.ext
+ var _Api; // DataTable.Api
+ var _api_register; // DataTable.Api.register
+ var _api_registerPlural; // DataTable.Api.registerPlural
+
+ var _re_dic = {};
+ var _re_new_lines = /[\r\n]/g;
+ var _re_html = /<.*?>/g;
+ var _re_date_start = /^[\w\+\-]/;
+ var _re_date_end = /[\w\+\-]$/;
+
+ // Escape regular expression special characters
+ var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
+
+ // http://en.wikipedia.org/wiki/Foreign_exchange_market
+ // - \u20BD - Russian ruble.
+ // - \u20a9 - South Korean Won
+ // - \u20BA - Turkish Lira
+ // - \u20B9 - Indian Rupee
+ // - R - Brazil (R$) and South Africa
+ // - fr - Swiss Franc
+ // - kr - Swedish krona, Norwegian krone and Danish krone
+ // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
+ // standards as thousands separators.
+ var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
+
+
+ var _empty = function ( d ) {
+ return !d || d === true || d === '-' ? true : false;
+ };
+
+
+ var _intVal = function ( s ) {
+ var integer = parseInt( s, 10 );
+ return !isNaN(integer) && isFinite(s) ? integer : null;
+ };
+
+ // Convert from a formatted number with characters other than `.` as the
+ // decimal place, to a Javascript number
+ var _numToDecimal = function ( num, decimalPoint ) {
+ // Cache created regular expressions for speed as this function is called often
+ if ( ! _re_dic[ decimalPoint ] ) {
+ _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
+ }
+ return typeof num === 'string' && decimalPoint !== '.' ?
+ num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
+ num;
+ };
+
+
+ var _isNumber = function ( d, decimalPoint, formatted ) {
+ var strType = typeof d === 'string';
+
+ // If empty return immediately so there must be a number if it is a
+ // formatted string (this stops the string "k", or "kr", etc being detected
+ // as a formatted number for currency
+ if ( _empty( d ) ) {
+ return true;
+ }
+
+ if ( decimalPoint && strType ) {
+ d = _numToDecimal( d, decimalPoint );
+ }
+
+ if ( formatted && strType ) {
+ d = d.replace( _re_formatted_numeric, '' );
+ }
+
+ return !isNaN( parseFloat(d) ) && isFinite( d );
+ };
+
+
+ // A string without HTML in it can be considered to be HTML still
+ var _isHtml = function ( d ) {
+ return _empty( d ) || typeof d === 'string';
+ };
+
+
+ var _htmlNumeric = function ( d, decimalPoint, formatted ) {
+ if ( _empty( d ) ) {
+ return true;
+ }
+
+ var html = _isHtml( d );
+ return ! html ?
+ null :
+ _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
+ true :
+ null;
+ };
+
+
+ var _pluck = function ( a, prop, prop2 ) {
+ var out = [];
+ var i=0, ien=a.length;
+
+ // Could have the test in the loop for slightly smaller code, but speed
+ // is essential here
+ if ( prop2 !== undefined ) {
+ for ( ; i<ien ; i++ ) {
+ if ( a[i] && a[i][ prop ] ) {
+ out.push( a[i][ prop ][ prop2 ] );
+ }
}
- else
- {
- var oPre = oSettings.aoPreSearchCols[ iCol ];
-
- /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */
- if ( oPre.bRegex === undefined )
- {
- oPre.bRegex = true;
+ }
+ else {
+ for ( ; i<ien ; i++ ) {
+ if ( a[i] ) {
+ out.push( a[i][ prop ] );
}
-
- if ( oPre.bSmart === undefined )
- {
- oPre.bSmart = true;
+ }
+ }
+
+ return out;
+ };
+
+
+ // Basically the same as _pluck, but rather than looping over `a` we use `order`
+ // as the indexes to pick from `a`
+ var _pluck_order = function ( a, order, prop, prop2 )
+ {
+ var out = [];
+ var i=0, ien=order.length;
+
+ // Could have the test in the loop for slightly smaller code, but speed
+ // is essential here
+ if ( prop2 !== undefined ) {
+ for ( ; i<ien ; i++ ) {
+ if ( a[ order[i] ][ prop ] ) {
+ out.push( a[ order[i] ][ prop ][ prop2 ] );
}
-
- if ( oPre.bCaseInsensitive === undefined )
- {
- oPre.bCaseInsensitive = true;
+ }
+ }
+ else {
+ for ( ; i<ien ; i++ ) {
+ out.push( a[ order[i] ][ prop ] );
+ }
+ }
+
+ return out;
+ };
+
+
+ var _range = function ( len, start )
+ {
+ var out = [];
+ var end;
+
+ if ( start === undefined ) {
+ start = 0;
+ end = len;
+ }
+ else {
+ end = start;
+ start = len;
+ }
+
+ for ( var i=start ; i<end ; i++ ) {
+ out.push( i );
+ }
+
+ return out;
+ };
+
+
+ var _removeEmpty = function ( a )
+ {
+ var out = [];
+
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
+ if ( a[i] ) { // careful - will remove all falsy values!
+ out.push( a[i] );
+ }
+ }
+
+ return out;
+ };
+
+
+ var _stripHtml = function ( d ) {
+ return d.replace( _re_html, '' );
+ };
+
+
+ /**
+ * Find the unique elements in a source array.
+ *
+ * @param {array} src Source array
+ * @return {array} Array of unique items
+ * @ignore
+ */
+ var _unique = function ( src )
+ {
+ // A faster unique method is to use object keys to identify used values,
+ // but this doesn't work with arrays or objects, which we must also
+ // consider. See jsperf.com/compare-array-unique-versions/4 for more
+ // information.
+ var
+ out = [],
+ val,
+ i, ien=src.length,
+ j, k=0;
+
+ again: for ( i=0 ; i<ien ; i++ ) {
+ val = src[i];
+
+ for ( j=0 ; j<k ; j++ ) {
+ if ( out[j] === val ) {
+ continue again;
}
}
-
- /* Use the column options function to initialise classes etc */
- _fnColumnOptions( oSettings, iCol, null );
+
+ out.push( val );
+ k++;
}
-
-
- /**
- * Apply options for a column
- * @param {object} oSettings dataTables settings object
- * @param {int} iCol column index to consider
- * @param {object} oOptions object with sType, bVisible and bSearchable etc
- * @memberof DataTable#oApi
- */
- function _fnColumnOptions( oSettings, iCol, oOptions )
- {
- var oCol = oSettings.aoColumns[ iCol ];
-
- /* User specified column options */
- if ( oOptions !== undefined && oOptions !== null )
+
+ return out;
+ };
+
+
+
+ /**
+ * Create a mapping object that allows camel case parameters to be looked up
+ * for their Hungarian counterparts. The mapping is stored in a private
+ * parameter called `_hungarianMap` which can be accessed on the source object.
+ * @param {object} o
+ * @memberof DataTable#oApi
+ */
+ function _fnHungarianMap ( o )
+ {
+ var
+ hungarian = 'a aa ai ao as b fn i m o s ',
+ match,
+ newKey,
+ map = {};
+
+ $.each( o, function (key, val) {
+ match = key.match(/^([^A-Z]+?)([A-Z])/);
+
+ if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
{
- /* Backwards compatibility for mDataProp */
- if ( oOptions.mDataProp && !oOptions.mData )
+ newKey = key.replace( match[0], match[2].toLowerCase() );
+ map[ newKey ] = key;
+
+ if ( match[1] === 'o' )
{
- oOptions.mData = oOptions.mDataProp;
+ _fnHungarianMap( o[key] );
}
-
- if ( oOptions.sType !== undefined )
+ }
+ } );
+
+ o._hungarianMap = map;
+ }
+
+
+ /**
+ * Convert from camel case parameters to Hungarian, based on a Hungarian map
+ * created by _fnHungarianMap.
+ * @param {object} src The model object which holds all parameters that can be
+ * mapped.
+ * @param {object} user The object to convert from camel case to Hungarian.
+ * @param {boolean} force When set to `true`, properties which already have a
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
+ * won't be.
+ * @memberof DataTable#oApi
+ */
+ function _fnCamelToHungarian ( src, user, force )
+ {
+ if ( ! src._hungarianMap ) {
+ _fnHungarianMap( src );
+ }
+
+ var hungarianKey;
+
+ $.each( user, function (key, val) {
+ hungarianKey = src._hungarianMap[ key ];
+
+ if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
+ {
+ // For objects, we need to buzz down into the object to copy parameters
+ if ( hungarianKey.charAt(0) === 'o' )
{
- oCol.sType = oOptions.sType;
- oCol._bAutoType = false;
+ // Copy the camelCase options over to the hungarian
+ if ( ! user[ hungarianKey ] ) {
+ user[ hungarianKey ] = {};
+ }
+ $.extend( true, user[hungarianKey], user[key] );
+
+ _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
}
-
- $.extend( oCol, oOptions );
- _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
-
- /* iDataSort to be applied (backwards compatibility), but aDataSort will take
- * priority if defined
- */
- if ( oOptions.iDataSort !== undefined )
- {
- oCol.aDataSort = [ oOptions.iDataSort ];
+ else {
+ user[hungarianKey] = user[ key ];
}
- _fnMap( oCol, oOptions, "aDataSort" );
}
-
- /* Cache the data get and set functions for speed */
- var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
- var mData = _fnGetObjectDataFn( oCol.mData );
-
- oCol.fnGetData = function (oData, sSpecific) {
- var innerData = mData( oData, sSpecific );
-
- if ( oCol.mRender && (sSpecific && sSpecific !== '') )
- {
- return mRender( innerData, sSpecific, oData );
+ } );
+ }
+
+
+ /**
+ * Language compatibility - when certain options are given, and others aren't, we
+ * need to duplicate the values over, in order to provide backwards compatibility
+ * with older language files.
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnLanguageCompat( lang )
+ {
+ var defaults = DataTable.defaults.oLanguage;
+ var zeroRecords = lang.sZeroRecords;
+
+ /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
+ * sZeroRecords - assuming that is given.
+ */
+ if ( ! lang.sEmptyTable && zeroRecords &&
+ defaults.sEmptyTable === "No data available in table" )
+ {
+ _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
+ }
+
+ /* Likewise with loading records */
+ if ( ! lang.sLoadingRecords && zeroRecords &&
+ defaults.sLoadingRecords === "Loading..." )
+ {
+ _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
+ }
+
+ // Old parameter name of the thousands separator mapped onto the new
+ if ( lang.sInfoThousands ) {
+ lang.sThousands = lang.sInfoThousands;
+ }
+
+ var decimal = lang.sDecimal;
+ if ( decimal ) {
+ _addNumericSort( decimal );
+ }
+ }
+
+
+ /**
+ * Map one parameter onto another
+ * @param {object} o Object to map
+ * @param {*} knew The new parameter name
+ * @param {*} old The old parameter name
+ */
+ var _fnCompatMap = function ( o, knew, old ) {
+ if ( o[ knew ] !== undefined ) {
+ o[ old ] = o[ knew ];
+ }
+ };
+
+
+ /**
+ * Provide backwards compatibility for the main DT options. Note that the new
+ * options are mapped onto the old parameters, so this is an external interface
+ * change only.
+ * @param {object} init Object to map
+ */
+ function _fnCompatOpts ( init )
+ {
+ _fnCompatMap( init, 'ordering', 'bSort' );
+ _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
+ _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
+ _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
+ _fnCompatMap( init, 'order', 'aaSorting' );
+ _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
+ _fnCompatMap( init, 'paging', 'bPaginate' );
+ _fnCompatMap( init, 'pagingType', 'sPaginationType' );
+ _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
+ _fnCompatMap( init, 'searching', 'bFilter' );
+
+ // Boolean initialisation of x-scrolling
+ if ( typeof init.sScrollX === 'boolean' ) {
+ init.sScrollX = init.sScrollX ? '100%' : '';
+ }
+ if ( typeof init.scrollX === 'boolean' ) {
+ init.scrollX = init.scrollX ? '100%' : '';
+ }
+
+ // Column search objects are in an array, so it needs to be converted
+ // element by element
+ var searchCols = init.aoSearchCols;
+
+ if ( searchCols ) {
+ for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
+ if ( searchCols[i] ) {
+ _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
}
- return innerData;
- };
- oCol.fnSetData = _fnSetObjectDataFn( oCol.mData );
-
- /* Feature sorting overrides column specific when off */
- if ( !oSettings.oFeatures.bSort )
- {
- oCol.bSortable = false;
}
-
- /* Check that the class assignment is correct for sorting */
- if ( !oCol.bSortable ||
- ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
- {
- oCol.sSortingClass = oSettings.oClasses.sSortableNone;
- oCol.sSortingClassJUI = "";
+ }
+ }
+
+
+ /**
+ * Provide backwards compatibility for column options. Note that the new options
+ * are mapped onto the old parameters, so this is an external interface change
+ * only.
+ * @param {object} init Object to map
+ */
+ function _fnCompatCols ( init )
+ {
+ _fnCompatMap( init, 'orderable', 'bSortable' );
+ _fnCompatMap( init, 'orderData', 'aDataSort' );
+ _fnCompatMap( init, 'orderSequence', 'asSorting' );
+ _fnCompatMap( init, 'orderDataType', 'sortDataType' );
+
+ // orderData can be given as an integer
+ var dataSort = init.aDataSort;
+ if ( dataSort && ! $.isArray( dataSort ) ) {
+ init.aDataSort = [ dataSort ];
+ }
+ }
+
+
+ /**
+ * Browser feature detection for capabilities, quirks
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnBrowserDetect( settings )
+ {
+ // We don't need to do this every time DataTables is constructed, the values
+ // calculated are specific to the browser and OS configuration which we
+ // don't expect to change between initialisations
+ if ( ! DataTable.__browser ) {
+ var browser = {};
+ DataTable.__browser = browser;
+
+ // Scrolling feature / quirks detection
+ var n = $('<div/>')
+ .css( {
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ height: 1,
+ width: 1,
+ overflow: 'hidden'
+ } )
+ .append(
+ $('<div/>')
+ .css( {
+ position: 'absolute',
+ top: 1,
+ left: 1,
+ width: 100,
+ overflow: 'scroll'
+ } )
+ .append(
+ $('<div/>')
+ .css( {
+ width: '100%',
+ height: 10
+ } )
+ )
+ )
+ .appendTo( 'body' );
+
+ var outer = n.children();
+ var inner = outer.children();
+
+ // Numbers below, in order, are:
+ // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
+ //
+ // IE6 XP: 100 100 100 83
+ // IE7 Vista: 100 100 100 83
+ // IE 8+ Windows: 83 83 100 83
+ // Evergreen Windows: 83 83 100 83
+ // Evergreen Mac with scrollbars: 85 85 100 85
+ // Evergreen Mac without scrollbars: 100 100 100 100
+
+ // Get scrollbar width
+ browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
+
+ // IE6/7 will oversize a width 100% element inside a scrolling element, to
+ // include the width of the scrollbar, while other browsers ensure the inner
+ // element is contained without forcing scrolling
+ browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
+
+ // In rtl text layout, some browsers (most, but not all) will place the
+ // scrollbar on the left, rather than the right.
+ browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
+
+ // IE8- don't provide height and width for getBoundingClientRect
+ browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
+
+ n.remove();
+ }
+
+ $.extend( settings.oBrowser, DataTable.__browser );
+ settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
+ }
+
+
+ /**
+ * Array.prototype reduce[Right] method, used for browsers which don't support
+ * JS 1.6. Done this way to reduce code size, since we iterate either way
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnReduce ( that, fn, init, start, end, inc )
+ {
+ var
+ i = start,
+ value,
+ isSet = false;
+
+ if ( init !== undefined ) {
+ value = init;
+ isSet = true;
+ }
+
+ while ( i !== end ) {
+ if ( ! that.hasOwnProperty(i) ) {
+ continue;
}
- else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 )
- {
- oCol.sSortingClass = oSettings.oClasses.sSortable;
- oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI;
+
+ value = isSet ?
+ fn( value, that[i], i, that ) :
+ that[i];
+
+ isSet = true;
+ i += inc;
+ }
+
+ return value;
+ }
+
+ /**
+ * Add a column to the list used for the table with default values
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nTh The th element for this column
+ * @memberof DataTable#oApi
+ */
+ function _fnAddColumn( oSettings, nTh )
+ {
+ // Add column to aoColumns array
+ var oDefaults = DataTable.defaults.column;
+ var iCol = oSettings.aoColumns.length;
+ var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
+ "nTh": nTh ? nTh : document.createElement('th'),
+ "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
+ "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
+ "mData": oDefaults.mData ? oDefaults.mData : iCol,
+ idx: iCol
+ } );
+ oSettings.aoColumns.push( oCol );
+
+ // Add search object for column specific search. Note that the `searchCols[ iCol ]`
+ // passed into extend can be undefined. This allows the user to give a default
+ // with only some of the parameters defined, and also not give a default
+ var searchCols = oSettings.aoPreSearchCols;
+ searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
+
+ // Use the default column options function to initialise classes etc
+ _fnColumnOptions( oSettings, iCol, $(nTh).data() );
+ }
+
+
+ /**
+ * Apply options for a column
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iCol column index to consider
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnOptions( oSettings, iCol, oOptions )
+ {
+ var oCol = oSettings.aoColumns[ iCol ];
+ var oClasses = oSettings.oClasses;
+ var th = $(oCol.nTh);
+
+ // Try to get width information from the DOM. We can't get it from CSS
+ // as we'd need to parse the CSS stylesheet. `width` option can override
+ if ( ! oCol.sWidthOrig ) {
+ // Width attribute
+ oCol.sWidthOrig = th.attr('width') || null;
+
+ // Style attribute
+ var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
+ if ( t ) {
+ oCol.sWidthOrig = t[1];
}
- else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
+ }
+
+ /* User specified column options */
+ if ( oOptions !== undefined && oOptions !== null )
+ {
+ // Backwards compatibility
+ _fnCompatCols( oOptions );
+
+ // Map camel case parameters to their Hungarian counterparts
+ _fnCamelToHungarian( DataTable.defaults.column, oOptions );
+
+ /* Backwards compatibility for mDataProp */
+ if ( oOptions.mDataProp !== undefined && !oOptions.mData )
{
- oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
- oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
+ oOptions.mData = oOptions.mDataProp;
}
- else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
+
+ if ( oOptions.sType )
{
- oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
- oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
+ oCol._sManualType = oOptions.sType;
}
- }
-
-
- /**
- * Adjust the table column widths for new data. Note: you would probably want to
- * do a redraw after calling this function!
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAdjustColumnSizing ( oSettings )
- {
- /* Not interested in doing column width calculation if auto-width is disabled */
- if ( oSettings.oFeatures.bAutoWidth === false )
+
+ // `class` is a reserved word in Javascript, so we need to provide
+ // the ability to use a valid name for the camel case input
+ if ( oOptions.className && ! oOptions.sClass )
{
- return false;
+ oOptions.sClass = oOptions.className;
}
-
- _fnCalculateColumnWidths( oSettings );
- for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+
+ $.extend( oCol, oOptions );
+ _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
+
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
+ * priority if defined
+ */
+ if ( oOptions.iDataSort !== undefined )
{
- oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
+ oCol.aDataSort = [ oOptions.iDataSort ];
}
+ _fnMap( oCol, oOptions, "aDataSort" );
}
-
-
- /**
- * Covert the index of a visible column to the index in the data array (take account
- * of hidden columns)
- * @param {object} oSettings dataTables settings object
- * @param {int} iMatch Visible column index to lookup
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnVisibleToColumnIndex( oSettings, iMatch )
- {
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
-
- return typeof aiVis[iMatch] === 'number' ?
- aiVis[iMatch] :
- null;
+
+ /* Cache the data get and set functions for speed */
+ var mDataSrc = oCol.mData;
+ var mData = _fnGetObjectDataFn( mDataSrc );
+ var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
+
+ var attrTest = function( src ) {
+ return typeof src === 'string' && src.indexOf('@') !== -1;
+ };
+ oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
+ attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
+ );
+ oCol._setter = null;
+
+ oCol.fnGetData = function (rowData, type, meta) {
+ var innerData = mData( rowData, type, undefined, meta );
+
+ return mRender && type ?
+ mRender( innerData, type, rowData, meta ) :
+ innerData;
+ };
+ oCol.fnSetData = function ( rowData, val, meta ) {
+ return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
+ };
+
+ // Indicate if DataTables should read DOM data as an object or array
+ // Used in _fnGetRowElements
+ if ( typeof mDataSrc !== 'number' ) {
+ oSettings._rowReadObject = true;
}
-
-
- /**
- * Covert the index of an index in the data array and convert it to the visible
- * column index (take account of hidden columns)
- * @param {int} iMatch Column index to lookup
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnColumnIndexToVisible( oSettings, iMatch )
+
+ /* Feature sorting overrides column specific when off */
+ if ( !oSettings.oFeatures.bSort )
{
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
- var iPos = $.inArray( iMatch, aiVis );
-
- return iPos !== -1 ? iPos : null;
+ oCol.bSortable = false;
+ th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
}
-
-
- /**
- * Get the number of visible columns
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the number of visible columns
- * @memberof DataTable#oApi
- */
- function _fnVisbleColumns( oSettings )
+
+ /* Check that the class assignment is correct for sorting */
+ var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
+ var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
+ if ( !oCol.bSortable || (!bAsc && !bDesc) )
{
- return _fnGetColumns( oSettings, 'bVisible' ).length;
+ oCol.sSortingClass = oClasses.sSortableNone;
+ oCol.sSortingClassJUI = "";
}
-
-
- /**
- * Get an array of column indexes that match a given property
- * @param {object} oSettings dataTables settings object
- * @param {string} sParam Parameter in aoColumns to look for - typically
- * bVisible or bSearchable
- * @returns {array} Array of indexes with matched properties
- * @memberof DataTable#oApi
- */
- function _fnGetColumns( oSettings, sParam )
+ else if ( bAsc && !bDesc )
{
- var a = [];
-
- $.map( oSettings.aoColumns, function(val, i) {
- if ( val[sParam] ) {
- a.push( i );
- }
- } );
-
- return a;
+ oCol.sSortingClass = oClasses.sSortableAsc;
+ oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
}
-
-
- /**
- * Get the sort type based on an input string
- * @param {string} sData data we wish to know the type of
- * @returns {string} type (defaults to 'string' if no type can be detected)
- * @memberof DataTable#oApi
- */
- function _fnDetectType( sData )
+ else if ( !bAsc && bDesc )
{
- var aTypes = DataTable.ext.aTypes;
- var iLen = aTypes.length;
-
- for ( var i=0 ; i<iLen ; i++ )
- {
- var sType = aTypes[i]( sData );
- if ( sType !== null )
- {
- return sType;
- }
- }
-
- return 'string';
+ oCol.sSortingClass = oClasses.sSortableDesc;
+ oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
}
-
-
- /**
- * Figure out how to reorder a display list
- * @param {object} oSettings dataTables settings object
- * @returns array {int} aiReturn index list for reordering
- * @memberof DataTable#oApi
- */
- function _fnReOrderIndex ( oSettings, sColumns )
+ else
{
- var aColumns = sColumns.split(',');
- var aiReturn = [];
-
- for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- for ( var j=0 ; j<iLen ; j++ )
- {
- if ( oSettings.aoColumns[i].sName == aColumns[j] )
- {
- aiReturn.push( j );
- break;
- }
- }
- }
-
- return aiReturn;
+ oCol.sSortingClass = oClasses.sSortable;
+ oCol.sSortingClassJUI = oClasses.sSortJUI;
}
-
-
- /**
- * Get the column ordering that DataTables expects
- * @param {object} oSettings dataTables settings object
- * @returns {string} comma separated list of names
- * @memberof DataTable#oApi
- */
- function _fnColumnOrdering ( oSettings )
+ }
+
+
+ /**
+ * Adjust the table column widths for new data. Note: you would probably want to
+ * do a redraw after calling this function!
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAdjustColumnSizing ( settings )
+ {
+ /* Not interested in doing column width calculation if auto-width is disabled */
+ if ( settings.oFeatures.bAutoWidth !== false )
{
- var sNames = '';
- for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- sNames += oSettings.aoColumns[i].sName+',';
- }
- if ( sNames.length == iLen )
+ var columns = settings.aoColumns;
+
+ _fnCalculateColumnWidths( settings );
+ for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
{
- return "";
+ columns[i].nTh.style.width = columns[i].sWidth;
}
- return sNames.slice(0, -1);
}
-
-
- /**
- * Take the column definitions and static columns arrays and calculate how
- * they relate to column indexes. The callback function will then apply the
- * definition found for a column to a suitable configuration object.
- * @param {object} oSettings dataTables settings object
- * @param {array} aoColDefs The aoColumnDefs array that is to be applied
- * @param {array} aoCols The aoColumns array that defines columns individually
- * @param {function} fn Callback function - takes two parameters, the calculated
- * column index and the definition for that column.
- * @memberof DataTable#oApi
- */
- function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
+
+ var scroll = settings.oScroll;
+ if ( scroll.sY !== '' || scroll.sX !== '')
{
- var i, iLen, j, jLen, k, kLen;
-
- // Column definitions with aTargets
- if ( aoColDefs )
- {
- /* Loop over the definitions array - loop in reverse so first instance has priority */
- for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
- {
- /* Each definition can target multiple columns, as it is an array */
- var aTargets = aoColDefs[i].aTargets;
- if ( !$.isArray( aTargets ) )
- {
- _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
- }
-
- for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
- {
- if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
- {
- /* Add columns that we don't yet know about */
- while( oSettings.aoColumns.length <= aTargets[j] )
- {
- _fnAddColumn( oSettings );
- }
-
- /* Integer, basic index */
- fn( aTargets[j], aoColDefs[i] );
+ _fnScrollDraw( settings );
+ }
+
+ _fnCallbackFire( settings, null, 'column-sizing', [settings] );
+ }
+
+
+ /**
+ * Covert the index of a visible column to the index in the data array (take account
+ * of hidden columns)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iMatch Visible column index to lookup
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnVisibleToColumnIndex( oSettings, iMatch )
+ {
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
+
+ return typeof aiVis[iMatch] === 'number' ?
+ aiVis[iMatch] :
+ null;
+ }
+
+
+ /**
+ * Covert the index of an index in the data array and convert it to the visible
+ * column index (take account of hidden columns)
+ * @param {int} iMatch Column index to lookup
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnIndexToVisible( oSettings, iMatch )
+ {
+ var aiVis = _fnGetColumns( oSettings, 'bVisible' );
+ var iPos = $.inArray( iMatch, aiVis );
+
+ return iPos !== -1 ? iPos : null;
+ }
+
+
+ /**
+ * Get the number of visible columns
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the number of visible columns
+ * @memberof DataTable#oApi
+ */
+ function _fnVisbleColumns( oSettings )
+ {
+ return $( _pluck( oSettings.aoColumns, 'nTh' ) ).filter(':visible').length;
+ }
+
+
+ /**
+ * Get an array of column indexes that match a given property
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sParam Parameter in aoColumns to look for - typically
+ * bVisible or bSearchable
+ * @returns {array} Array of indexes with matched properties
+ * @memberof DataTable#oApi
+ */
+ function _fnGetColumns( oSettings, sParam )
+ {
+ var a = [];
+
+ $.map( oSettings.aoColumns, function(val, i) {
+ if ( val[sParam] ) {
+ a.push( i );
+ }
+ } );
+
+ return a;
+ }
+
+
+ /**
+ * Calculate the 'type' of a column
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnTypes ( settings )
+ {
+ var columns = settings.aoColumns;
+ var data = settings.aoData;
+ var types = DataTable.ext.type.detect;
+ var i, ien, j, jen, k, ken;
+ var col, cell, detectedType, cache;
+
+ // For each column, spin over the
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
+ col = columns[i];
+ cache = [];
+
+ if ( ! col.sType && col._sManualType ) {
+ col.sType = col._sManualType;
+ }
+ else if ( ! col.sType ) {
+ for ( j=0, jen=types.length ; j<jen ; j++ ) {
+ for ( k=0, ken=data.length ; k<ken ; k++ ) {
+ // Use a cache array so we only need to get the type data
+ // from the formatter once (when using multiple detectors)
+ if ( cache[k] === undefined ) {
+ cache[k] = _fnGetCellData( settings, k, i, 'type' );
}
- else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
- {
- /* Negative integer, right to left column counting */
- fn( oSettings.aoColumns.length+aTargets[j], aoColDefs[i] );
+
+ detectedType = types[j]( cache[k], settings );
+
+ // If null, then this type can't apply to this column, so
+ // rather than testing all cells, break out. There is an
+ // exception for the last type which is `html`. We need to
+ // scan all rows since it is possible to mix string and HTML
+ // types
+ if ( ! detectedType && j !== types.length-1 ) {
+ break;
}
- else if ( typeof aTargets[j] === 'string' )
- {
- /* Class name matching on TH element */
- for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
- {
- if ( aTargets[j] == "_all" ||
- $(oSettings.aoColumns[k].nTh).hasClass( aTargets[j] ) )
- {
- fn( k, aoColDefs[i] );
- }
- }
+
+ // Only a single match is needed for html type since it is
+ // bottom of the pile and very similar to string
+ if ( detectedType === 'html' ) {
+ break;
}
}
+
+ // Type is valid for all data points in the column - use this
+ // type
+ if ( detectedType ) {
+ col.sType = detectedType;
+ break;
+ }
}
- }
-
- // Statically defined columns array
- if ( aoCols )
- {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
- {
- fn( i, aoCols[i] );
+
+ // Fall back - if no type was detected, always use string
+ if ( ! col.sType ) {
+ col.sType = 'string';
}
}
}
-
- /**
- * Add a data array to the table, creating DOM node etc. This is the parallel to
- * _fnGatherData, but for adding rows from a Javascript source, rather than a
- * DOM source.
- * @param {object} oSettings dataTables settings object
- * @param {array} aData data array to be added
- * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
- * @memberof DataTable#oApi
- */
- function _fnAddData ( oSettings, aDataSupplied )
+ }
+
+
+ /**
+ * Take the column definitions and static columns arrays and calculate how
+ * they relate to column indexes. The callback function will then apply the
+ * definition found for a column to a suitable configuration object.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
+ * @param {array} aoCols The aoColumns array that defines columns individually
+ * @param {function} fn Callback function - takes two parameters, the calculated
+ * column index and the definition for that column.
+ * @memberof DataTable#oApi
+ */
+ function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
+ {
+ var i, iLen, j, jLen, k, kLen, def;
+ var columns = oSettings.aoColumns;
+
+ // Column definitions with aTargets
+ if ( aoColDefs )
{
- var oCol;
-
- /* Take an independent copy of the data source so we can bash it about as we wish */
- var aDataIn = ($.isArray(aDataSupplied)) ?
- aDataSupplied.slice() :
- $.extend( true, {}, aDataSupplied );
-
- /* Create the object for storing information about this new row */
- var iRow = oSettings.aoData.length;
- var oData = $.extend( true, {}, DataTable.models.oRow );
- oData._aData = aDataIn;
- oSettings.aoData.push( oData );
-
- /* Create the cells */
- var nTd, sThisType;
- for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
+ for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
{
- oCol = oSettings.aoColumns[i];
-
- /* Use rendered data for filtering / sorting */
- if ( typeof oCol.fnRender === 'function' && oCol.bUseRendered && oCol.mData !== null )
- {
- _fnSetCellData( oSettings, iRow, i, _fnRender(oSettings, iRow, i) );
- }
- else
+ def = aoColDefs[i];
+
+ /* Each definition can target multiple columns, as it is an array */
+ var aTargets = def.targets !== undefined ?
+ def.targets :
+ def.aTargets;
+
+ if ( ! $.isArray( aTargets ) )
{
- _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
+ aTargets = [ aTargets ];
}
-
- /* See if we should auto-detect the column type */
- if ( oCol._bAutoType && oCol.sType != 'string' )
- {
- /* Attempt to auto detect the type - same as _fnGatherData() */
- var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
- if ( sVarType !== null && sVarType !== '' )
- {
- sThisType = _fnDetectType( sVarType );
- if ( oCol.sType === null )
- {
- oCol.sType = sThisType;
- }
- else if ( oCol.sType != sThisType && oCol.sType != "html" )
- {
- /* String is always the 'fallback' option */
- oCol.sType = 'string';
- }
- }
- }
- }
-
- /* Add to the display array */
- oSettings.aiDisplayMaster.push( iRow );
-
- /* Create the DOM information */
- if ( !oSettings.oFeatures.bDeferRender )
- {
- _fnCreateTr( oSettings, iRow );
- }
-
- return iRow;
- }
-
-
- /**
- * Read in the data from the target table from the DOM
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnGatherData( oSettings )
- {
- var iLoop, i, iLen, j, jLen, jInner,
- nTds, nTrs, nTd, nTr, aLocalData, iThisIndex,
- iRow, iRows, iColumn, iColumns, sNodeName,
- oCol, oData;
-
- /*
- * Process by row first
- * Add the data object for the whole table - storing the tr node. Note - no point in getting
- * DOM based data if we are going to go and replace it with Ajax source data.
- */
- if ( oSettings.bDeferLoading || oSettings.sAjaxSource === null )
- {
- nTr = oSettings.nTBody.firstChild;
- while ( nTr )
+
+ for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
{
- if ( nTr.nodeName.toUpperCase() == "TR" )
+ if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
{
- iThisIndex = oSettings.aoData.length;
- nTr._DT_RowIndex = iThisIndex;
- oSettings.aoData.push( $.extend( true, {}, DataTable.models.oRow, {
- "nTr": nTr
- } ) );
-
- oSettings.aiDisplayMaster.push( iThisIndex );
- nTd = nTr.firstChild;
- jInner = 0;
- while ( nTd )
+ /* Add columns that we don't yet know about */
+ while( columns.length <= aTargets[j] )
{
- sNodeName = nTd.nodeName.toUpperCase();
- if ( sNodeName == "TD" || sNodeName == "TH" )
- {
- _fnSetCellData( oSettings, iThisIndex, jInner, $.trim(nTd.innerHTML) );
- jInner++;
- }
- nTd = nTd.nextSibling;
+ _fnAddColumn( oSettings );
}
+
+ /* Integer, basic index */
+ fn( aTargets[j], def );
}
- nTr = nTr.nextSibling;
- }
- }
-
- /* Gather in the TD elements of the Table - note that this is basically the same as
- * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
- * setup!
- */
- nTrs = _fnGetTrNodes( oSettings );
- nTds = [];
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- nTd = nTrs[i].firstChild;
- while ( nTd )
- {
- sNodeName = nTd.nodeName.toUpperCase();
- if ( sNodeName == "TD" || sNodeName == "TH" )
+ else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
{
- nTds.push( nTd );
+ /* Negative integer, right to left column counting */
+ fn( columns.length+aTargets[j], def );
}
- nTd = nTd.nextSibling;
- }
- }
-
- /* Now process by column */
- for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
- {
- oCol = oSettings.aoColumns[iColumn];
-
- /* Get the title of the column - unless there is a user set one */
- if ( oCol.sTitle === null )
- {
- oCol.sTitle = oCol.nTh.innerHTML;
- }
-
- var
- bAutoType = oCol._bAutoType,
- bRender = typeof oCol.fnRender === 'function',
- bClass = oCol.sClass !== null,
- bVisible = oCol.bVisible,
- nCell, sThisType, sRendered, sValType;
-
- /* A single loop to rule them all (and be more efficient) */
- if ( bAutoType || bRender || bClass || !bVisible )
- {
- for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
+ else if ( typeof aTargets[j] === 'string' )
{
- oData = oSettings.aoData[iRow];
- nCell = nTds[ (iRow*iColumns) + iColumn ];
-
- /* Type detection */
- if ( bAutoType && oCol.sType != 'string' )
+ /* Class name matching on TH element */
+ for ( k=0, kLen=columns.length ; k<kLen ; k++ )
{
- sValType = _fnGetCellData( oSettings, iRow, iColumn, 'type' );
- if ( sValType !== '' )
+ if ( aTargets[j] == "_all" ||
+ $(columns[k].nTh).hasClass( aTargets[j] ) )
{
- sThisType = _fnDetectType( sValType );
- if ( oCol.sType === null )
- {
- oCol.sType = sThisType;
- }
- else if ( oCol.sType != sThisType &&
- oCol.sType != "html" )
- {
- /* String is always the 'fallback' option */
- oCol.sType = 'string';
- }
+ fn( k, def );
}
}
-
- if ( oCol.mRender )
- {
- // mRender has been defined, so we need to get the value and set it
- nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
- }
- else if ( oCol.mData !== iColumn )
- {
- // If mData is not the same as the column number, then we need to
- // get the dev set value. If it is the column, no point in wasting
- // time setting the value that is already there!
- nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
- }
-
- /* Rendering */
- if ( bRender )
- {
- sRendered = _fnRender( oSettings, iRow, iColumn );
- nCell.innerHTML = sRendered;
- if ( oCol.bUseRendered )
- {
- /* Use the rendered data for filtering / sorting */
- _fnSetCellData( oSettings, iRow, iColumn, sRendered );
- }
- }
-
- /* Classes */
- if ( bClass )
- {
- nCell.className += ' '+oCol.sClass;
- }
-
- /* Column visibility */
- if ( !bVisible )
- {
- oData._anHidden[iColumn] = nCell;
- nCell.parentNode.removeChild( nCell );
- }
- else
- {
- oData._anHidden[iColumn] = null;
- }
-
- if ( oCol.fnCreatedCell )
- {
- oCol.fnCreatedCell.call( oSettings.oInstance,
- nCell, _fnGetCellData( oSettings, iRow, iColumn, 'display' ), oData._aData, iRow, iColumn
- );
- }
}
}
}
-
- /* Row created callbacks */
- if ( oSettings.aoRowCreatedCallback.length !== 0 )
+ }
+
+ // Statically defined columns array
+ if ( aoCols )
+ {
+ for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
{
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- oData = oSettings.aoData[i];
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, i] );
- }
+ fn( i, aoCols[i] );
}
}
-
-
- /**
- * Take a TR element and convert it to an index in aoData
- * @param {object} oSettings dataTables settings object
- * @param {node} n the TR element to find
- * @returns {int} index if the node is found, null if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToDataIndex( oSettings, n )
+ }
+
+ /**
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
+ * DOM source.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aData data array to be added
+ * @param {node} [nTr] TR element to add to the table - optional. If not given,
+ * DataTables will create a row automatically
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
+ * if nTr is.
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
+ * @memberof DataTable#oApi
+ */
+ function _fnAddData ( oSettings, aDataIn, nTr, anTds )
+ {
+ /* Create the object for storing information about this new row */
+ var iRow = oSettings.aoData.length;
+ var oData = $.extend( true, {}, DataTable.models.oRow, {
+ src: nTr ? 'dom' : 'data',
+ idx: iRow
+ } );
+
+ oData._aData = aDataIn;
+ oSettings.aoData.push( oData );
+
+ /* Create the cells */
+ var nTd, sThisType;
+ var columns = oSettings.aoColumns;
+
+ // Invalidate the column types as the new data needs to be revalidated
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
{
- return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
+ columns[i].sType = null;
}
-
-
- /**
- * Take a TD element and convert it into a column data index (not the visible index)
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow The row number the TD/TH can be found in
- * @param {node} n The TD/TH element to find
- * @returns {int} index if the node is found, -1 if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToColumnIndex( oSettings, iRow, n )
- {
- var anCells = _fnGetTdNodes( oSettings, iRow );
-
- for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- if ( anCells[i] === n )
- {
- return i;
- }
- }
- return -1;
+
+ /* Add to the display array */
+ oSettings.aiDisplayMaster.push( iRow );
+
+ var id = oSettings.rowIdFn( aDataIn );
+ if ( id !== undefined ) {
+ oSettings.aIds[ id ] = oData;
}
-
-
- /**
- * Get an array of data for a given row from the internal data cache
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData row id
- * @param {string} sSpecific data get type ('type' 'filter' 'sort')
- * @param {array} aiColumns Array of column indexes to get data from
- * @returns {array} Data array
- * @memberof DataTable#oApi
- */
- function _fnGetRowData( oSettings, iRow, sSpecific, aiColumns )
+
+ /* Create the DOM information, or register it if already present */
+ if ( nTr || ! oSettings.oFeatures.bDeferRender )
{
- var out = [];
- for ( var i=0, iLen=aiColumns.length ; i<iLen ; i++ )
- {
- out.push( _fnGetCellData( oSettings, iRow, aiColumns[i], sSpecific ) );
+ _fnCreateTr( oSettings, iRow, nTr, anTds );
+ }
+
+ return iRow;
+ }
+
+
+ /**
+ * Add one or more TR elements to the table. Generally we'd expect to
+ * use this for reading data from a DOM sourced table, but it could be
+ * used for an TR element. Note that if a TR is given, it is used (i.e.
+ * it is not cloned).
+ * @param {object} settings dataTables settings object
+ * @param {array|node|jQuery} trs The TR element(s) to add to the table
+ * @returns {array} Array of indexes for the added rows
+ * @memberof DataTable#oApi
+ */
+ function _fnAddTr( settings, trs )
+ {
+ var row;
+
+ // Allow an individual node to be passed in
+ if ( ! (trs instanceof $) ) {
+ trs = $(trs);
+ }
+
+ return trs.map( function (i, el) {
+ row = _fnGetRowElements( settings, el );
+ return _fnAddData( settings, row.data, el, row.cells );
+ } );
+ }
+
+
+ /**
+ * Take a TR element and convert it to an index in aoData
+ * @param {object} oSettings dataTables settings object
+ * @param {node} n the TR element to find
+ * @returns {int} index if the node is found, null if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToDataIndex( oSettings, n )
+ {
+ return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
+ }
+
+
+ /**
+ * Take a TD element and convert it into a column data index (not the visible index)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow The row number the TD/TH can be found in
+ * @param {node} n The TD/TH element to find
+ * @returns {int} index if the node is found, -1 if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToColumnIndex( oSettings, iRow, n )
+ {
+ return $.inArray( n, oSettings.aoData[ iRow ].anCells );
+ }
+
+
+ /**
+ * Get the data for a given cell from the internal cache, taking into account data mapping
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
+ * @param {string} type data get type ('display', 'type' 'filter' 'sort')
+ * @returns {*} Cell data
+ * @memberof DataTable#oApi
+ */
+ function _fnGetCellData( settings, rowIdx, colIdx, type )
+ {
+ var draw = settings.iDraw;
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
+ var defaultContent = col.sDefaultContent;
+ var cellData = col.fnGetData( rowData, type, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ } );
+
+ if ( cellData === undefined ) {
+ if ( settings.iDrawError != draw && defaultContent === null ) {
+ _fnLog( settings, 0, "Requested unknown parameter "+
+ (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
+ " for row "+rowIdx+", column "+colIdx, 4 );
+ settings.iDrawError = draw;
}
- return out;
+ return defaultContent;
}
-
-
- /**
- * Get the data for a given cell from the internal cache, taking into account data mapping
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData row id
- * @param {int} iCol Column index
- * @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
- * @returns {*} Cell data
- * @memberof DataTable#oApi
- */
- function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
+
+ // When the data source is null and a specific data type is requested (i.e.
+ // not the original data), we can use default column data
+ if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
+ cellData = defaultContent;
+ }
+ else if ( typeof cellData === 'function' ) {
+ // If the data source is a function, then we run it and use the return,
+ // executing in the scope of the data object (for instances)
+ return cellData.call( rowData );
+ }
+
+ if ( cellData === null && type == 'display' ) {
+ return '';
+ }
+ return cellData;
+ }
+
+
+ /**
+ * Set the value for a specific cell, into the internal data cache
+ * @param {object} settings dataTables settings object
+ * @param {int} rowIdx aoData row id
+ * @param {int} colIdx Column index
+ * @param {*} val Value to set
+ * @memberof DataTable#oApi
+ */
+ function _fnSetCellData( settings, rowIdx, colIdx, val )
+ {
+ var col = settings.aoColumns[colIdx];
+ var rowData = settings.aoData[rowIdx]._aData;
+
+ col.fnSetData( rowData, val, {
+ settings: settings,
+ row: rowIdx,
+ col: colIdx
+ } );
+ }
+
+
+ // Private variable that is used to match action syntax in the data property object
+ var __reArray = /\[.*?\]$/;
+ var __reFn = /\(\)$/;
+
+ /**
+ * Split string on periods, taking into account escaped periods
+ * @param {string} str String to split
+ * @return {array} Split string
+ */
+ function _fnSplitObjNotation( str )
+ {
+ return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
+ return s.replace(/\\./g, '.');
+ } );
+ }
+
+
+ /**
+ * Return a function that can be used to get data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data get function
+ * @memberof DataTable#oApi
+ */
+ function _fnGetObjectDataFn( mSource )
+ {
+ if ( $.isPlainObject( mSource ) )
{
- var sData;
- var oCol = oSettings.aoColumns[iCol];
- var oData = oSettings.aoData[iRow]._aData;
-
- if ( (sData=oCol.fnGetData( oData, sSpecific )) === undefined )
- {
- if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
- {
- _fnLog( oSettings, 0, "Requested unknown parameter "+
- (typeof oCol.mData=='function' ? '{mData function}' : "'"+oCol.mData+"'")+
- " from the data source for row "+iRow );
- oSettings.iDrawError = oSettings.iDraw;
+ /* Build an object of get functions, and wrap them in a single call */
+ var o = {};
+ $.each( mSource, function (key, val) {
+ if ( val ) {
+ o[key] = _fnGetObjectDataFn( val );
}
- return oCol.sDefaultContent;
- }
-
- /* When the data source is null, we can use default column data */
- if ( sData === null && oCol.sDefaultContent !== null )
- {
- sData = oCol.sDefaultContent;
- }
- else if ( typeof sData === 'function' )
- {
- /* If the data source is a function, then we run it and use the return */
- return sData();
- }
-
- if ( sSpecific == 'display' && sData === null )
- {
- return '';
- }
- return sData;
+ } );
+
+ return function (data, type, row, meta) {
+ var t = o[type] || o._;
+ return t !== undefined ?
+ t(data, type, row, meta) :
+ data;
+ };
}
-
-
- /**
- * Set the value for a specific cell, into the internal data cache
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData row id
- * @param {int} iCol Column index
- * @param {*} val Value to set
- * @memberof DataTable#oApi
- */
- function _fnSetCellData( oSettings, iRow, iCol, val )
+ else if ( mSource === null )
{
- var oCol = oSettings.aoColumns[iCol];
- var oData = oSettings.aoData[iRow]._aData;
-
- oCol.fnSetData( oData, val );
+ /* Give an empty string for rendering / sorting etc */
+ return function (data) { // type, row and meta also passed, but not used
+ return data;
+ };
}
-
-
- // Private variable that is used to match array syntax in the data property object
- var __reArray = /\[.*?\]$/;
-
- /**
- * Return a function that can be used to get data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data get function
- * @memberof DataTable#oApi
- */
- function _fnGetObjectDataFn( mSource )
+ else if ( typeof mSource === 'function' )
{
- if ( mSource === null )
- {
- /* Give an empty string for rendering / sorting etc */
- return function (data, type) {
- return null;
- };
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, type, extra) {
- return mSource( data, type, extra );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
- {
- /* If there is a . in the source string then the data source is in a
- * nested object so we loop over the data for each level to get the next
- * level down. On each loop we test for undefined, and if found immediately
- * return. This allows entire objects to be missing and sDefaultContent to
- * be used if defined, rather than throwing an error
- */
- var fetchData = function (data, type, src) {
- var a = src.split('.');
- var arrayNotation, out, innerSrc;
-
- if ( src !== "" )
+ return function (data, type, row, meta) {
+ return mSource( data, type, row, meta );
+ };
+ }
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
+ {
+ /* If there is a . in the source string then the data source is in a
+ * nested object so we loop over the data for each level to get the next
+ * level down. On each loop we test for undefined, and if found immediately
+ * return. This allows entire objects to be missing and sDefaultContent to
+ * be used if defined, rather than throwing an error
+ */
+ var fetchData = function (data, type, src) {
+ var arrayNotation, funcNotation, out, innerSrc;
+
+ if ( src !== "" )
+ {
+ var a = _fnSplitObjNotation( src );
+
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
{
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
+ // Check if we are dealing with special notation
+ arrayNotation = a[i].match(__reArray);
+ funcNotation = a[i].match(__reFn);
+
+ if ( arrayNotation )
{
- // Check if we are dealing with an array notation request
- arrayNotation = a[i].match(__reArray);
-
- if ( arrayNotation ) {
- a[i] = a[i].replace(__reArray, '');
-
- // Condition allows simply [] to be passed in
- if ( a[i] !== "" ) {
- data = data[ a[i] ];
- }
- out = [];
-
- // Get the remainder of the nested object to get
- a.splice( 0, i+1 );
- innerSrc = a.join('.');
-
- // Traverse each entry in the array getting the properties requested
+ // Array notation
+ a[i] = a[i].replace(__reArray, '');
+
+ // Condition allows simply [] to be passed in
+ if ( a[i] !== "" ) {
+ data = data[ a[i] ];
+ }
+ out = [];
+
+ // Get the remainder of the nested object to get
+ a.splice( 0, i+1 );
+ innerSrc = a.join('.');
+
+ // Traverse each entry in the array getting the properties requested
+ if ( $.isArray( data ) ) {
for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
out.push( fetchData( data[j], type, innerSrc ) );
}
-
- // If a string is given in between the array notation indicators, that
- // is used to join the strings together, otherwise an array is returned
- var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
- data = (join==="") ? out : out.join(join);
-
- // The inner call to fetchData has already traversed through the remainder
- // of the source requested, so we exit from the loop
- break;
- }
-
- if ( data === null || data[ a[i] ] === undefined )
- {
- return undefined;
}
- data = data[ a[i] ];
+
+ // If a string is given in between the array notation indicators, that
+ // is used to join the strings together, otherwise an array is returned
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
+ data = (join==="") ? out : out.join(join);
+
+ // The inner call to fetchData has already traversed through the remainder
+ // of the source requested, so we exit from the loop
+ break;
}
- }
-
- return data;
- };
-
- return function (data, type) {
- return fetchData( data, type, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, type) {
- return data[mSource];
- };
- }
- }
-
-
- /**
- * Return a function that can be used to set data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data set function
- * @memberof DataTable#oApi
- */
- function _fnSetObjectDataFn( mSource )
- {
- if ( mSource === null )
- {
- /* Nothing to do when the data source is null */
- return function (data, val) {};
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, val) {
- mSource( data, 'set', val );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
- {
- /* Like the get, we need to get data from a nested object */
- var setData = function (data, val, src) {
- var a = src.split('.'), b;
- var arrayNotation, o, innerSrc;
-
- for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
- {
- // Check if we are dealing with an array notation request
- arrayNotation = a[i].match(__reArray);
-
- if ( arrayNotation )
+ else if ( funcNotation )
{
- a[i] = a[i].replace(__reArray, '');
- data[ a[i] ] = [];
-
- // Get the remainder of the nested object to set so we can recurse
- b = a.slice();
- b.splice( 0, i+1 );
- innerSrc = b.join('.');
-
- // Traverse each entry in the array setting the properties requested
- for ( var j=0, jLen=val.length ; j<jLen ; j++ )
- {
- o = {};
- setData( o, val[j], innerSrc );
- data[ a[i] ].push( o );
- }
-
- // The inner call to setData has already traversed through the remainder
- // of the source and has set the data, thus we can exit here
- return;
+ // Function call
+ a[i] = a[i].replace(__reFn, '');
+ data = data[ a[i] ]();
+ continue;
}
-
- // If the nested object doesn't currently exist - since we are
- // trying to set the value - create it
- if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
+
+ if ( data === null || data[ a[i] ] === undefined )
{
- data[ a[i] ] = {};
+ return undefined;
}
data = data[ a[i] ];
}
-
- // If array notation is used, we just want to strip it and use the property name
- // and assign the value. If it isn't used, then we get the result we want anyway
- data[ a[a.length-1].replace(__reArray, '') ] = val;
- };
-
- return function (data, val) {
- return setData( data, val, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, val) {
- data[mSource] = val;
- };
- }
- }
-
-
- /**
- * Return an array with the full table data
- * @param {object} oSettings dataTables settings object
- * @returns array {array} aData Master data array
- * @memberof DataTable#oApi
- */
- function _fnGetDataMaster ( oSettings )
- {
- var aData = [];
- var iLen = oSettings.aoData.length;
- for ( var i=0 ; i<iLen; i++ )
- {
- aData.push( oSettings.aoData[i]._aData );
- }
- return aData;
+ }
+
+ return data;
+ };
+
+ return function (data, type) { // row and meta also passed, but not used
+ return fetchData( data, type, mSource );
+ };
}
-
-
- /**
- * Nuke the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnClearTable( oSettings )
+ else
{
- oSettings.aoData.splice( 0, oSettings.aoData.length );
- oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
- oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
- _fnCalculateEnd( oSettings );
+ /* Array or flat object mapping */
+ return function (data, type) { // row and meta also passed, but not used
+ return data[mSource];
+ };
}
-
-
- /**
- * Take an array of integers (index array) and remove a target integer (value - not
- * the key!)
- * @param {array} a Index array to target
- * @param {int} iTarget value to find
- * @memberof DataTable#oApi
- */
- function _fnDeleteIndex( a, iTarget )
+ }
+
+
+ /**
+ * Return a function that can be used to set data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data set function
+ * @memberof DataTable#oApi
+ */
+ function _fnSetObjectDataFn( mSource )
+ {
+ if ( $.isPlainObject( mSource ) )
{
- var iTargetIndex = -1;
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- if ( a[i] == iTarget )
- {
- iTargetIndex = i;
- }
- else if ( a[i] > iTarget )
- {
- a[i]--;
- }
- }
-
- if ( iTargetIndex != -1 )
- {
- a.splice( iTargetIndex, 1 );
- }
+ /* Unlike get, only the underscore (global) option is used for for
+ * setting data since we don't know the type here. This is why an object
+ * option is not documented for `mData` (which is read/write), but it is
+ * for `mRender` which is read only.
+ */
+ return _fnSetObjectDataFn( mSource._ );
}
-
-
- /**
- * Call the developer defined fnRender function for a given cell (row/column) with
- * the required parameters and return the result.
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow aoData index for the row
- * @param {int} iCol aoColumns index for the column
- * @returns {*} Return of the developer's fnRender function
- * @memberof DataTable#oApi
- */
- function _fnRender( oSettings, iRow, iCol )
+ else if ( mSource === null )
{
- var oCol = oSettings.aoColumns[iCol];
-
- return oCol.fnRender( {
- "iDataRow": iRow,
- "iDataColumn": iCol,
- "oSettings": oSettings,
- "aData": oSettings.aoData[iRow]._aData,
- "mDataProp": oCol.mData
- }, _fnGetCellData(oSettings, iRow, iCol, 'display') );
+ /* Nothing to do when the data source is null */
+ return function () {};
}
- /**
- * Create a new TR element (and it's TD children) for a row
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow Row to consider
- * @memberof DataTable#oApi
- */
- function _fnCreateTr ( oSettings, iRow )
+ else if ( typeof mSource === 'function' )
{
- var oData = oSettings.aoData[iRow];
- var nTd;
-
- if ( oData.nTr === null )
- {
- oData.nTr = document.createElement('tr');
-
- /* Use a private property on the node to allow reserve mapping from the node
- * to the aoData array for fast look up
- */
- oData.nTr._DT_RowIndex = iRow;
-
- /* Special parameters can be given by the data source to be used on the row */
- if ( oData._aData.DT_RowId )
- {
- oData.nTr.id = oData._aData.DT_RowId;
- }
-
- if ( oData._aData.DT_RowClass )
- {
- oData.nTr.className = oData._aData.DT_RowClass;
- }
-
- /* Process each column */
- for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- var oCol = oSettings.aoColumns[i];
- nTd = document.createElement( oCol.sCellType );
-
- /* Render if needed - if bUseRendered is true then we already have the rendered
- * value in the data source - so can just use that
- */
- nTd.innerHTML = (typeof oCol.fnRender === 'function' && (!oCol.bUseRendered || oCol.mData === null)) ?
- _fnRender( oSettings, iRow, i ) :
- _fnGetCellData( oSettings, iRow, i, 'display' );
-
- /* Add user defined class */
- if ( oCol.sClass !== null )
- {
- nTd.className = oCol.sClass;
- }
-
- if ( oCol.bVisible )
- {
- oData.nTr.appendChild( nTd );
- oData._anHidden[i] = null;
- }
- else
- {
- oData._anHidden[i] = nTd;
- }
-
- if ( oCol.fnCreatedCell )
- {
- oCol.fnCreatedCell.call( oSettings.oInstance,
- nTd, _fnGetCellData( oSettings, iRow, i, 'display' ), oData._aData, iRow, i
- );
- }
- }
-
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, iRow] );
- }
+ return function (data, val, meta) {
+ mSource( data, 'set', val, meta );
+ };
}
-
-
- /**
- * Create the HTML header for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBuildHead( oSettings )
+ else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
+ mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
{
- var i, nTh, iLen, j, jLen;
- var iThs = $('th, td', oSettings.nTHead).length;
- var iCorrector = 0;
- var jqChildren;
-
- /* If there is a header in place - then use it - otherwise it's going to get nuked... */
- if ( iThs !== 0 )
- {
- /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ /* Like the get, we need to get data from a nested object */
+ var setData = function (data, val, src) {
+ var a = _fnSplitObjNotation( src ), b;
+ var aLast = a[a.length-1];
+ var arrayNotation, funcNotation, o, innerSrc;
+
+ for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
{
- nTh = oSettings.aoColumns[i].nTh;
- nTh.setAttribute('role', 'columnheader');
- if ( oSettings.aoColumns[i].bSortable )
- {
- nTh.setAttribute('tabindex', oSettings.iTabIndex);
- nTh.setAttribute('aria-controls', oSettings.sTableId);
- }
-
- if ( oSettings.aoColumns[i].sClass !== null )
+ // Check if we are dealing with an array notation request
+ arrayNotation = a[i].match(__reArray);
+ funcNotation = a[i].match(__reFn);
+
+ if ( arrayNotation )
{
- $(nTh).addClass( oSettings.aoColumns[i].sClass );
+ a[i] = a[i].replace(__reArray, '');
+ data[ a[i] ] = [];
+
+ // Get the remainder of the nested object to set so we can recurse
+ b = a.slice();
+ b.splice( 0, i+1 );
+ innerSrc = b.join('.');
+
+ // Traverse each entry in the array setting the properties requested
+ if ( $.isArray( val ) )
+ {
+ for ( var j=0, jLen=val.length ; j<jLen ; j++ )
+ {
+ o = {};
+ setData( o, val[j], innerSrc );
+ data[ a[i] ].push( o );
+ }
+ }
+ else
+ {
+ // We've been asked to save data to an array, but it
+ // isn't array data to be saved. Best that can be done
+ // is to just save the value.
+ data[ a[i] ] = val;
+ }
+
+ // The inner call to setData has already traversed through the remainder
+ // of the source and has set the data, thus we can exit here
+ return;
}
-
- /* Set the title of the column if it is user defined (not what was auto detected) */
- if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
+ else if ( funcNotation )
{
- nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+ // Function call
+ a[i] = a[i].replace(__reFn, '');
+ data = data[ a[i] ]( val );
}
- }
- }
- else
- {
- /* We don't have a header in the DOM - so we are going to have to create one */
- var nTr = document.createElement( "tr" );
-
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- nTh = oSettings.aoColumns[i].nTh;
- nTh.innerHTML = oSettings.aoColumns[i].sTitle;
- nTh.setAttribute('tabindex', '0');
-
- if ( oSettings.aoColumns[i].sClass !== null )
+
+ // If the nested object doesn't currently exist - since we are
+ // trying to set the value - create it
+ if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
{
- $(nTh).addClass( oSettings.aoColumns[i].sClass );
+ data[ a[i] ] = {};
}
-
- nTr.appendChild( nTh );
+ data = data[ a[i] ];
}
- $(oSettings.nTHead).html( '' )[0].appendChild( nTr );
- _fnDetectHeader( oSettings.aoHeader, oSettings.nTHead );
- }
-
- /* ARIA role for the rows */
- $(oSettings.nTHead).children('tr').attr('role', 'row');
-
- /* Add the extra markup needed by jQuery UI's themes */
- if ( oSettings.bJUI )
- {
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+
+ // Last item in the input - i.e, the actual set
+ if ( aLast.match(__reFn ) )
{
- nTh = oSettings.aoColumns[i].nTh;
-
- var nDiv = document.createElement('div');
- nDiv.className = oSettings.oClasses.sSortJUIWrapper;
- $(nTh).contents().appendTo(nDiv);
-
- var nSpan = document.createElement('span');
- nSpan.className = oSettings.oClasses.sSortIcon;
- nDiv.appendChild( nSpan );
- nTh.appendChild( nDiv );
+ // Function call
+ data = data[ aLast.replace(__reFn, '') ]( val );
}
- }
-
- if ( oSettings.oFeatures.bSort )
- {
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
+ else
{
- if ( oSettings.aoColumns[i].bSortable !== false )
- {
- _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
- }
- else
- {
- $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
- }
+ // If array notation is used, we just want to strip it and use the property name
+ // and assign the value. If it isn't used, then we get the result we want anyway
+ data[ aLast.replace(__reArray, '') ] = val;
}
- }
-
- /* Deal with the footer - add classes if required */
- if ( oSettings.oClasses.sFooterTH !== "" )
+ };
+
+ return function (data, val) { // meta is also passed in, but not used
+ return setData( data, val, mSource );
+ };
+ }
+ else
+ {
+ /* Array or flat object mapping */
+ return function (data, val) { // meta is also passed in, but not used
+ data[mSource] = val;
+ };
+ }
+ }
+
+
+ /**
+ * Return an array with the full table data
+ * @param {object} oSettings dataTables settings object
+ * @returns array {array} aData Master data array
+ * @memberof DataTable#oApi
+ */
+ function _fnGetDataMaster ( settings )
+ {
+ return _pluck( settings.aoData, '_aData' );
+ }
+
+
+ /**
+ * Nuke the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnClearTable( settings )
+ {
+ settings.aoData.length = 0;
+ settings.aiDisplayMaster.length = 0;
+ settings.aiDisplay.length = 0;
+ settings.aIds = {};
+ }
+
+
+ /**
+ * Take an array of integers (index array) and remove a target integer (value - not
+ * the key!)
+ * @param {array} a Index array to target
+ * @param {int} iTarget value to find
+ * @memberof DataTable#oApi
+ */
+ function _fnDeleteIndex( a, iTarget, splice )
+ {
+ var iTargetIndex = -1;
+
+ for ( var i=0, iLen=a.length ; i<iLen ; i++ )
+ {
+ if ( a[i] == iTarget )
{
- $(oSettings.nTFoot).children('tr').children('th').addClass( oSettings.oClasses.sFooterTH );
+ iTargetIndex = i;
}
-
- /* Cache the footer elements */
- if ( oSettings.nTFoot !== null )
+ else if ( a[i] > iTarget )
{
- var anCells = _fnGetUniqueThs( oSettings, null, oSettings.aoFooter );
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- if ( anCells[i] )
- {
- oSettings.aoColumns[i].nTf = anCells[i];
- if ( oSettings.aoColumns[i].sClass )
- {
- $(anCells[i]).addClass( oSettings.aoColumns[i].sClass );
- }
- }
- }
+ a[i]--;
}
}
-
-
- /**
- * Draw the header (or footer) element based on the column visibility states. The
- * methodology here is to use the layout array from _fnDetectHeader, modified for
- * the instantaneous column visibility, to construct the new layout. The grid is
- * traversed over cell at a time in a rows x columns grid fashion, although each
- * cell insert can cover multiple elements in the grid - which is tracks using the
- * aApplied array. Cell inserts in the grid will only occur where there isn't
- * already a cell in that position.
- * @param {object} oSettings dataTables settings object
- * @param array {objects} aoSource Layout array from _fnDetectHeader
- * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
- * @memberof DataTable#oApi
- */
- function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
+
+ if ( iTargetIndex != -1 && splice === undefined )
{
- var i, iLen, j, jLen, k, kLen, n, nLocalTr;
- var aoLocal = [];
- var aApplied = [];
- var iColumns = oSettings.aoColumns.length;
- var iRowspan, iColspan;
-
- if ( bIncludeHidden === undefined )
- {
- bIncludeHidden = false;
- }
-
- /* Make a copy of the master layout array, but without the visible columns in it */
- for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
- {
- aoLocal[i] = aoSource[i].slice();
- aoLocal[i].nTr = aoSource[i].nTr;
-
- /* Remove any columns which are currently hidden */
- for ( j=iColumns-1 ; j>=0 ; j-- )
- {
- if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
- {
- aoLocal[i].splice( j, 1 );
- }
- }
-
- /* Prep the applied array - it needs an element for each row */
- aApplied.push( [] );
+ a.splice( iTargetIndex, 1 );
+ }
+ }
+
+
+ /**
+ * Mark cached data as invalid such that a re-read of the data will occur when
+ * the cached data is next requested. Also update from the data source object.
+ *
+ * @param {object} settings DataTables settings object
+ * @param {int} rowIdx Row index to invalidate
+ * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
+ * or 'data'
+ * @param {int} [colIdx] Column index to invalidate. If undefined the whole
+ * row will be invalidated
+ * @memberof DataTable#oApi
+ *
+ * @todo For the modularisation of v1.11 this will need to become a callback, so
+ * the sort and filter methods can subscribe to it. That will required
+ * initialisation options for sorting, which is why it is not already baked in
+ */
+ function _fnInvalidate( settings, rowIdx, src, colIdx )
+ {
+ var row = settings.aoData[ rowIdx ];
+ var i, ien;
+ var cellWrite = function ( cell, col ) {
+ // This is very frustrating, but in IE if you just write directly
+ // to innerHTML, and elements that are overwritten are GC'ed,
+ // even if there is a reference to them elsewhere
+ while ( cell.childNodes.length ) {
+ cell.removeChild( cell.firstChild );
}
-
- for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
- {
- nLocalTr = aoLocal[i].nTr;
-
- /* All cells are going to be replaced, so empty out the row */
- if ( nLocalTr )
- {
- while( (n = nLocalTr.firstChild) )
- {
- nLocalTr.removeChild( n );
- }
+
+ cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
+ };
+
+ // Are we reading last data from DOM or the data object?
+ if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
+ // Read the data from the DOM
+ row._aData = _fnGetRowElements(
+ settings, row, colIdx, colIdx === undefined ? undefined : row._aData
+ )
+ .data;
+ }
+ else {
+ // Reading from data object, update the DOM
+ var cells = row.anCells;
+
+ if ( cells ) {
+ if ( colIdx !== undefined ) {
+ cellWrite( cells[colIdx], colIdx );
}
-
- for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
- {
- iRowspan = 1;
- iColspan = 1;
-
- /* Check to see if there is already a cell (row/colspan) covering our target
- * insert point. If there is, then there is nothing to do.
- */
- if ( aApplied[i][j] === undefined )
- {
- nLocalTr.appendChild( aoLocal[i][j].cell );
- aApplied[i][j] = 1;
-
- /* Expand the cell to cover as many rows as needed */
- while ( aoLocal[i+iRowspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
- {
- aApplied[i+iRowspan][j] = 1;
- iRowspan++;
- }
-
- /* Expand the cell to cover as many columns as needed */
- while ( aoLocal[i][j+iColspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
- {
- /* Must update the applied array over the rows for the columns */
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aApplied[i+k][j+iColspan] = 1;
- }
- iColspan++;
- }
-
- /* Do the actual expansion in the DOM */
- aoLocal[i][j].cell.rowSpan = iRowspan;
- aoLocal[i][j].cell.colSpan = iColspan;
+ else {
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
+ cellWrite( cells[i], i );
}
}
}
}
-
-
- /**
- * Insert the required TR nodes into the table for display
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnDraw( oSettings )
- {
- /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
- var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
- if ( $.inArray( false, aPreDraw ) !== -1 )
- {
- _fnProcessingDisplay( oSettings, false );
- return;
+
+ // For both row and cell invalidation, the cached data for sorting and
+ // filtering is nulled out
+ row._aSortData = null;
+ row._aFilterData = null;
+
+ // Invalidate the type for a specific column (if given) or all columns since
+ // the data might have changed
+ var cols = settings.aoColumns;
+ if ( colIdx !== undefined ) {
+ cols[ colIdx ].sType = null;
+ }
+ else {
+ for ( i=0, ien=cols.length ; i<ien ; i++ ) {
+ cols[i].sType = null;
}
-
- var i, iLen, n;
- var anRows = [];
- var iRowCount = 0;
- var iStripes = oSettings.asStripeClasses.length;
- var iOpenRows = oSettings.aoOpenRows.length;
-
- oSettings.bDrawing = true;
-
- /* Check and see if we have an initial draw position from state saving */
- if ( oSettings.iInitDisplayStart !== undefined && oSettings.iInitDisplayStart != -1 )
- {
- if ( oSettings.oFeatures.bServerSide )
- {
- oSettings._iDisplayStart = oSettings.iInitDisplayStart;
- }
- else
- {
- oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
- 0 : oSettings.iInitDisplayStart;
+
+ // Update DataTables special `DT_*` attributes for the row
+ _fnRowAttributes( settings, row );
+ }
+ }
+
+
+ /**
+ * Build a data source object from an HTML row, reading the contents of the
+ * cells that are in the row.
+ *
+ * @param {object} settings DataTables settings object
+ * @param {node|object} TR element from which to read data or existing row
+ * object from which to re-read the data from the cells
+ * @param {int} [colIdx] Optional column index
+ * @param {array|object} [d] Data source object. If `colIdx` is given then this
+ * parameter should also be given and will be used to write the data into.
+ * Only the column in question will be written
+ * @returns {object} Object with two parameters: `data` the data read, in
+ * document order, and `cells` and array of nodes (they can be useful to the
+ * caller, so rather than needing a second traversal to get them, just return
+ * them from here).
+ * @memberof DataTable#oApi
+ */
+ function _fnGetRowElements( settings, row, colIdx, d )
+ {
+ var
+ tds = [],
+ td = row.firstChild,
+ name, col, o, i=0, contents,
+ columns = settings.aoColumns,
+ objectRead = settings._rowReadObject;
+
+ // Allow the data object to be passed in, or construct
+ d = d !== undefined ?
+ d :
+ objectRead ?
+ {} :
+ [];
+
+ var attr = function ( str, td ) {
+ if ( typeof str === 'string' ) {
+ var idx = str.indexOf('@');
+
+ if ( idx !== -1 ) {
+ var attr = str.substring( idx+1 );
+ var setter = _fnSetObjectDataFn( str );
+ setter( d, td.getAttribute( attr ) );
}
- oSettings.iInitDisplayStart = -1;
- _fnCalculateEnd( oSettings );
}
-
- /* Server-side processing draw intercept */
- if ( oSettings.bDeferLoading )
- {
- oSettings.bDeferLoading = false;
- oSettings.iDraw++;
- }
- else if ( !oSettings.oFeatures.bServerSide )
- {
- oSettings.iDraw++;
- }
- else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
- {
- return;
- }
-
- if ( oSettings.aiDisplay.length !== 0 )
- {
- var iStart = oSettings._iDisplayStart;
- var iEnd = oSettings._iDisplayEnd;
-
- if ( oSettings.oFeatures.bServerSide )
- {
- iStart = 0;
- iEnd = oSettings.aoData.length;
- }
-
- for ( var j=iStart ; j<iEnd ; j++ )
- {
- var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
- if ( aoData.nTr === null )
- {
- _fnCreateTr( oSettings, oSettings.aiDisplay[j] );
- }
-
- var nRow = aoData.nTr;
-
- /* Remove the old striping classes and then add the new one */
- if ( iStripes !== 0 )
- {
- var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ];
- if ( aoData._sRowStripe != sStripe )
- {
- $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
- aoData._sRowStripe = sStripe;
- }
- }
-
- /* Row callback functions - might want to manipulate the row */
- _fnCallbackFire( oSettings, 'aoRowCallback', null,
- [nRow, oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j] );
-
- anRows.push( nRow );
- iRowCount++;
-
- /* If there is an open row - and it is attached to this parent - attach it on redraw */
- if ( iOpenRows !== 0 )
- {
- for ( var k=0 ; k<iOpenRows ; k++ )
- {
- if ( nRow == oSettings.aoOpenRows[k].nParent )
- {
- anRows.push( oSettings.aoOpenRows[k].nTr );
- break;
- }
+ };
+
+ // Read data from a cell and store into the data object
+ var cellProcess = function ( cell ) {
+ if ( colIdx === undefined || colIdx === i ) {
+ col = columns[i];
+ contents = $.trim(cell.innerHTML);
+
+ if ( col && col._bAttrSrc ) {
+ var setter = _fnSetObjectDataFn( col.mData._ );
+ setter( d, contents );
+
+ attr( col.mData.sort, cell );
+ attr( col.mData.type, cell );
+ attr( col.mData.filter, cell );
+ }
+ else {
+ // Depending on the `data` option for the columns the data can
+ // be read to either an object or an array.
+ if ( objectRead ) {
+ if ( ! col._setter ) {
+ // Cache the setter function
+ col._setter = _fnSetObjectDataFn( col.mData );
}
+ col._setter( d, contents );
}
- }
- }
- else
- {
- /* Table is empty - create a row with an empty message in it */
- anRows[ 0 ] = document.createElement( 'tr' );
-
- if ( oSettings.asStripeClasses[0] )
- {
- anRows[ 0 ].className = oSettings.asStripeClasses[0];
- }
-
- var oLang = oSettings.oLanguage;
- var sZero = oLang.sZeroRecords;
- if ( oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
- {
- sZero = oLang.sLoadingRecords;
- }
- else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
- {
- sZero = oLang.sEmptyTable;
- }
-
- var nTd = document.createElement( 'td' );
- nTd.setAttribute( 'valign', "top" );
- nTd.colSpan = _fnVisbleColumns( oSettings );
- nTd.className = oSettings.oClasses.sRowEmpty;
- nTd.innerHTML = _fnInfoMacros( oSettings, sZero );
-
- anRows[ iRowCount ].appendChild( nTd );
- }
-
- /* Header and footer callbacks */
- _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
- _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
-
- _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
- _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
-
- /*
- * Need to remove any old row from the display - note we can't just empty the tbody using
- * $().html('') since this will unbind the jQuery event handlers (even although the node
- * still exists!) - equally we can't use innerHTML, since IE throws an exception.
- */
- var
- nAddFrag = document.createDocumentFragment(),
- nRemoveFrag = document.createDocumentFragment(),
- nBodyPar, nTrs;
-
- if ( oSettings.nTBody )
- {
- nBodyPar = oSettings.nTBody.parentNode;
- nRemoveFrag.appendChild( oSettings.nTBody );
-
- /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
- * up. When not infinite scroll, always do it.
- */
- if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
- oSettings.bSorted || oSettings.bFiltered )
- {
- while( (n = oSettings.nTBody.firstChild) )
- {
- oSettings.nTBody.removeChild( n );
+ else {
+ d[i] = contents;
}
}
-
- /* Put the draw table into the dom */
- for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
- {
- nAddFrag.appendChild( anRows[i] );
- }
-
- oSettings.nTBody.appendChild( nAddFrag );
- if ( nBodyPar !== null )
- {
- nBodyPar.appendChild( oSettings.nTBody );
- }
}
-
- /* Call all required callback functions for the end of a draw */
- _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
-
- /* Draw is complete, sorting and filtering must be as well */
- oSettings.bSorted = false;
- oSettings.bFiltered = false;
- oSettings.bDrawing = false;
-
- if ( oSettings.oFeatures.bServerSide )
- {
- _fnProcessingDisplay( oSettings, false );
- if ( !oSettings._bInitComplete )
- {
- _fnInitComplete( oSettings );
+
+ i++;
+ };
+
+ if ( td ) {
+ // `tr` element was passed in
+ while ( td ) {
+ name = td.nodeName.toUpperCase();
+
+ if ( name == "TD" || name == "TH" ) {
+ cellProcess( td );
+ tds.push( td );
}
+
+ td = td.nextSibling;
}
}
-
-
- /**
- * Redraw the table - taking account of the various features which are enabled
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnReDraw( oSettings )
- {
- if ( oSettings.oFeatures.bSort )
- {
- /* Sorting will refilter and draw for us */
- _fnSort( oSettings, oSettings.oPreviousSearch );
- }
- else if ( oSettings.oFeatures.bFilter )
- {
- /* Filtering will redraw for us */
- _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
+ else {
+ // Existing row object passed in
+ tds = row.anCells;
+
+ for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
+ cellProcess( tds[j] );
}
- else
- {
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
+ }
+
+ // Read the ID from the DOM if present
+ var rowNode = row.firstChild ? row : row.nTr;
+
+ if ( rowNode ) {
+ var id = rowNode.getAttribute( 'id' );
+
+ if ( id ) {
+ _fnSetObjectDataFn( settings.rowId )( d, id );
}
}
-
-
- /**
- * Add the options to the page HTML for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAddOptionsHtml ( oSettings )
+
+ return {
+ data: d,
+ cells: tds
+ };
+ }
+ /**
+ * Create a new TR element (and it's TD children) for a row
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow Row to consider
+ * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
+ * DataTables will create a row automatically
+ * @param {array} [anTds] Array of TD|TH elements for the row - must be given
+ * if nTr is.
+ * @memberof DataTable#oApi
+ */
+ function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
+ {
+ var
+ row = oSettings.aoData[iRow],
+ rowData = row._aData,
+ cells = [],
+ nTr, nTd, oCol,
+ i, iLen;
+
+ if ( row.nTr === null )
{
- /*
- * Create a temporary, empty, div which we can later on replace with what we have generated
- * we do it this way to rendering the 'options' html offline - speed :-)
- */
- var nHolding = $('<div></div>')[0];
- oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
-
- /*
- * All DataTables are wrapped in a div
+ nTr = nTrIn || document.createElement('tr');
+
+ row.nTr = nTr;
+ row.anCells = cells;
+
+ /* Use a private property on the node to allow reserve mapping from the node
+ * to the aoData array for fast look up
*/
- oSettings.nTableWrapper = $('<div id="'+oSettings.sTableId+'_wrapper" class="'+oSettings.oClasses.sWrapper+'" role="grid"></div>')[0];
- oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
-
- /* Track where we want to insert the option */
- var nInsertNode = oSettings.nTableWrapper;
-
- /* Loop over the user set positioning and place the elements as needed */
- var aDom = oSettings.sDom.split('');
- var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
- for ( var i=0 ; i<aDom.length ; i++ )
+ nTr._DT_RowIndex = iRow;
+
+ /* Special parameters can be given by the data source to be used on the row */
+ _fnRowAttributes( oSettings, row );
+
+ /* Process each column */
+ for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
{
- iPushFeature = 0;
- cOption = aDom[i];
+ oCol = oSettings.aoColumns[i];
+
+ nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
+ nTd._DT_CellIndex = {
+ row: iRow,
+ column: i
+ };
- if ( cOption == '<' )
- {
- /* New container div */
- nNewNode = $('<div></div>')[0];
-
- /* Check to see if we should append an id and/or a class name to the container */
- cNext = aDom[i+1];
- if ( cNext == "'" || cNext == '"' )
- {
- sAttr = "";
- j = 2;
- while ( aDom[i+j] != cNext )
- {
- sAttr += aDom[i+j];
- j++;
- }
-
- /* Replace jQuery UI constants */
- if ( sAttr == "H" )
- {
- sAttr = oSettings.oClasses.sJUIHeader;
- }
- else if ( sAttr == "F" )
- {
- sAttr = oSettings.oClasses.sJUIFooter;
- }
-
- /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
- * breaks the string into parts and applies them as needed
- */
- if ( sAttr.indexOf('.') != -1 )
- {
- var aSplit = sAttr.split('.');
- nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
- nNewNode.className = aSplit[1];
- }
- else if ( sAttr.charAt(0) == "#" )
- {
- nNewNode.id = sAttr.substr(1, sAttr.length-1);
- }
- else
- {
- nNewNode.className = sAttr;
- }
-
- i += j; /* Move along the position array */
- }
-
- nInsertNode.appendChild( nNewNode );
- nInsertNode = nNewNode;
- }
- else if ( cOption == '>' )
- {
- /* End container div */
- nInsertNode = nInsertNode.parentNode;
- }
- else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
- {
- /* Length */
- nTmp = _fnFeatureHtmlLength( oSettings );
- iPushFeature = 1;
- }
- else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
- {
- /* Filter */
- nTmp = _fnFeatureHtmlFilter( oSettings );
- iPushFeature = 1;
- }
- else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
- {
- /* pRocessing */
- nTmp = _fnFeatureHtmlProcessing( oSettings );
- iPushFeature = 1;
- }
- else if ( cOption == 't' )
- {
- /* Table */
- nTmp = _fnFeatureHtmlTable( oSettings );
- iPushFeature = 1;
+ cells.push( nTd );
+
+ // Need to create the HTML if new, or if a rendering function is defined
+ if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
+ (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
+ ) {
+ nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
}
- else if ( cOption == 'i' && oSettings.oFeatures.bInfo )
+
+ /* Add user defined class */
+ if ( oCol.sClass )
{
- /* Info */
- nTmp = _fnFeatureHtmlInfo( oSettings );
- iPushFeature = 1;
+ nTd.className += ' '+oCol.sClass;
}
- else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
+
+ // Visibility - add or remove as required
+ if ( oCol.bVisible && ! nTrIn )
{
- /* Pagination */
- nTmp = _fnFeatureHtmlPaginate( oSettings );
- iPushFeature = 1;
+ nTr.appendChild( nTd );
}
- else if ( DataTable.ext.aoFeatures.length !== 0 )
+ else if ( ! oCol.bVisible && nTrIn )
{
- /* Plug-in features */
- var aoFeatures = DataTable.ext.aoFeatures;
- for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
- {
- if ( cOption == aoFeatures[k].cFeature )
- {
- nTmp = aoFeatures[k].fnInit( oSettings );
- if ( nTmp )
- {
- iPushFeature = 1;
- }
- break;
- }
- }
+ nTd.parentNode.removeChild( nTd );
}
-
- /* Add to the 2D features array */
- if ( iPushFeature == 1 && nTmp !== null )
+
+ if ( oCol.fnCreatedCell )
{
- if ( typeof oSettings.aanFeatures[cOption] !== 'object' )
- {
- oSettings.aanFeatures[cOption] = [];
- }
- oSettings.aanFeatures[cOption].push( nTmp );
- nInsertNode.appendChild( nTmp );
+ oCol.fnCreatedCell.call( oSettings.oInstance,
+ nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
+ );
}
}
-
- /* Built our DOM structure - replace the holding div with what we want */
- nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
+
+ _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
}
-
-
- /**
- * Use the DOM source to create up an array of header cells. The idea here is to
- * create a layout grid (array) of rows x columns, which contains a reference
- * to the cell that that point in the grid (regardless of col/rowspan), such that
- * any column / row could be removed and the new grid constructed
- * @param array {object} aLayout Array to store the calculated layout in
- * @param {node} nThead The header/footer element for the table
- * @memberof DataTable#oApi
- */
- function _fnDetectHeader ( aLayout, nThead )
- {
- var nTrs = $(nThead).children('tr');
- var nTr, nCell;
- var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
- var bUnique;
- var fnShiftCol = function ( a, i, j ) {
- var k = a[i];
- while ( k[j] ) {
- j++;
- }
- return j;
- };
-
- aLayout.splice( 0, aLayout.length );
-
- /* We know how many rows there are in the layout - so prep it */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- aLayout.push( [] );
+
+ // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
+ // and deployed
+ row.nTr.setAttribute( 'role', 'row' );
+ }
+
+
+ /**
+ * Add attributes to a row based on the special `DT_*` parameters in a data
+ * source object.
+ * @param {object} settings DataTables settings object
+ * @param {object} DataTables row object for the row to be modified
+ * @memberof DataTable#oApi
+ */
+ function _fnRowAttributes( settings, row )
+ {
+ var tr = row.nTr;
+ var data = row._aData;
+
+ if ( tr ) {
+ var id = settings.rowIdFn( data );
+
+ if ( id ) {
+ tr.id = id;
}
-
- /* Calculate a layout array */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- nTr = nTrs[i];
- iColumn = 0;
-
- /* For every cell in the row... */
- nCell = nTr.firstChild;
- while ( nCell ) {
- if ( nCell.nodeName.toUpperCase() == "TD" ||
- nCell.nodeName.toUpperCase() == "TH" )
- {
- /* Get the col and rowspan attributes from the DOM and sanitise them */
- iColspan = nCell.getAttribute('colspan') * 1;
- iRowspan = nCell.getAttribute('rowspan') * 1;
- iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
- iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
-
- /* There might be colspan cells already in this row, so shift our target
- * accordingly
- */
- iColShifted = fnShiftCol( aLayout, i, iColumn );
-
- /* Cache calculation for unique columns */
- bUnique = iColspan === 1 ? true : false;
-
- /* If there is col / rowspan, copy the information into the layout grid */
- for ( l=0 ; l<iColspan ; l++ )
- {
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aLayout[i+k][iColShifted+l] = {
- "cell": nCell,
- "unique": bUnique
- };
- aLayout[i+k].nTr = nTr;
- }
- }
- }
- nCell = nCell.nextSibling;
- }
+
+ if ( data.DT_RowClass ) {
+ // Remove any classes added by DT_RowClass before
+ var a = data.DT_RowClass.split(' ');
+ row.__rowc = row.__rowc ?
+ _unique( row.__rowc.concat( a ) ) :
+ a;
+
+ $(tr)
+ .removeClass( row.__rowc.join(' ') )
+ .addClass( data.DT_RowClass );
}
- }
-
-
- /**
- * Get an array of unique th elements, one for each column
- * @param {object} oSettings dataTables settings object
- * @param {node} nHeader automatically detect the layout from this node - optional
- * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
- * @returns array {node} aReturn list of unique th's
- * @memberof DataTable#oApi
- */
- function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
- {
- var aReturn = [];
- if ( !aLayout )
- {
- aLayout = oSettings.aoHeader;
- if ( nHeader )
- {
- aLayout = [];
- _fnDetectHeader( aLayout, nHeader );
- }
+
+ if ( data.DT_RowAttr ) {
+ $(tr).attr( data.DT_RowAttr );
}
-
- for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
- {
- for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
- {
- if ( aLayout[i][j].unique &&
- (!aReturn[j] || !oSettings.bSortCellsTop) )
- {
- aReturn[j] = aLayout[i][j].cell;
- }
- }
+
+ if ( data.DT_RowData ) {
+ $(tr).data( data.DT_RowData );
}
-
- return aReturn;
}
-
-
-
- /**
- * Update the table using an Ajax call
- * @param {object} oSettings dataTables settings object
- * @returns {boolean} Block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdate( oSettings )
- {
- if ( oSettings.bAjaxDataGet )
- {
- oSettings.iDraw++;
- _fnProcessingDisplay( oSettings, true );
- var iColumns = oSettings.aoColumns.length;
- var aoData = _fnAjaxParameters( oSettings );
- _fnServerParams( oSettings, aoData );
-
- oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
- function(json) {
- _fnAjaxUpdateDraw( oSettings, json );
- }, oSettings );
- return false;
- }
- else
- {
- return true;
- }
+ }
+
+
+ /**
+ * Create the HTML header for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnBuildHead( oSettings )
+ {
+ var i, ien, cell, row, column;
+ var thead = oSettings.nTHead;
+ var tfoot = oSettings.nTFoot;
+ var createHeader = $('th, td', thead).length === 0;
+ var classes = oSettings.oClasses;
+ var columns = oSettings.aoColumns;
+
+ if ( createHeader ) {
+ row = $('<tr/>').appendTo( thead );
}
-
-
- /**
- * Build up the parameters in an object needed for a server-side processing request
- * @param {object} oSettings dataTables settings object
- * @returns {bool} block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxParameters( oSettings )
- {
- var iColumns = oSettings.aoColumns.length;
- var aoData = [], mDataProp, aaSort, aDataSort;
- var i, j;
-
- aoData.push( { "name": "sEcho", "value": oSettings.iDraw } );
- aoData.push( { "name": "iColumns", "value": iColumns } );
- aoData.push( { "name": "sColumns", "value": _fnColumnOrdering(oSettings) } );
- aoData.push( { "name": "iDisplayStart", "value": oSettings._iDisplayStart } );
- aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
- oSettings._iDisplayLength : -1 } );
-
- for ( i=0 ; i<iColumns ; i++ )
- {
- mDataProp = oSettings.aoColumns[i].mData;
- aoData.push( { "name": "mDataProp_"+i, "value": typeof(mDataProp)==="function" ? 'function' : mDataProp } );
+
+ for ( i=0, ien=columns.length ; i<ien ; i++ ) {
+ column = columns[i];
+ cell = $( column.nTh ).addClass( column.sClass );
+
+ if ( createHeader ) {
+ cell.appendTo( row );
}
-
- /* Filtering */
- if ( oSettings.oFeatures.bFilter !== false )
- {
- aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
- aoData.push( { "name": "bRegex", "value": oSettings.oPreviousSearch.bRegex } );
- for ( i=0 ; i<iColumns ; i++ )
- {
- aoData.push( { "name": "sSearch_"+i, "value": oSettings.aoPreSearchCols[i].sSearch } );
- aoData.push( { "name": "bRegex_"+i, "value": oSettings.aoPreSearchCols[i].bRegex } );
- aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
+
+ // 1.11 move into sorting
+ if ( oSettings.oFeatures.bSort ) {
+ cell.addClass( column.sSortingClass );
+
+ if ( column.bSortable !== false ) {
+ cell
+ .attr( 'tabindex', oSettings.iTabIndex )
+ .attr( 'aria-controls', oSettings.sTableId );
+
+ _fnSortAttachListener( oSettings, column.nTh, i );
}
}
-
- /* Sorting */
- if ( oSettings.oFeatures.bSort !== false )
- {
- var iCounter = 0;
-
- aaSort = ( oSettings.aaSortingFixed !== null ) ?
- oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
- oSettings.aaSorting.slice();
-
- for ( i=0 ; i<aaSort.length ; i++ )
- {
- aDataSort = oSettings.aoColumns[ aaSort[i][0] ].aDataSort;
-
- for ( j=0 ; j<aDataSort.length ; j++ )
- {
- aoData.push( { "name": "iSortCol_"+iCounter, "value": aDataSort[j] } );
- aoData.push( { "name": "sSortDir_"+iCounter, "value": aaSort[i][1] } );
- iCounter++;
- }
- }
- aoData.push( { "name": "iSortingCols", "value": iCounter } );
-
- for ( i=0 ; i<iColumns ; i++ )
- {
- aoData.push( { "name": "bSortable_"+i, "value": oSettings.aoColumns[i].bSortable } );
- }
+
+ if ( column.sTitle != cell[0].innerHTML ) {
+ cell.html( column.sTitle );
}
-
- return aoData;
+
+ _fnRenderer( oSettings, 'header' )(
+ oSettings, cell, column, classes
+ );
}
-
-
- /**
- * Add Ajax parameters from plug-ins
- * @param {object} oSettings dataTables settings object
- * @param array {objects} aoData name/value pairs to send to the server
- * @memberof DataTable#oApi
- */
- function _fnServerParams( oSettings, aoData )
- {
- _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [aoData] );
+
+ if ( createHeader ) {
+ _fnDetectHeader( oSettings.aoHeader, thead );
}
-
- /**
- * Data the data from the server (nuking the old) and redraw the table
- * @param {object} oSettings dataTables settings object
- * @param {object} json json data return from the server.
- * @param {string} json.sEcho Tracking flag for DataTables to match requests
- * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
- * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
- * @param {array} json.aaData The data to display on this page
- * @param {string} [json.sColumns] Column ordering (sName, comma separated)
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdateDraw ( oSettings, json )
- {
- if ( json.sEcho !== undefined )
- {
- /* Protect against old returns over-writing a new one. Possible when you get
- * very fast interaction, and later queries are completed much faster
- */
- if ( json.sEcho*1 < oSettings.iDraw )
- {
- return;
- }
- else
- {
- oSettings.iDraw = json.sEcho * 1;
- }
- }
-
- if ( !oSettings.oScroll.bInfinite ||
- (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
- {
- _fnClearTable( oSettings );
- }
- oSettings._iRecordsTotal = parseInt(json.iTotalRecords, 10);
- oSettings._iRecordsDisplay = parseInt(json.iTotalDisplayRecords, 10);
-
- /* Determine if reordering is required */
- var sOrdering = _fnColumnOrdering(oSettings);
- var bReOrder = (json.sColumns !== undefined && sOrdering !== "" && json.sColumns != sOrdering );
- var aiIndex;
- if ( bReOrder )
- {
- aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
- }
-
- var aData = _fnGetObjectDataFn( oSettings.sAjaxDataProp )( json );
- for ( var i=0, iLen=aData.length ; i<iLen ; i++ )
- {
- if ( bReOrder )
- {
- /* If we need to re-order, then create a new array with the correct order and add it */
- var aDataSorted = [];
- for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
- {
- aDataSorted.push( aData[i][ aiIndex[j] ] );
- }
- _fnAddData( oSettings, aDataSorted );
- }
- else
- {
- /* No re-order required, sever got it "right" - just straight add */
- _fnAddData( oSettings, aData[i] );
+ /* ARIA role for the rows */
+ $(thead).find('>tr').attr('role', 'row');
+
+ /* Deal with the footer - add classes if required */
+ $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
+ $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
+
+ // Cache the footer cells. Note that we only take the cells from the first
+ // row in the footer. If there is more than one row the user wants to
+ // interact with, they need to use the table().foot() method. Note also this
+ // allows cells to be used for multiple columns using colspan
+ if ( tfoot !== null ) {
+ var cells = oSettings.aoFooter[0];
+
+ for ( i=0, ien=cells.length ; i<ien ; i++ ) {
+ column = columns[i];
+ column.nTf = cells[i].cell;
+
+ if ( column.sClass ) {
+ $(column.nTf).addClass( column.sClass );
}
}
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-
- oSettings.bAjaxDataGet = false;
- _fnDraw( oSettings );
- oSettings.bAjaxDataGet = true;
- _fnProcessingDisplay( oSettings, false );
}
-
-
-
- /**
- * Generate the node required for filtering text
- * @returns {node} Filter control element
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlFilter ( oSettings )
+ }
+
+
+ /**
+ * Draw the header (or footer) element based on the column visibility states. The
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
+ * the instantaneous column visibility, to construct the new layout. The grid is
+ * traversed over cell at a time in a rows x columns grid fashion, although each
+ * cell insert can cover multiple elements in the grid - which is tracks using the
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
+ * already a cell in that position.
+ * @param {object} oSettings dataTables settings object
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
+ * @memberof DataTable#oApi
+ */
+ function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
+ {
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
+ var aoLocal = [];
+ var aApplied = [];
+ var iColumns = oSettings.aoColumns.length;
+ var iRowspan, iColspan;
+
+ if ( ! aoSource )
{
- var oPreviousSearch = oSettings.oPreviousSearch;
-
- var sSearchStr = oSettings.oLanguage.sSearch;
- sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ?
- sSearchStr.replace('_INPUT_', '<input type="text" />') :
- sSearchStr==="" ? '<input type="text" />' : sSearchStr+' <input type="text" />';
-
- var nFilter = document.createElement( 'div' );
- nFilter.className = oSettings.oClasses.sFilter;
- nFilter.innerHTML = '<label>'+sSearchStr+'</label>';
- if ( !oSettings.aanFeatures.f )
- {
- nFilter.id = oSettings.sTableId+'_filter';
- }
-
- var jqFilter = $('input[type="text"]', nFilter);
-
- // Store a reference to the input element, so other input elements could be
- // added to the filter wrapper if needed (submit button for example)
- nFilter._DT_Input = jqFilter[0];
-
- jqFilter.val( oPreviousSearch.sSearch.replace('"','&quot;') );
- jqFilter.bind( 'keyup.DT', function(e) {
- /* Update all other filter input elements for the new display */
- var n = oSettings.aanFeatures.f;
- var val = this.value==="" ? "" : this.value; // mental IE8 fix :-(
-
- for ( var i=0, iLen=n.length ; i<iLen ; i++ )
- {
- if ( n[i] != $(this).parents('div.dataTables_filter')[0] )
- {
- $(n[i]._DT_Input).val( val );
- }
- }
-
- /* Now do the filter */
- if ( val != oPreviousSearch.sSearch )
- {
- _fnFilterComplete( oSettings, {
- "sSearch": val,
- "bRegex": oPreviousSearch.bRegex,
- "bSmart": oPreviousSearch.bSmart ,
- "bCaseInsensitive": oPreviousSearch.bCaseInsensitive
- } );
- }
- } );
-
- jqFilter
- .attr('aria-controls', oSettings.sTableId)
- .bind( 'keypress.DT', function(e) {
- /* Prevent form submission */
- if ( e.keyCode == 13 )
- {
- return false;
- }
- }
- );
-
- return nFilter;
+ return;
}
-
-
- /**
- * Filter the table using both the global filter and column based filtering
- * @param {object} oSettings dataTables settings object
- * @param {object} oSearch search information
- * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
- * @memberof DataTable#oApi
- */
- function _fnFilterComplete ( oSettings, oInput, iForce )
+
+ if ( bIncludeHidden === undefined )
{
- var oPrevSearch = oSettings.oPreviousSearch;
- var aoPrevSearch = oSettings.aoPreSearchCols;
- var fnSaveFilter = function ( oFilter ) {
- /* Save the filtering values */
- oPrevSearch.sSearch = oFilter.sSearch;
- oPrevSearch.bRegex = oFilter.bRegex;
- oPrevSearch.bSmart = oFilter.bSmart;
- oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
- };
-
- /* In server-side processing all filtering is done by the server, so no point hanging around here */
- if ( !oSettings.oFeatures.bServerSide )
- {
- /* Global filter */
- _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart, oInput.bCaseInsensitive );
- fnSaveFilter( oInput );
-
- /* Now do the individual column filter */
- for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
- {
- _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, aoPrevSearch[i].bRegex,
- aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
- }
-
- /* Custom filtering */
- _fnFilterCustom( oSettings );
- }
- else
- {
- fnSaveFilter( oInput );
- }
-
- /* Tell the draw function we have been filtering */
- oSettings.bFiltered = true;
- $(oSettings.oInstance).trigger('filter', oSettings);
-
- /* Redraw the table */
- oSettings._iDisplayStart = 0;
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
-
- /* Rebuild search array 'offline' */
- _fnBuildSearchArray( oSettings, 0 );
+ bIncludeHidden = false;
}
-
-
- /**
- * Apply custom filtering functions
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFilterCustom( oSettings )
+
+ /* Make a copy of the master layout array, but without the visible columns in it */
+ for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
{
- var afnFilters = DataTable.ext.afnFiltering;
- var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
-
- for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
+ aoLocal[i] = aoSource[i].slice();
+ aoLocal[i].nTr = aoSource[i].nTr;
+
+ /* Remove any columns which are currently hidden */
+ for ( j=iColumns-1 ; j>=0 ; j-- )
{
- var iCorrector = 0;
- for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
+ if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
{
- var iDisIndex = oSettings.aiDisplay[j-iCorrector];
- var bTest = afnFilters[i](
- oSettings,
- _fnGetRowData( oSettings, iDisIndex, 'filter', aiFilterColumns ),
- iDisIndex
- );
-
- /* Check if we should use this row based on the filtering function */
- if ( !bTest )
- {
- oSettings.aiDisplay.splice( j-iCorrector, 1 );
- iCorrector++;
- }
+ aoLocal[i].splice( j, 1 );
}
}
+
+ /* Prep the applied array - it needs an element for each row */
+ aApplied.push( [] );
}
-
-
- /**
- * Filter the table on a per-column basis
- * @param {object} oSettings dataTables settings object
- * @param {string} sInput string to filter on
- * @param {int} iColumn column to filter
- * @param {bool} bRegex treat search string as a regular expression or not
- * @param {bool} bSmart use smart filtering or not
- * @param {bool} bCaseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart, bCaseInsensitive )
+
+ for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
{
- if ( sInput === "" )
- {
- return;
- }
-
- var iIndexCorrector = 0;
- var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
-
- for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
+ nLocalTr = aoLocal[i].nTr;
+
+ /* All cells are going to be replaced, so empty out the row */
+ if ( nLocalTr )
{
- var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ),
- oSettings.aoColumns[iColumn].sType );
- if ( ! rpSearch.test( sData ) )
+ while( (n = nLocalTr.firstChild) )
{
- oSettings.aiDisplay.splice( i, 1 );
- iIndexCorrector++;
+ nLocalTr.removeChild( n );
}
}
- }
-
-
- /**
- * Filter the data table based on user input and draw the table
- * @param {object} oSettings dataTables settings object
- * @param {string} sInput string to filter on
- * @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0)
- * @param {bool} bRegex treat as a regular expression or not
- * @param {bool} bSmart perform smart filtering or not
- * @param {bool} bCaseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive )
- {
- var i;
- var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
- var oPrevSearch = oSettings.oPreviousSearch;
-
- /* Check if we are forcing or not - optional parameter */
- if ( !iForce )
- {
- iForce = 0;
- }
-
- /* Need to take account of custom filtering functions - always filter */
- if ( DataTable.ext.afnFiltering.length !== 0 )
- {
- iForce = 1;
- }
-
- /*
- * If the input is blank - we want the full data set
- */
- if ( sInput.length <= 0 )
- {
- oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
- }
- else
+
+ for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
{
- /*
- * We are starting a new search or the new search string is smaller
- * then the old one (i.e. delete). Search from the master array
- */
- if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
- oPrevSearch.sSearch.length > sInput.length || iForce == 1 ||
- sInput.indexOf(oPrevSearch.sSearch) !== 0 )
+ iRowspan = 1;
+ iColspan = 1;
+
+ /* Check to see if there is already a cell (row/colspan) covering our target
+ * insert point. If there is, then there is nothing to do.
+ */
+ if ( aApplied[i][j] === undefined )
{
- /* Nuke the old display array - we are going to rebuild it */
- oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
-
- /* Force a rebuild of the search array */
- _fnBuildSearchArray( oSettings, 1 );
-
- /* Search through all records to populate the search array
- * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1
- * mapping
- */
- for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
+ nLocalTr.appendChild( aoLocal[i][j].cell );
+ aApplied[i][j] = 1;
+
+ /* Expand the cell to cover as many rows as needed */
+ while ( aoLocal[i+iRowspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
{
- if ( rpSearch.test(oSettings.asDataSearch[i]) )
- {
- oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
- }
+ aApplied[i+iRowspan][j] = 1;
+ iRowspan++;
}
- }
- else
- {
- /* Using old search array - refine it - do it this way for speed
- * Don't have to search the whole master array again
- */
- var iIndexCorrector = 0;
-
- /* Search the current results */
- for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
+
+ /* Expand the cell to cover as many columns as needed */
+ while ( aoLocal[i][j+iColspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
{
- if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
+ /* Must update the applied array over the rows for the columns */
+ for ( k=0 ; k<iRowspan ; k++ )
{
- oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
- iIndexCorrector++;
- }
- }
- }
- }
- }
-
-
- /**
- * Create an array which can be quickly search through
- * @param {object} oSettings dataTables settings object
- * @param {int} iMaster use the master data array - optional
- * @memberof DataTable#oApi
- */
- function _fnBuildSearchArray ( oSettings, iMaster )
- {
- if ( !oSettings.oFeatures.bServerSide )
- {
- /* Clear out the old data */
- oSettings.asDataSearch = [];
-
- var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
- var aiIndex = (iMaster===1) ?
- oSettings.aiDisplayMaster :
- oSettings.aiDisplay;
-
- for ( var i=0, iLen=aiIndex.length ; i<iLen ; i++ )
- {
- oSettings.asDataSearch[i] = _fnBuildSearchRow(
- oSettings,
- _fnGetRowData( oSettings, aiIndex[i], 'filter', aiFilterColumns )
- );
+ aApplied[i+k][j+iColspan] = 1;
+ }
+ iColspan++;
+ }
+
+ /* Do the actual expansion in the DOM */
+ $(aoLocal[i][j].cell)
+ .attr('rowspan', iRowspan)
+ .attr('colspan', iColspan);
}
}
}
-
-
- /**
- * Create a searchable string from a single data row
- * @param {object} oSettings dataTables settings object
- * @param {array} aData Row data array to use for the data to search
- * @memberof DataTable#oApi
- */
- function _fnBuildSearchRow( oSettings, aData )
+ }
+
+
+ /**
+ * Insert the required TR nodes into the table for display
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnDraw( oSettings )
+ {
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
+ var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
+ if ( $.inArray( false, aPreDraw ) !== -1 )
{
- var sSearch = aData.join(' ');
-
- /* If it looks like there is an HTML entity in the string, attempt to decode it */
- if ( sSearch.indexOf('&') !== -1 )
- {
- sSearch = $('<div>').html(sSearch).text();
- }
-
- // Strip newline characters
- return sSearch.replace( /[\n\r]/g, " " );
+ _fnProcessingDisplay( oSettings, false );
+ return;
}
-
- /**
- * Build a regular expression object suitable for searching a table
- * @param {string} sSearch string to search for
- * @param {bool} bRegex treat as a regular expression or not
- * @param {bool} bSmart perform smart filtering or not
- * @param {bool} bCaseInsensitive Do case insensitive matching or not
- * @returns {RegExp} constructed object
- * @memberof DataTable#oApi
- */
- function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive )
+
+ var i, iLen, n;
+ var anRows = [];
+ var iRowCount = 0;
+ var asStripeClasses = oSettings.asStripeClasses;
+ var iStripes = asStripeClasses.length;
+ var iOpenRows = oSettings.aoOpenRows.length;
+ var oLang = oSettings.oLanguage;
+ var iInitDisplayStart = oSettings.iInitDisplayStart;
+ var bServerSide = _fnDataSource( oSettings ) == 'ssp';
+ var aiDisplay = oSettings.aiDisplay;
+
+ oSettings.bDrawing = true;
+
+ /* Check and see if we have an initial draw position from state saving */
+ if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
+ {
+ oSettings._iDisplayStart = bServerSide ?
+ iInitDisplayStart :
+ iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
+ 0 :
+ iInitDisplayStart;
+
+ oSettings.iInitDisplayStart = -1;
+ }
+
+ var iDisplayStart = oSettings._iDisplayStart;
+ var iDisplayEnd = oSettings.fnDisplayEnd();
+
+ /* Server-side processing draw intercept */
+ if ( oSettings.bDeferLoading )
{
- var asSearch, sRegExpString;
-
- if ( bSmart )
- {
- /* Generate the regular expression to use. Something along the lines of:
- * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
- */
- asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
- sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
- return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" );
- }
- else
- {
- sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
- return new RegExp( sSearch, bCaseInsensitive ? "i" : "" );
- }
+ oSettings.bDeferLoading = false;
+ oSettings.iDraw++;
+ _fnProcessingDisplay( oSettings, false );
}
-
-
- /**
- * Convert raw data into something that the user can search on
- * @param {string} sData data to be modified
- * @param {string} sType data type
- * @returns {string} search string
- * @memberof DataTable#oApi
- */
- function _fnDataToSearch ( sData, sType )
+ else if ( !bServerSide )
{
- if ( typeof DataTable.ext.ofnSearch[sType] === "function" )
- {
- return DataTable.ext.ofnSearch[sType]( sData );
- }
- else if ( sData === null )
- {
- return '';
- }
- else if ( sType == "html" )
- {
- return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" );
- }
- else if ( typeof sData === "string" )
- {
- return sData.replace(/[\r\n]/g," ");
- }
- return sData;
+ oSettings.iDraw++;
}
-
-
- /**
- * scape a string such that it can be used in a regular expression
- * @param {string} sVal string to escape
- * @returns {string} escaped string
- * @memberof DataTable#oApi
- */
- function _fnEscapeRegex ( sVal )
+ else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
{
- var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ];
- var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
- return sVal.replace(reReplace, '\\$1');
+ return;
}
-
-
- /**
- * Generate the node required for the info display
- * @param {object} oSettings dataTables settings object
- * @returns {node} Information element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlInfo ( oSettings )
+
+ if ( aiDisplay.length !== 0 )
{
- var nInfo = document.createElement( 'div' );
- nInfo.className = oSettings.oClasses.sInfo;
-
- /* Actions that are to be taken once only for this feature */
- if ( !oSettings.aanFeatures.i )
+ var iStart = bServerSide ? 0 : iDisplayStart;
+ var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
+
+ for ( var j=iStart ; j<iEnd ; j++ )
{
- /* Add draw callback */
- oSettings.aoDrawCallback.push( {
- "fn": _fnUpdateInfo,
- "sName": "information"
- } );
-
- /* Add id */
- nInfo.id = oSettings.sTableId+'_info';
+ var iDataIndex = aiDisplay[j];
+ var aoData = oSettings.aoData[ iDataIndex ];
+ if ( aoData.nTr === null )
+ {
+ _fnCreateTr( oSettings, iDataIndex );
+ }
+
+ var nRow = aoData.nTr;
+
+ /* Remove the old striping classes and then add the new one */
+ if ( iStripes !== 0 )
+ {
+ var sStripe = asStripeClasses[ iRowCount % iStripes ];
+ if ( aoData._sRowStripe != sStripe )
+ {
+ $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
+ aoData._sRowStripe = sStripe;
+ }
+ }
+
+ // Row callback functions - might want to manipulate the row
+ // iRowCount and j are not currently documented. Are they at all
+ // useful?
+ _fnCallbackFire( oSettings, 'aoRowCallback', null,
+ [nRow, aoData._aData, iRowCount, j] );
+
+ anRows.push( nRow );
+ iRowCount++;
}
- oSettings.nTable.setAttribute( 'aria-describedby', oSettings.sTableId+'_info' );
-
- return nInfo;
}
-
-
- /**
- * Update the information elements in the display
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnUpdateInfo ( oSettings )
+ else
{
- /* Show information about the table */
- if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
+ /* Table is empty - create a row with an empty message in it */
+ var sZero = oLang.sZeroRecords;
+ if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
{
- return;
+ sZero = oLang.sLoadingRecords;
}
-
- var
- oLang = oSettings.oLanguage,
- iStart = oSettings._iDisplayStart+1,
- iEnd = oSettings.fnDisplayEnd(),
- iMax = oSettings.fnRecordsTotal(),
- iTotal = oSettings.fnRecordsDisplay(),
- sOut;
-
- if ( iTotal === 0 )
+ else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
{
- /* Empty record set */
- sOut = oLang.sInfoEmpty;
- }
- else {
- /* Normal record set */
- sOut = oLang.sInfo;
- }
-
- if ( iTotal != iMax )
- {
- /* Record set after filtering */
- sOut += ' ' + oLang.sInfoFiltered;
- }
-
- // Convert the macros
- sOut += oLang.sInfoPostFix;
- sOut = _fnInfoMacros( oSettings, sOut );
-
- if ( oLang.fnInfoCallback !== null )
- {
- sOut = oLang.fnInfoCallback.call( oSettings.oInstance,
- oSettings, iStart, iEnd, iMax, iTotal, sOut );
- }
-
- var n = oSettings.aanFeatures.i;
- for ( var i=0, iLen=n.length ; i<iLen ; i++ )
- {
- $(n[i]).html( sOut );
+ sZero = oLang.sEmptyTable;
}
+
+ anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
+ .append( $('<td />', {
+ 'valign': 'top',
+ 'colSpan': _fnVisbleColumns( oSettings ),
+ 'class': oSettings.oClasses.sRowEmpty
+ } ).html( sZero ) )[0];
}
-
-
- function _fnInfoMacros ( oSettings, str )
- {
- var
- iStart = oSettings._iDisplayStart+1,
- sStart = oSettings.fnFormatNumber( iStart ),
- iEnd = oSettings.fnDisplayEnd(),
- sEnd = oSettings.fnFormatNumber( iEnd ),
- iTotal = oSettings.fnRecordsDisplay(),
- sTotal = oSettings.fnFormatNumber( iTotal ),
- iMax = oSettings.fnRecordsTotal(),
- sMax = oSettings.fnFormatNumber( iMax );
-
- // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
- // internally
- if ( oSettings.oScroll.bInfinite )
- {
- sStart = oSettings.fnFormatNumber( 1 );
- }
-
- return str.
- replace(/_START_/g, sStart).
- replace(/_END_/g, sEnd).
- replace(/_TOTAL_/g, sTotal).
- replace(/_MAX_/g, sMax);
+
+ /* Header and footer callbacks */
+ _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
+
+ _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
+ _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
+
+ var body = $(oSettings.nTBody);
+
+ body.children().detach();
+ body.append( $(anRows) );
+
+ /* Call all required callback functions for the end of a draw */
+ _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
+
+ /* Draw is complete, sorting and filtering must be as well */
+ oSettings.bSorted = false;
+ oSettings.bFiltered = false;
+ oSettings.bDrawing = false;
+ }
+
+
+ /**
+ * Redraw the table - taking account of the various features which are enabled
+ * @param {object} oSettings dataTables settings object
+ * @param {boolean} [holdPosition] Keep the current paging position. By default
+ * the paging is reset to the first page
+ * @memberof DataTable#oApi
+ */
+ function _fnReDraw( settings, holdPosition )
+ {
+ var
+ features = settings.oFeatures,
+ sort = features.bSort,
+ filter = features.bFilter;
+
+ if ( sort ) {
+ _fnSort( settings );
}
-
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnInitialise ( oSettings )
+
+ if ( filter ) {
+ _fnFilterComplete( settings, settings.oPreviousSearch );
+ }
+ else {
+ // No filtering, so we want to just use the display master
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+ }
+
+ if ( holdPosition !== true ) {
+ settings._iDisplayStart = 0;
+ }
+
+ // Let any modules know about the draw hold position state (used by
+ // scrolling internally)
+ settings._drawHold = holdPosition;
+
+ _fnDraw( settings );
+
+ settings._drawHold = false;
+ }
+
+
+ /**
+ * Add the options to the page HTML for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAddOptionsHtml ( oSettings )
+ {
+ var classes = oSettings.oClasses;
+ var table = $(oSettings.nTable);
+ var holding = $('<div/>').insertBefore( table ); // Holding element for speed
+ var features = oSettings.oFeatures;
+
+ // All DataTables are wrapped in a div
+ var insert = $('<div/>', {
+ id: oSettings.sTableId+'_wrapper',
+ 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
+ } );
+
+ oSettings.nHolding = holding[0];
+ oSettings.nTableWrapper = insert[0];
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
+
+ /* Loop over the user set positioning and place the elements as needed */
+ var aDom = oSettings.sDom.split('');
+ var featureNode, cOption, nNewNode, cNext, sAttr, j;
+ for ( var i=0 ; i<aDom.length ; i++ )
{
- var i, iLen, iAjaxStart=oSettings.iInitDisplayStart;
-
- /* Ensure that the table data is fully initialised */
- if ( oSettings.bInitialised === false )
- {
- setTimeout( function(){ _fnInitialise( oSettings ); }, 200 );
- return;
- }
-
- /* Show the display HTML options */
- _fnAddOptionsHtml( oSettings );
-
- /* Build and draw the header / footer for the table */
- _fnBuildHead( oSettings );
- _fnDrawHead( oSettings, oSettings.aoHeader );
- if ( oSettings.nTFoot )
- {
- _fnDrawHead( oSettings, oSettings.aoFooter );
- }
-
- /* Okay to show that something is going on now */
- _fnProcessingDisplay( oSettings, true );
-
- /* Calculate sizes for columns */
- if ( oSettings.oFeatures.bAutoWidth )
- {
- _fnCalculateColumnWidths( oSettings );
- }
-
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
+ featureNode = null;
+ cOption = aDom[i];
+
+ if ( cOption == '<' )
{
- if ( oSettings.aoColumns[i].sWidth !== null )
+ /* New container div */
+ nNewNode = $('<div/>')[0];
+
+ /* Check to see if we should append an id and/or a class name to the container */
+ cNext = aDom[i+1];
+ if ( cNext == "'" || cNext == '"' )
{
- oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
- }
- }
-
- /* If there is default sorting required - let's do it. The sort function will do the
- * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
- * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
- */
- if ( oSettings.oFeatures.bSort )
- {
- _fnSort( oSettings );
- }
- else if ( oSettings.oFeatures.bFilter )
- {
- _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
- }
- else
- {
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
- }
-
- /* if there is an ajax source load the data */
- if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
- {
- var aoData = [];
- _fnServerParams( oSettings, aoData );
- oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData, function(json) {
- var aData = (oSettings.sAjaxDataProp !== "") ?
- _fnGetObjectDataFn( oSettings.sAjaxDataProp )(json) : json;
-
- /* Got the data - add it to the table */
- for ( i=0 ; i<aData.length ; i++ )
+ sAttr = "";
+ j = 2;
+ while ( aDom[i+j] != cNext )
{
- _fnAddData( oSettings, aData[i] );
+ sAttr += aDom[i+j];
+ j++;
}
-
- /* Reset the init display for cookie saving. We've already done a filter, and
- * therefore cleared it before. So we need to make it appear 'fresh'
+
+ /* Replace jQuery UI constants @todo depreciated */
+ if ( sAttr == "H" )
+ {
+ sAttr = classes.sJUIHeader;
+ }
+ else if ( sAttr == "F" )
+ {
+ sAttr = classes.sJUIFooter;
+ }
+
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
+ * breaks the string into parts and applies them as needed
*/
- oSettings.iInitDisplayStart = iAjaxStart;
-
- if ( oSettings.oFeatures.bSort )
+ if ( sAttr.indexOf('.') != -1 )
+ {
+ var aSplit = sAttr.split('.');
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
+ nNewNode.className = aSplit[1];
+ }
+ else if ( sAttr.charAt(0) == "#" )
{
- _fnSort( oSettings );
+ nNewNode.id = sAttr.substr(1, sAttr.length-1);
}
else
{
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
+ nNewNode.className = sAttr;
}
-
- _fnProcessingDisplay( oSettings, false );
- _fnInitComplete( oSettings, json );
- }, oSettings );
- return;
+
+ i += j; /* Move along the position array */
+ }
+
+ insert.append( nNewNode );
+ insert = $(nNewNode);
}
-
- /* Server-side processing initialisation complete is done at the end of _fnDraw */
- if ( !oSettings.oFeatures.bServerSide )
+ else if ( cOption == '>' )
{
- _fnProcessingDisplay( oSettings, false );
- _fnInitComplete( oSettings );
+ /* End container div */
+ insert = insert.parent();
}
- }
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} oSettings dataTables settings object
- * @param {object} [json] JSON from the server that completed the table, if using Ajax source
- * with client-side processing (optional)
- * @memberof DataTable#oApi
- */
- function _fnInitComplete ( oSettings, json )
- {
- oSettings._bInitComplete = true;
- _fnCallbackFire( oSettings, 'aoInitComplete', 'init', [oSettings, json] );
- }
-
-
- /**
- * Language compatibility - when certain options are given, and others aren't, we
- * need to duplicate the values over, in order to provide backwards compatibility
- * with older language files.
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnLanguageCompat( oLanguage )
- {
- var oDefaults = DataTable.defaults.oLanguage;
-
- /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
- * sZeroRecords - assuming that is given.
- */
- if ( !oLanguage.sEmptyTable && oLanguage.sZeroRecords &&
- oDefaults.sEmptyTable === "No data available in table" )
+ // @todo Move options into their own plugins?
+ else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
{
- _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
+ /* Length */
+ featureNode = _fnFeatureHtmlLength( oSettings );
}
-
- /* Likewise with loading records */
- if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords &&
- oDefaults.sLoadingRecords === "Loading..." )
+ else if ( cOption == 'f' && features.bFilter )
{
- _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' );
+ /* Filter */
+ featureNode = _fnFeatureHtmlFilter( oSettings );
}
- }
-
-
-
- /**
- * Generate the node required for user display length changing
- * @param {object} oSettings dataTables settings object
- * @returns {node} Display length feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlLength ( oSettings )
- {
- if ( oSettings.oScroll.bInfinite )
+ else if ( cOption == 'r' && features.bProcessing )
{
- return null;
+ /* pRocessing */
+ featureNode = _fnFeatureHtmlProcessing( oSettings );
}
-
- /* This can be overruled by not using the _MENU_ var/macro in the language variable */
- var sName = 'name="'+oSettings.sTableId+'_length"';
- var sStdMenu = '<select size="1" '+sName+'>';
- var i, iLen;
- var aLengthMenu = oSettings.aLengthMenu;
-
- if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' &&
- typeof aLengthMenu[1] === 'object' )
+ else if ( cOption == 't' )
{
- for ( i=0, iLen=aLengthMenu[0].length ; i<iLen ; i++ )
- {
- sStdMenu += '<option value="'+aLengthMenu[0][i]+'">'+aLengthMenu[1][i]+'</option>';
- }
+ /* Table */
+ featureNode = _fnFeatureHtmlTable( oSettings );
}
- else
+ else if ( cOption == 'i' && features.bInfo )
{
- for ( i=0, iLen=aLengthMenu.length ; i<iLen ; i++ )
- {
- sStdMenu += '<option value="'+aLengthMenu[i]+'">'+aLengthMenu[i]+'</option>';
- }
+ /* Info */
+ featureNode = _fnFeatureHtmlInfo( oSettings );
}
- sStdMenu += '</select>';
-
- var nLength = document.createElement( 'div' );
- if ( !oSettings.aanFeatures.l )
+ else if ( cOption == 'p' && features.bPaginate )
{
- nLength.id = oSettings.sTableId+'_length';
+ /* Pagination */
+ featureNode = _fnFeatureHtmlPaginate( oSettings );
}
- nLength.className = oSettings.oClasses.sLength;
- nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>';
-
- /*
- * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
- * and Stefan Skopnik for fixing the fix!
- */
- $('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true);
-
- $('select', nLength).bind( 'change.DT', function(e) {
- var iVal = $(this).val();
-
- /* Update all other length options for the new display */
- var n = oSettings.aanFeatures.l;
- for ( i=0, iLen=n.length ; i<iLen ; i++ )
- {
- if ( n[i] != this.parentNode )
- {
- $('select', n[i]).val( iVal );
- }
- }
-
- /* Redraw the table */
- oSettings._iDisplayLength = parseInt(iVal, 10);
- _fnCalculateEnd( oSettings );
-
- /* If we have space to show extra rows (backing up from the end point - then do so */
- if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
+ else if ( DataTable.ext.feature.length !== 0 )
+ {
+ /* Plug-in features */
+ var aoFeatures = DataTable.ext.feature;
+ for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
{
- oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
- if ( oSettings._iDisplayStart < 0 )
+ if ( cOption == aoFeatures[k].cFeature )
{
- oSettings._iDisplayStart = 0;
+ featureNode = aoFeatures[k].fnInit( oSettings );
+ break;
}
}
-
- if ( oSettings._iDisplayLength == -1 )
- {
- oSettings._iDisplayStart = 0;
- }
-
- _fnDraw( oSettings );
- } );
-
-
- $('select', nLength).attr('aria-controls', oSettings.sTableId);
-
- return nLength;
- }
-
-
- /**
- * Recalculate the end point based on the start point
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateEnd( oSettings )
- {
- if ( oSettings.oFeatures.bPaginate === false )
- {
- oSettings._iDisplayEnd = oSettings.aiDisplay.length;
}
- else
+
+ /* Add to the 2D features array */
+ if ( featureNode )
{
- /* Set the end point of the display - based on how many elements there are
- * still to display
- */
- if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
- oSettings._iDisplayLength == -1 )
- {
- oSettings._iDisplayEnd = oSettings.aiDisplay.length;
- }
- else
+ var aanFeatures = oSettings.aanFeatures;
+
+ if ( ! aanFeatures[cOption] )
{
- oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
+ aanFeatures[cOption] = [];
}
+
+ aanFeatures[cOption].push( featureNode );
+ insert.append( featureNode );
}
}
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Note that most of the paging logic is done in
- * DataTable.ext.oPagination
- */
-
- /**
- * Generate the node required for default pagination
- * @param {object} oSettings dataTables settings object
- * @returns {node} Pagination feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlPaginate ( oSettings )
+
+ /* Built our DOM structure - replace the holding div with what we want */
+ holding.replaceWith( insert );
+ oSettings.nHolding = null;
+ }
+
+
+ /**
+ * Use the DOM source to create up an array of header cells. The idea here is to
+ * create a layout grid (array) of rows x columns, which contains a reference
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
+ * any column / row could be removed and the new grid constructed
+ * @param array {object} aLayout Array to store the calculated layout in
+ * @param {node} nThead The header/footer element for the table
+ * @memberof DataTable#oApi
+ */
+ function _fnDetectHeader ( aLayout, nThead )
+ {
+ var nTrs = $(nThead).children('tr');
+ var nTr, nCell;
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
+ var bUnique;
+ var fnShiftCol = function ( a, i, j ) {
+ var k = a[i];
+ while ( k[j] ) {
+ j++;
+ }
+ return j;
+ };
+
+ aLayout.splice( 0, aLayout.length );
+
+ /* We know how many rows there are in the layout - so prep it */
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
{
- if ( oSettings.oScroll.bInfinite )
- {
- return null;
- }
-
- var nPaginate = document.createElement( 'div' );
- nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
-
- DataTable.ext.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate,
- function( oSettings ) {
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
- }
- );
-
- /* Add a draw callback for the pagination on first instance, to update the paging display */
- if ( !oSettings.aanFeatures.p )
- {
- oSettings.aoDrawCallback.push( {
- "fn": function( oSettings ) {
- DataTable.ext.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
- } );
- },
- "sName": "pagination"
- } );
- }
- return nPaginate;
+ aLayout.push( [] );
}
-
-
- /**
- * Alter the display settings to change the page
- * @param {object} oSettings dataTables settings object
- * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
- * or page number to jump to (integer)
- * @returns {bool} true page has changed, false - no change (no effect) eg 'first' on page 1
- * @memberof DataTable#oApi
- */
- function _fnPageChange ( oSettings, mAction )
+
+ /* Calculate a layout array */
+ for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
{
- var iOldStart = oSettings._iDisplayStart;
-
- if ( typeof mAction === "number" )
- {
- oSettings._iDisplayStart = mAction * oSettings._iDisplayLength;
- if ( oSettings._iDisplayStart > oSettings.fnRecordsDisplay() )
+ nTr = nTrs[i];
+ iColumn = 0;
+
+ /* For every cell in the row... */
+ nCell = nTr.firstChild;
+ while ( nCell ) {
+ if ( nCell.nodeName.toUpperCase() == "TD" ||
+ nCell.nodeName.toUpperCase() == "TH" )
{
- oSettings._iDisplayStart = 0;
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
+ iColspan = nCell.getAttribute('colspan') * 1;
+ iRowspan = nCell.getAttribute('rowspan') * 1;
+ iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
+ iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
+
+ /* There might be colspan cells already in this row, so shift our target
+ * accordingly
+ */
+ iColShifted = fnShiftCol( aLayout, i, iColumn );
+
+ /* Cache calculation for unique columns */
+ bUnique = iColspan === 1 ? true : false;
+
+ /* If there is col / rowspan, copy the information into the layout grid */
+ for ( l=0 ; l<iColspan ; l++ )
+ {
+ for ( k=0 ; k<iRowspan ; k++ )
+ {
+ aLayout[i+k][iColShifted+l] = {
+ "cell": nCell,
+ "unique": bUnique
+ };
+ aLayout[i+k].nTr = nTr;
+ }
+ }
}
+ nCell = nCell.nextSibling;
}
- else if ( mAction == "first" )
+ }
+ }
+
+
+ /**
+ * Get an array of unique th elements, one for each column
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nHeader automatically detect the layout from this node - optional
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
+ * @returns array {node} aReturn list of unique th's
+ * @memberof DataTable#oApi
+ */
+ function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
+ {
+ var aReturn = [];
+ if ( !aLayout )
+ {
+ aLayout = oSettings.aoHeader;
+ if ( nHeader )
{
- oSettings._iDisplayStart = 0;
+ aLayout = [];
+ _fnDetectHeader( aLayout, nHeader );
}
- else if ( mAction == "previous" )
+ }
+
+ for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
+ {
+ for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
{
- oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
- oSettings._iDisplayStart - oSettings._iDisplayLength :
- 0;
-
- /* Correct for under-run */
- if ( oSettings._iDisplayStart < 0 )
+ if ( aLayout[i][j].unique &&
+ (!aReturn[j] || !oSettings.bSortCellsTop) )
{
- oSettings._iDisplayStart = 0;
+ aReturn[j] = aLayout[i][j].cell;
}
}
- else if ( mAction == "next" )
- {
- if ( oSettings._iDisplayLength >= 0 )
- {
- /* Make sure we are not over running the display array */
- if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
- {
- oSettings._iDisplayStart += oSettings._iDisplayLength;
+ }
+
+ return aReturn;
+ }
+
+ /**
+ * Create an Ajax call based on the table's settings, taking into account that
+ * parameters can have multiple forms, and backwards compatibility.
+ *
+ * @param {object} oSettings dataTables settings object
+ * @param {array} data Data to send to the server, required by
+ * DataTables - may be augmented by developer callbacks
+ * @param {function} fn Callback function to run when data is obtained
+ */
+ function _fnBuildAjax( oSettings, data, fn )
+ {
+ // Compatibility with 1.9-, allow fnServerData and event to manipulate
+ _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
+
+ // Convert to object based for 1.10+ if using the old array scheme which can
+ // come from server-side processing or serverParams
+ if ( data && $.isArray(data) ) {
+ var tmp = {};
+ var rbracket = /(.*?)\[\]$/;
+
+ $.each( data, function (key, val) {
+ var match = val.name.match(rbracket);
+
+ if ( match ) {
+ // Support for arrays
+ var name = match[0];
+
+ if ( ! tmp[ name ] ) {
+ tmp[ name ] = [];
}
+ tmp[ name ].push( val.value );
}
- else
- {
- oSettings._iDisplayStart = 0;
+ else {
+ tmp[val.name] = val.value;
}
- }
- else if ( mAction == "last" )
- {
- if ( oSettings._iDisplayLength >= 0 )
- {
- var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
- oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
+ } );
+ data = tmp;
+ }
+
+ var ajaxData;
+ var ajax = oSettings.ajax;
+ var instance = oSettings.oInstance;
+ var callback = function ( json ) {
+ _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
+ fn( json );
+ };
+
+ if ( $.isPlainObject( ajax ) && ajax.data )
+ {
+ ajaxData = ajax.data;
+
+ var newData = $.isFunction( ajaxData ) ?
+ ajaxData( data, oSettings ) : // fn can manipulate data or return
+ ajaxData; // an object object or array to merge
+
+ // If the function returned something, use that alone
+ data = $.isFunction( ajaxData ) && newData ?
+ newData :
+ $.extend( true, data, newData );
+
+ // Remove the data property as we've resolved it already and don't want
+ // jQuery to do it again (it is restored at the end of the function)
+ delete ajax.data;
+ }
+
+ var baseAjax = {
+ "data": data,
+ "success": function (json) {
+ var error = json.error || json.sError;
+ if ( error ) {
+ _fnLog( oSettings, 0, error );
}
- else
- {
- oSettings._iDisplayStart = 0;
+
+ oSettings.json = json;
+ callback( json );
+ },
+ "dataType": "json",
+ "cache": false,
+ "type": oSettings.sServerMethod,
+ "error": function (xhr, error, thrown) {
+ var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
+
+ if ( $.inArray( true, ret ) === -1 ) {
+ if ( error == "parsererror" ) {
+ _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
+ }
+ else if ( xhr.readyState === 4 ) {
+ _fnLog( oSettings, 0, 'Ajax error', 7 );
+ }
}
+
+ _fnProcessingDisplay( oSettings, false );
}
- else
- {
- _fnLog( oSettings, 0, "Unknown paging action: "+mAction );
- }
- $(oSettings.oInstance).trigger('page', oSettings);
-
- return iOldStart != oSettings._iDisplayStart;
+ };
+
+ // Store the data submitted for the API
+ oSettings.oAjaxData = data;
+
+ // Allow plug-ins and external processes to modify the data
+ _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
+
+ if ( oSettings.fnServerData )
+ {
+ // DataTables 1.9- compatibility
+ oSettings.fnServerData.call( instance,
+ oSettings.sAjaxSource,
+ $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
+ return { name: key, value: val };
+ } ),
+ callback,
+ oSettings
+ );
}
-
-
-
- /**
- * Generate the node required for the processing node
- * @param {object} oSettings dataTables settings object
- * @returns {node} Processing element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlProcessing ( oSettings )
+ else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
{
- var nProcessing = document.createElement( 'div' );
-
- if ( !oSettings.aanFeatures.r )
- {
- nProcessing.id = oSettings.sTableId+'_processing';
- }
- nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
- nProcessing.className = oSettings.oClasses.sProcessing;
- oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
-
- return nProcessing;
+ // DataTables 1.9- compatibility
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
+ url: ajax || oSettings.sAjaxSource
+ } ) );
}
-
-
- /**
- * Display or hide the processing indicator
- * @param {object} oSettings dataTables settings object
- * @param {bool} bShow Show the processing indicator (true) or not (false)
- * @memberof DataTable#oApi
- */
- function _fnProcessingDisplay ( oSettings, bShow )
+ else if ( $.isFunction( ajax ) )
{
- if ( oSettings.oFeatures.bProcessing )
- {
- var an = oSettings.aanFeatures.r;
- for ( var i=0, iLen=an.length ; i<iLen ; i++ )
- {
- an[i].style.visibility = bShow ? "visible" : "hidden";
- }
- }
-
- $(oSettings.oInstance).trigger('processing', [oSettings, bShow]);
+ // Is a function - let the caller define what needs to be done
+ oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
}
-
- /**
- * Add any control elements for the table - specifically scrolling
- * @param {object} oSettings dataTables settings object
- * @returns {node} Node to add to the DOM
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlTable ( oSettings )
+ else
{
- /* Check if scrolling is enabled or not - if not then leave the DOM unaltered */
- if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
- {
- return oSettings.nTable;
- }
-
- /*
- * The HTML structure that we want to generate in this function is:
- * div - nScroller
- * div - nScrollHead
- * div - nScrollHeadInner
- * table - nScrollHeadTable
- * thead - nThead
- * div - nScrollBody
- * table - oSettings.nTable
- * thead - nTheadSize
- * tbody - nTbody
- * div - nScrollFoot
- * div - nScrollFootInner
- * table - nScrollFootTable
- * tfoot - nTfoot
- */
- var
- nScroller = document.createElement('div'),
- nScrollHead = document.createElement('div'),
- nScrollHeadInner = document.createElement('div'),
- nScrollBody = document.createElement('div'),
- nScrollFoot = document.createElement('div'),
- nScrollFootInner = document.createElement('div'),
- nScrollHeadTable = oSettings.nTable.cloneNode(false),
- nScrollFootTable = oSettings.nTable.cloneNode(false),
- nThead = oSettings.nTable.getElementsByTagName('thead')[0],
- nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null :
- oSettings.nTable.getElementsByTagName('tfoot')[0],
- oClasses = oSettings.oClasses;
-
- nScrollHead.appendChild( nScrollHeadInner );
- nScrollFoot.appendChild( nScrollFootInner );
- nScrollBody.appendChild( oSettings.nTable );
- nScroller.appendChild( nScrollHead );
- nScroller.appendChild( nScrollBody );
- nScrollHeadInner.appendChild( nScrollHeadTable );
- nScrollHeadTable.appendChild( nThead );
- if ( nTfoot !== null )
- {
- nScroller.appendChild( nScrollFoot );
- nScrollFootInner.appendChild( nScrollFootTable );
- nScrollFootTable.appendChild( nTfoot );
- }
-
- nScroller.className = oClasses.sScrollWrapper;
- nScrollHead.className = oClasses.sScrollHead;
- nScrollHeadInner.className = oClasses.sScrollHeadInner;
- nScrollBody.className = oClasses.sScrollBody;
- nScrollFoot.className = oClasses.sScrollFoot;
- nScrollFootInner.className = oClasses.sScrollFootInner;
-
- if ( oSettings.oScroll.bAutoCss )
- {
- nScrollHead.style.overflow = "hidden";
- nScrollHead.style.position = "relative";
- nScrollFoot.style.overflow = "hidden";
- nScrollBody.style.overflow = "auto";
- }
-
- nScrollHead.style.border = "0";
- nScrollHead.style.width = "100%";
- nScrollFoot.style.border = "0";
- nScrollHeadInner.style.width = oSettings.oScroll.sXInner !== "" ?
- oSettings.oScroll.sXInner : "100%"; /* will be overwritten */
-
- /* Modify attributes to respect the clones */
- nScrollHeadTable.removeAttribute('id');
- nScrollHeadTable.style.marginLeft = "0";
- oSettings.nTable.style.marginLeft = "0";
- if ( nTfoot !== null )
- {
- nScrollFootTable.removeAttribute('id');
- nScrollFootTable.style.marginLeft = "0";
- }
-
- /* Move caption elements from the body to the header, footer or leave where it is
- * depending on the configuration. Note that the DTD says there can be only one caption */
- var nCaption = $(oSettings.nTable).children('caption');
- if ( nCaption.length > 0 )
- {
- nCaption = nCaption[0];
- if ( nCaption._captionSide === "top" )
- {
- nScrollHeadTable.appendChild( nCaption );
- }
- else if ( nCaption._captionSide === "bottom" && nTfoot )
- {
- nScrollFootTable.appendChild( nCaption );
+ // Object to extend the base settings
+ oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
+
+ // Restore for next time around
+ ajax.data = ajaxData;
+ }
+ }
+
+
+ /**
+ * Update the table using an Ajax call
+ * @param {object} settings dataTables settings object
+ * @returns {boolean} Block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdate( settings )
+ {
+ if ( settings.bAjaxDataGet ) {
+ settings.iDraw++;
+ _fnProcessingDisplay( settings, true );
+
+ _fnBuildAjax(
+ settings,
+ _fnAjaxParameters( settings ),
+ function(json) {
+ _fnAjaxUpdateDraw( settings, json );
}
+ );
+
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Build up the parameters in an object needed for a server-side processing
+ * request. Note that this is basically done twice, is different ways - a modern
+ * method which is used by default in DataTables 1.10 which uses objects and
+ * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
+ * the sAjaxSource option is used in the initialisation, or the legacyAjax
+ * option is set.
+ * @param {object} oSettings dataTables settings object
+ * @returns {bool} block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxParameters( settings )
+ {
+ var
+ columns = settings.aoColumns,
+ columnCount = columns.length,
+ features = settings.oFeatures,
+ preSearch = settings.oPreviousSearch,
+ preColSearch = settings.aoPreSearchCols,
+ i, data = [], dataProp, column, columnSearch,
+ sort = _fnSortFlatten( settings ),
+ displayStart = settings._iDisplayStart,
+ displayLength = features.bPaginate !== false ?
+ settings._iDisplayLength :
+ -1;
+
+ var param = function ( name, value ) {
+ data.push( { 'name': name, 'value': value } );
+ };
+
+ // DataTables 1.9- compatible method
+ param( 'sEcho', settings.iDraw );
+ param( 'iColumns', columnCount );
+ param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
+ param( 'iDisplayStart', displayStart );
+ param( 'iDisplayLength', displayLength );
+
+ // DataTables 1.10+ method
+ var d = {
+ draw: settings.iDraw,
+ columns: [],
+ order: [],
+ start: displayStart,
+ length: displayLength,
+ search: {
+ value: preSearch.sSearch,
+ regex: preSearch.bRegex
}
-
- /*
- * Sizing
- */
- /* When x-scrolling add the width and a scroller to move the header with the body */
- if ( oSettings.oScroll.sX !== "" )
- {
- nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
- nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
-
- if ( nTfoot !== null )
- {
- nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );
+ };
+
+ for ( i=0 ; i<columnCount ; i++ ) {
+ column = columns[i];
+ columnSearch = preColSearch[i];
+ dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
+
+ d.columns.push( {
+ data: dataProp,
+ name: column.sName,
+ searchable: column.bSearchable,
+ orderable: column.bSortable,
+ search: {
+ value: columnSearch.sSearch,
+ regex: columnSearch.bRegex
}
-
- /* When the body is scrolled, then we also want to scroll the headers */
- $(nScrollBody).scroll( function (e) {
- nScrollHead.scrollLeft = this.scrollLeft;
-
- if ( nTfoot !== null )
- {
- nScrollFoot.scrollLeft = this.scrollLeft;
- }
- } );
+ } );
+
+ param( "mDataProp_"+i, dataProp );
+
+ if ( features.bFilter ) {
+ param( 'sSearch_'+i, columnSearch.sSearch );
+ param( 'bRegex_'+i, columnSearch.bRegex );
+ param( 'bSearchable_'+i, column.bSearchable );
}
-
- /* When yscrolling, add the height */
- if ( oSettings.oScroll.sY !== "" )
- {
- nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
+
+ if ( features.bSort ) {
+ param( 'bSortable_'+i, column.bSortable );
}
-
- /* Redraw - align columns across the tables */
- oSettings.aoDrawCallback.push( {
- "fn": _fnScrollDraw,
- "sName": "scrolling"
+ }
+
+ if ( features.bFilter ) {
+ param( 'sSearch', preSearch.sSearch );
+ param( 'bRegex', preSearch.bRegex );
+ }
+
+ if ( features.bSort ) {
+ $.each( sort, function ( i, val ) {
+ d.order.push( { column: val.col, dir: val.dir } );
+
+ param( 'iSortCol_'+i, val.col );
+ param( 'sSortDir_'+i, val.dir );
} );
-
- /* Infinite scrolling event handlers */
- if ( oSettings.oScroll.bInfinite )
- {
- $(nScrollBody).scroll( function() {
- /* Use a blocker to stop scrolling from loading more data while other data is still loading */
- if ( !oSettings.bDrawing && $(this).scrollTop() !== 0 )
- {
- /* Check if we should load the next data set */
- if ( $(this).scrollTop() + $(this).height() >
- $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
- {
- /* Only do the redraw if we have to - we might be at the end of the data */
- if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
- {
- _fnPageChange( oSettings, 'next' );
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
- }
- }
- }
- } );
- }
-
- oSettings.nScrollHead = nScrollHead;
- oSettings.nScrollFoot = nScrollFoot;
-
- return nScroller;
+
+ param( 'iSortingCols', sort.length );
}
-
-
- /**
- * Update the various tables for resizing. It's a bit of a pig this function, but
- * basically the idea to:
- * 1. Re-create the table inside the scrolling div
- * 2. Take live measurements from the DOM
- * 3. Apply the measurements
- * 4. Clean up
- * @param {object} o dataTables settings object
- * @returns {node} Node to add to the DOM
- * @memberof DataTable#oApi
- */
- function _fnScrollDraw ( o )
- {
- var
- nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
- nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
- nScrollBody = o.nTable.parentNode,
- i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
- nTheadSize, nTfootSize,
- iWidth, aApplied=[], aAppliedFooter=[], iSanityWidth,
- nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null,
- nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null,
- ie67 = o.oBrowser.bScrollOversize,
- zeroOut = function(nSizer) {
- oStyle = nSizer.style;
- oStyle.paddingTop = "0";
- oStyle.paddingBottom = "0";
- oStyle.borderTopWidth = "0";
- oStyle.borderBottomWidth = "0";
- oStyle.height = 0;
- };
-
- /*
- * 1. Re-create the table inside the scrolling div
- */
-
- /* Remove the old minimised thead and tfoot elements in the inner table */
- $(o.nTable).children('thead, tfoot').remove();
-
- /* Clone the current header and footer elements and then place it into the inner table */
- nTheadSize = $(o.nTHead).clone()[0];
- o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
- anHeadToSize = o.nTHead.getElementsByTagName('tr');
- anHeadSizers = nTheadSize.getElementsByTagName('tr');
-
- if ( o.nTFoot !== null )
- {
- nTfootSize = $(o.nTFoot).clone()[0];
- o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
- anFootToSize = o.nTFoot.getElementsByTagName('tr');
- anFootSizers = nTfootSize.getElementsByTagName('tr');
- }
-
- /*
- * 2. Take live measurements from the DOM - do not alter the DOM itself!
- */
-
- /* Remove old sizing and apply the calculated column widths
- * Get the unique column headers in the newly created (cloned) header. We want to apply the
- * calculated sizes to this header
- */
- if ( o.oScroll.sX === "" )
- {
- nScrollBody.style.width = '100%';
- nScrollHeadInner.parentNode.style.width = '100%';
- }
-
- var nThs = _fnGetUniqueThs( o, nTheadSize );
- for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
- {
- iVis = _fnVisibleToColumnIndex( o, i );
- nThs[i].style.width = o.aoColumns[iVis].sWidth;
- }
-
- if ( o.nTFoot !== null )
- {
- _fnApplyToChildren( function(n) {
- n.style.width = "";
- }, anFootSizers );
- }
-
- // If scroll collapse is enabled, when we put the headers back into the body for sizing, we
- // will end up forcing the scrollbar to appear, making our measurements wrong for when we
- // then hide it (end of this function), so add the header height to the body scroller.
- if ( o.oScroll.bCollapse && o.oScroll.sY !== "" )
- {
- nScrollBody.style.height = (nScrollBody.offsetHeight + o.nTHead.offsetHeight)+"px";
+
+ // If the legacy.ajax parameter is null, then we automatically decide which
+ // form to use, based on sAjaxSource
+ var legacy = DataTable.ext.legacy.ajax;
+ if ( legacy === null ) {
+ return settings.sAjaxSource ? data : d;
+ }
+
+ // Otherwise, if legacy has been specified then we use that to decide on the
+ // form
+ return legacy ? data : d;
+ }
+
+
+ /**
+ * Data the data from the server (nuking the old) and redraw the table
+ * @param {object} oSettings dataTables settings object
+ * @param {object} json json data return from the server.
+ * @param {string} json.sEcho Tracking flag for DataTables to match requests
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
+ * @param {array} json.aaData The data to display on this page
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdateDraw ( settings, json )
+ {
+ // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
+ // Support both
+ var compat = function ( old, modern ) {
+ return json[old] !== undefined ? json[old] : json[modern];
+ };
+
+ var data = _fnAjaxDataSrc( settings, json );
+ var draw = compat( 'sEcho', 'draw' );
+ var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
+ var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
+
+ if ( draw ) {
+ // Protect against out of sequence returns
+ if ( draw*1 < settings.iDraw ) {
+ return;
}
-
- /* Size the table as a whole */
- iSanityWidth = $(o.nTable).outerWidth();
- if ( o.oScroll.sX === "" )
- {
- /* No x scrolling */
- o.nTable.style.width = "100%";
-
- /* I know this is rubbish - but IE7 will make the width of the table when 100% include
- * the scrollbar - which is shouldn't. When there is a scrollbar we need to take this
- * into account.
- */
- if ( ie67 && ($('tbody', nScrollBody).height() > nScrollBody.offsetHeight ||
- $(nScrollBody).css('overflow-y') == "scroll") )
- {
- o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth);
- }
+ settings.iDraw = draw * 1;
+ }
+
+ _fnClearTable( settings );
+ settings._iRecordsTotal = parseInt(recordsTotal, 10);
+ settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
+
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ _fnAddData( settings, data[i] );
+ }
+ settings.aiDisplay = settings.aiDisplayMaster.slice();
+
+ settings.bAjaxDataGet = false;
+ _fnDraw( settings );
+
+ if ( ! settings._bInitComplete ) {
+ _fnInitComplete( settings, json );
+ }
+
+ settings.bAjaxDataGet = true;
+ _fnProcessingDisplay( settings, false );
+ }
+
+
+ /**
+ * Get the data from the JSON data source to use for drawing a table. Using
+ * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
+ * source object, or from a processing function.
+ * @param {object} oSettings dataTables settings object
+ * @param {object} json Data source object / array from the server
+ * @return {array} Array of data to use
+ */
+ function _fnAjaxDataSrc ( oSettings, json )
+ {
+ var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
+ oSettings.ajax.dataSrc :
+ oSettings.sAjaxDataProp; // Compatibility with 1.9-.
+
+ // Compatibility with 1.9-. In order to read from aaData, check if the
+ // default has been changed, if not, check for aaData
+ if ( dataSrc === 'data' ) {
+ return json.aaData || json[dataSrc];
+ }
+
+ return dataSrc !== "" ?
+ _fnGetObjectDataFn( dataSrc )( json ) :
+ json;
+ }
+
+ /**
+ * Generate the node required for filtering text
+ * @returns {node} Filter control element
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlFilter ( settings )
+ {
+ var classes = settings.oClasses;
+ var tableId = settings.sTableId;
+ var language = settings.oLanguage;
+ var previousSearch = settings.oPreviousSearch;
+ var features = settings.aanFeatures;
+ var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
+
+ var str = language.sSearch;
+ str = str.match(/_INPUT_/) ?
+ str.replace('_INPUT_', input) :
+ str+input;
+
+ var filter = $('<div/>', {
+ 'id': ! features.f ? tableId+'_filter' : null,
+ 'class': classes.sFilter
+ } )
+ .append( $('<label/>' ).append( str ) );
+
+ var searchFn = function() {
+ /* Update all other filter input elements for the new display */
+ var n = features.f;
+ var val = !this.value ? "" : this.value; // mental IE8 fix :-(
+
+ /* Now do the filter */
+ if ( val != previousSearch.sSearch ) {
+ _fnFilterComplete( settings, {
+ "sSearch": val,
+ "bRegex": previousSearch.bRegex,
+ "bSmart": previousSearch.bSmart ,
+ "bCaseInsensitive": previousSearch.bCaseInsensitive
+ } );
+
+ // Need to redraw, without resorting
+ settings._iDisplayStart = 0;
+ _fnDraw( settings );
}
- else
- {
- if ( o.oScroll.sXInner !== "" )
- {
- /* x scroll inner has been given - use it */
- o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
+ };
+
+ var searchDelay = settings.searchDelay !== null ?
+ settings.searchDelay :
+ _fnDataSource( settings ) === 'ssp' ?
+ 400 :
+ 0;
+
+ var jqFilter = $('input', filter)
+ .val( previousSearch.sSearch )
+ .attr( 'placeholder', language.sSearchPlaceholder )
+ .bind(
+ 'keyup.DT search.DT input.DT paste.DT cut.DT',
+ searchDelay ?
+ _fnThrottle( searchFn, searchDelay ) :
+ searchFn
+ )
+ .bind( 'keypress.DT', function(e) {
+ /* Prevent form submission */
+ if ( e.keyCode == 13 ) {
+ return false;
}
- else if ( iSanityWidth == $(nScrollBody).width() &&
- $(nScrollBody).height() < $(o.nTable).height() )
- {
- /* There is y-scrolling - try to take account of the y scroll bar */
- o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
- if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
- {
- /* Not possible to take account of it */
- o.nTable.style.width = _fnStringToCss( iSanityWidth );
+ } )
+ .attr('aria-controls', tableId);
+
+ // Update the input elements whenever the table is filtered
+ $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
+ if ( settings === s ) {
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame...
+ try {
+ if ( jqFilter[0] !== document.activeElement ) {
+ jqFilter.val( previousSearch.sSearch );
}
}
- else
- {
- /* All else fails */
- o.nTable.style.width = _fnStringToCss( iSanityWidth );
- }
+ catch ( e ) {}
}
-
- /* Recalculate the sanity width - now that we've applied the required width, before it was
- * a temporary variable. This is required because the column width calculation is done
- * before this table DOM is created.
- */
- iSanityWidth = $(o.nTable).outerWidth();
-
- /* We want the hidden header to have zero height, so remove padding and borders. Then
- * set the width based on the real headers
- */
-
- // Apply all styles in one pass. Invalidates layout only once because we don't read any
- // DOM properties.
- _fnApplyToChildren( zeroOut, anHeadSizers );
-
- // Read all widths in next pass. Forces layout only once because we do not change
- // any DOM properties.
- _fnApplyToChildren( function(nSizer) {
- aApplied.push( _fnStringToCss( $(nSizer).width() ) );
- }, anHeadSizers );
-
- // Apply all widths in final pass. Invalidates layout only once because we do not
- // read any DOM properties.
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = aApplied[i];
- }, anHeadToSize );
-
- $(anHeadSizers).height(0);
-
- /* Same again with the footer if we have one */
- if ( o.nTFoot !== null )
- {
- _fnApplyToChildren( zeroOut, anFootSizers );
-
- _fnApplyToChildren( function(nSizer) {
- aAppliedFooter.push( _fnStringToCss( $(nSizer).width() ) );
- }, anFootSizers );
-
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = aAppliedFooter[i];
- }, anFootToSize );
-
- $(anFootSizers).height(0);
- }
-
- /*
- * 3. Apply the measurements
- */
-
- /* "Hide" the header and footer that we used for the sizing. We want to also fix their width
- * to what they currently are
- */
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = "";
- nSizer.style.width = aApplied[i];
- }, anHeadSizers );
-
- if ( o.nTFoot !== null )
+ } );
+
+ return filter[0];
+ }
+
+
+ /**
+ * Filter the table using both the global filter and column based filtering
+ * @param {object} oSettings dataTables settings object
+ * @param {object} oSearch search information
+ * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterComplete ( oSettings, oInput, iForce )
+ {
+ var oPrevSearch = oSettings.oPreviousSearch;
+ var aoPrevSearch = oSettings.aoPreSearchCols;
+ var fnSaveFilter = function ( oFilter ) {
+ /* Save the filtering values */
+ oPrevSearch.sSearch = oFilter.sSearch;
+ oPrevSearch.bRegex = oFilter.bRegex;
+ oPrevSearch.bSmart = oFilter.bSmart;
+ oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
+ };
+ var fnRegex = function ( o ) {
+ // Backwards compatibility with the bEscapeRegex option
+ return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
+ };
+
+ // Resolve any column types that are unknown due to addition or invalidation
+ // @todo As per sort - can this be moved into an event handler?
+ _fnColumnTypes( oSettings );
+
+ /* In server-side processing all filtering is done by the server, so no point hanging around here */
+ if ( _fnDataSource( oSettings ) != 'ssp' )
+ {
+ /* Global filter */
+ _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
+ fnSaveFilter( oInput );
+
+ /* Now do the individual column filter */
+ for ( var i=0 ; i<aoPrevSearch.length ; i++ )
{
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = "";
- nSizer.style.width = aAppliedFooter[i];
- }, anFootSizers );
+ _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
+ aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
}
-
- /* Sanity check that the table is of a sensible width. If not then we are going to get
- * misalignment - try to prevent this by not allowing the table to shrink below its min width
- */
- if ( $(o.nTable).outerWidth() < iSanityWidth )
- {
- /* The min width depends upon if we have a vertical scrollbar visible or not */
- var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight ||
- $(nScrollBody).css('overflow-y') == "scroll")) ?
- iSanityWidth+o.oScroll.iBarWidth : iSanityWidth;
-
- /* IE6/7 are a law unto themselves... */
- if ( ie67 && (nScrollBody.scrollHeight >
- nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll") )
- {
- o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth );
- }
-
- /* Apply the calculated minimum width to the table wrappers */
- nScrollBody.style.width = _fnStringToCss( iCorrection );
- o.nScrollHead.style.width = _fnStringToCss( iCorrection );
-
- if ( o.nTFoot !== null )
- {
- o.nScrollFoot.style.width = _fnStringToCss( iCorrection );
- }
-
- /* And give the user a warning that we've stopped the table getting too small */
- if ( o.oScroll.sX === "" )
- {
- _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
- " misalignment. The table has been drawn at its minimum possible width." );
- }
- else if ( o.oScroll.sXInner !== "" )
- {
- _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
- " misalignment. Increase the sScrollXInner value or remove it to allow automatic"+
- " calculation" );
+
+ /* Custom filtering */
+ _fnFilterCustom( oSettings );
+ }
+ else
+ {
+ fnSaveFilter( oInput );
+ }
+
+ /* Tell the draw function we have been filtering */
+ oSettings.bFiltered = true;
+ _fnCallbackFire( oSettings, null, 'search', [oSettings] );
+ }
+
+
+ /**
+ * Apply custom filtering functions
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterCustom( settings )
+ {
+ var filters = DataTable.ext.search;
+ var displayRows = settings.aiDisplay;
+ var row, rowIdx;
+
+ for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
+ var rows = [];
+
+ // Loop over each row and see if it should be included
+ for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
+ rowIdx = displayRows[ j ];
+ row = settings.aoData[ rowIdx ];
+
+ if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
+ rows.push( rowIdx );
}
}
- else
- {
- nScrollBody.style.width = _fnStringToCss( '100%' );
- o.nScrollHead.style.width = _fnStringToCss( '100%' );
-
- if ( o.nTFoot !== null )
- {
- o.nScrollFoot.style.width = _fnStringToCss( '100%' );
- }
+
+ // So the array reference doesn't break set the results into the
+ // existing array
+ displayRows.length = 0;
+ $.merge( displayRows, rows );
+ }
+ }
+
+
+ /**
+ * Filter the table on a per-column basis
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sInput string to filter on
+ * @param {int} iColumn column to filter
+ * @param {bool} bRegex treat search string as a regular expression or not
+ * @param {bool} bSmart use smart filtering or not
+ * @param {bool} bCaseInsensitive Do case insenstive matching or not
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
+ {
+ if ( searchStr === '' ) {
+ return;
+ }
+
+ var data;
+ var display = settings.aiDisplay;
+ var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
+
+ for ( var i=display.length-1 ; i>=0 ; i-- ) {
+ data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
+
+ if ( ! rpSearch.test( data ) ) {
+ display.splice( i, 1 );
}
-
-
- /*
- * 4. Clean up
- */
- if ( o.oScroll.sY === "" )
- {
- /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
- * the scrollbar height from the visible display, rather than adding it on. We need to
- * set the height in order to sort this. Don't want to do it in any other browsers.
- */
- if ( ie67 )
- {
- nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
- }
+ }
+ }
+
+
+ /**
+ * Filter the data table based on user input and draw the table
+ * @param {object} settings dataTables settings object
+ * @param {string} input string to filter on
+ * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
+ * @param {bool} regex treat as a regular expression or not
+ * @param {bool} smart perform smart filtering or not
+ * @param {bool} caseInsensitive Do case insenstive matching or not
+ * @memberof DataTable#oApi
+ */
+ function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
+ {
+ var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
+ var prevSearch = settings.oPreviousSearch.sSearch;
+ var displayMaster = settings.aiDisplayMaster;
+ var display, invalidated, i;
+
+ // Need to take account of custom filtering functions - always filter
+ if ( DataTable.ext.search.length !== 0 ) {
+ force = true;
+ }
+
+ // Check if any of the rows were invalidated
+ invalidated = _fnFilterData( settings );
+
+ // If the input is blank - we just want the full data set
+ if ( input.length <= 0 ) {
+ settings.aiDisplay = displayMaster.slice();
+ }
+ else {
+ // New search - start from the master array
+ if ( invalidated ||
+ force ||
+ prevSearch.length > input.length ||
+ input.indexOf(prevSearch) !== 0 ||
+ settings.bSorted // On resort, the display master needs to be
+ // re-filtered since indexes will have changed
+ ) {
+ settings.aiDisplay = displayMaster.slice();
}
-
- if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
- {
- nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
-
- var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
- o.oScroll.iBarWidth : 0;
- if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
- {
- nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra );
+
+ // Search the display array
+ display = settings.aiDisplay;
+
+ for ( i=display.length-1 ; i>=0 ; i-- ) {
+ if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
+ display.splice( i, 1 );
}
}
-
- /* Finally set the width's of the header and footer tables */
- var iOuterWidth = $(o.nTable).outerWidth();
- nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
- nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth );
-
- // Figure out if there are scrollbar present - if so then we need a the header and footer to
- // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
- var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll";
- nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
-
- if ( o.nTFoot !== null )
- {
- nScrollFootTable.style.width = _fnStringToCss( iOuterWidth );
- nScrollFootInner.style.width = _fnStringToCss( iOuterWidth );
- nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
- }
-
- /* Adjust the position of the header in case we loose the y-scrollbar */
- $(nScrollBody).scroll();
-
- /* If sorting or filtering has occurred, jump the scrolling back to the top */
- if ( o.bSorted || o.bFiltered )
- {
- nScrollBody.scrollTop = 0;
- }
}
-
-
- /**
- * Apply a given function to the display child nodes of an element array (typically
- * TD children of TR rows
- * @param {function} fn Method to apply to the objects
- * @param array {nodes} an1 List of elements to look through for display children
- * @param array {nodes} an2 Another list (identical structure to the first) - optional
- * @memberof DataTable#oApi
- */
- function _fnApplyToChildren( fn, an1, an2 )
- {
- var index=0, i=0, iLen=an1.length;
- var nNode1, nNode2;
-
- while ( i < iLen )
- {
- nNode1 = an1[i].firstChild;
- nNode2 = an2 ? an2[i].firstChild : null;
- while ( nNode1 )
- {
- if ( nNode1.nodeType === 1 )
- {
- if ( an2 )
- {
- fn( nNode1, nNode2, index );
+ }
+
+
+ /**
+ * Build a regular expression object suitable for searching a table
+ * @param {string} sSearch string to search for
+ * @param {bool} bRegex treat as a regular expression or not
+ * @param {bool} bSmart perform smart filtering or not
+ * @param {bool} bCaseInsensitive Do case insensitive matching or not
+ * @returns {RegExp} constructed object
+ * @memberof DataTable#oApi
+ */
+ function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
+ {
+ search = regex ?
+ search :
+ _fnEscapeRegex( search );
+
+ if ( smart ) {
+ /* For smart filtering we want to allow the search to work regardless of
+ * word order. We also want double quoted text to be preserved, so word
+ * order is important - a la google. So this is what we want to
+ * generate:
+ *
+ * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
+ */
+ var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
+ if ( word.charAt(0) === '"' ) {
+ var m = word.match( /^"(.*)"$/ );
+ word = m ? m[1] : word;
+ }
+
+ return word.replace('"', '');
+ } );
+
+ search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
+ }
+
+ return new RegExp( search, caseInsensitive ? 'i' : '' );
+ }
+
+
+ /**
+ * Escape a string such that it can be used in a regular expression
+ * @param {string} sVal string to escape
+ * @returns {string} escaped string
+ * @memberof DataTable#oApi
+ */
+ function _fnEscapeRegex ( sVal )
+ {
+ return sVal.replace( _re_escape_regex, '\\$1' );
+ }
+
+
+
+ var __filter_div = $('<div>')[0];
+ var __filter_div_textContent = __filter_div.textContent !== undefined;
+
+ // Update the filtering data for each row if needed (by invalidation or first run)
+ function _fnFilterData ( settings )
+ {
+ var columns = settings.aoColumns;
+ var column;
+ var i, j, ien, jen, filterData, cellData, row;
+ var fomatters = DataTable.ext.type.search;
+ var wasInvalidated = false;
+
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ row = settings.aoData[i];
+
+ if ( ! row._aFilterData ) {
+ filterData = [];
+
+ for ( j=0, jen=columns.length ; j<jen ; j++ ) {
+ column = columns[j];
+
+ if ( column.bSearchable ) {
+ cellData = _fnGetCellData( settings, i, j, 'filter' );
+
+ if ( fomatters[ column.sType ] ) {
+ cellData = fomatters[ column.sType ]( cellData );
}
- else
- {
- fn( nNode1, index );
+
+ // Search in DataTables 1.10 is string based. In 1.11 this
+ // should be altered to also allow strict type checking.
+ if ( cellData === null ) {
+ cellData = '';
+ }
+
+ if ( typeof cellData !== 'string' && cellData.toString ) {
+ cellData = cellData.toString();
}
- index++;
}
- nNode1 = nNode1.nextSibling;
- nNode2 = an2 ? nNode2.nextSibling : null;
+ else {
+ cellData = '';
+ }
+
+ // If it looks like there is an HTML entity in the string,
+ // attempt to decode it so sorting works as expected. Note that
+ // we could use a single line of jQuery to do this, but the DOM
+ // method used here is much faster http://jsperf.com/html-decode
+ if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
+ __filter_div.innerHTML = cellData;
+ cellData = __filter_div_textContent ?
+ __filter_div.textContent :
+ __filter_div.innerText;
+ }
+
+ if ( cellData.replace ) {
+ cellData = cellData.replace(/[\r\n]/g, '');
+ }
+
+ filterData.push( cellData );
}
- i++;
+
+ row._aFilterData = filterData;
+ row._sFilterRow = filterData.join(' ');
+ wasInvalidated = true;
}
}
-
- /**
- * Convert a CSS unit width to pixels (e.g. 2em)
- * @param {string} sWidth width to be converted
- * @param {node} nParent parent to get the with for (required for relative widths) - optional
- * @returns {int} iWidth width in pixels
- * @memberof DataTable#oApi
- */
- function _fnConvertToWidth ( sWidth, nParent )
- {
- if ( !sWidth || sWidth === null || sWidth === '' )
- {
- return 0;
- }
-
- if ( !nParent )
- {
- nParent = document.body;
+
+ return wasInvalidated;
+ }
+
+
+ /**
+ * Convert from the internal Hungarian notation to camelCase for external
+ * interaction
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToCamel ( obj )
+ {
+ return {
+ search: obj.sSearch,
+ smart: obj.bSmart,
+ regex: obj.bRegex,
+ caseInsensitive: obj.bCaseInsensitive
+ };
+ }
+
+
+
+ /**
+ * Convert from camelCase notation to the internal Hungarian. We could use the
+ * Hungarian convert function here, but this is cleaner
+ * @param {object} obj Object to convert
+ * @returns {object} Inverted object
+ * @memberof DataTable#oApi
+ */
+ function _fnSearchToHung ( obj )
+ {
+ return {
+ sSearch: obj.search,
+ bSmart: obj.smart,
+ bRegex: obj.regex,
+ bCaseInsensitive: obj.caseInsensitive
+ };
+ }
+
+ /**
+ * Generate the node required for the info display
+ * @param {object} oSettings dataTables settings object
+ * @returns {node} Information element
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlInfo ( settings )
+ {
+ var
+ tid = settings.sTableId,
+ nodes = settings.aanFeatures.i,
+ n = $('<div/>', {
+ 'class': settings.oClasses.sInfo,
+ 'id': ! nodes ? tid+'_info' : null
+ } );
+
+ if ( ! nodes ) {
+ // Update display on each draw
+ settings.aoDrawCallback.push( {
+ "fn": _fnUpdateInfo,
+ "sName": "information"
+ } );
+
+ n
+ .attr( 'role', 'status' )
+ .attr( 'aria-live', 'polite' );
+
+ // Table is described by our info div
+ $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
+ }
+
+ return n[0];
+ }
+
+
+ /**
+ * Update the information elements in the display
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnUpdateInfo ( settings )
+ {
+ /* Show information about the table */
+ var nodes = settings.aanFeatures.i;
+ if ( nodes.length === 0 ) {
+ return;
+ }
+
+ var
+ lang = settings.oLanguage,
+ start = settings._iDisplayStart+1,
+ end = settings.fnDisplayEnd(),
+ max = settings.fnRecordsTotal(),
+ total = settings.fnRecordsDisplay(),
+ out = total ?
+ lang.sInfo :
+ lang.sInfoEmpty;
+
+ if ( total !== max ) {
+ /* Record set after filtering */
+ out += ' ' + lang.sInfoFiltered;
+ }
+
+ // Convert the macros
+ out += lang.sInfoPostFix;
+ out = _fnInfoMacros( settings, out );
+
+ var callback = lang.fnInfoCallback;
+ if ( callback !== null ) {
+ out = callback.call( settings.oInstance,
+ settings, start, end, max, total, out
+ );
+ }
+
+ $(nodes).html( out );
+ }
+
+
+ function _fnInfoMacros ( settings, str )
+ {
+ // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
+ // internally
+ var
+ formatter = settings.fnFormatNumber,
+ start = settings._iDisplayStart+1,
+ len = settings._iDisplayLength,
+ vis = settings.fnRecordsDisplay(),
+ all = len === -1;
+
+ return str.
+ replace(/_START_/g, formatter.call( settings, start ) ).
+ replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
+ replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
+ replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
+ replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
+ replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
+ }
+
+
+
+ /**
+ * Draw the table for the first time, adding all required features
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnInitialise ( settings )
+ {
+ var i, iLen, iAjaxStart=settings.iInitDisplayStart;
+ var columns = settings.aoColumns, column;
+ var features = settings.oFeatures;
+ var deferLoading = settings.bDeferLoading; // value modified by the draw
+
+ /* Ensure that the table data is fully initialised */
+ if ( ! settings.bInitialised ) {
+ setTimeout( function(){ _fnInitialise( settings ); }, 200 );
+ return;
+ }
+
+ /* Show the display HTML options */
+ _fnAddOptionsHtml( settings );
+
+ /* Build and draw the header / footer for the table */
+ _fnBuildHead( settings );
+ _fnDrawHead( settings, settings.aoHeader );
+ _fnDrawHead( settings, settings.aoFooter );
+
+ /* Okay to show that something is going on now */
+ _fnProcessingDisplay( settings, true );
+
+ /* Calculate sizes for columns */
+ if ( features.bAutoWidth ) {
+ _fnCalculateColumnWidths( settings );
+ }
+
+ for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
+ column = columns[i];
+
+ if ( column.sWidth ) {
+ column.nTh.style.width = _fnStringToCss( column.sWidth );
}
-
- var iWidth;
- var nTmp = document.createElement( "div" );
- nTmp.style.width = _fnStringToCss( sWidth );
-
- nParent.appendChild( nTmp );
- iWidth = nTmp.offsetWidth;
- nParent.removeChild( nTmp );
-
- return ( iWidth );
}
-
-
- /**
- * Calculate the width of columns for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateColumnWidths ( oSettings )
- {
- var iTableWidth = oSettings.nTable.offsetWidth;
- var iUserInputs = 0;
- var iTmpWidth;
- var iVisibleColumns = 0;
- var iColums = oSettings.aoColumns.length;
- var i, iIndex, iCorrector, iWidth;
- var oHeaders = $('th', oSettings.nTHead);
- var widthAttr = oSettings.nTable.getAttribute('width');
- var nWrapper = oSettings.nTable.parentNode;
-
- /* Convert any user input sizes into pixel sizes */
- for ( i=0 ; i<iColums ; i++ )
- {
- if ( oSettings.aoColumns[i].bVisible )
- {
- iVisibleColumns++;
-
- if ( oSettings.aoColumns[i].sWidth !== null )
- {
- iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig,
- nWrapper );
- if ( iTmpWidth !== null )
- {
- oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
- }
-
- iUserInputs++;
+
+ _fnCallbackFire( settings, null, 'preInit', [settings] );
+
+ // If there is default sorting required - let's do it. The sort function
+ // will do the drawing for us. Otherwise we draw the table regardless of the
+ // Ajax source - this allows the table to look initialised for Ajax sourcing
+ // data (show 'loading' message possibly)
+ _fnReDraw( settings );
+
+ // Server-side processing init complete is done by _fnAjaxUpdateDraw
+ var dataSrc = _fnDataSource( settings );
+ if ( dataSrc != 'ssp' || deferLoading ) {
+ // if there is an ajax source load the data
+ if ( dataSrc == 'ajax' ) {
+ _fnBuildAjax( settings, [], function(json) {
+ var aData = _fnAjaxDataSrc( settings, json );
+
+ // Got the data - add it to the table
+ for ( i=0 ; i<aData.length ; i++ ) {
+ _fnAddData( settings, aData[i] );
}
- }
+
+ // Reset the init display for cookie saving. We've already done
+ // a filter, and therefore cleared it before. So we need to make
+ // it appear 'fresh'
+ settings.iInitDisplayStart = iAjaxStart;
+
+ _fnReDraw( settings );
+
+ _fnProcessingDisplay( settings, false );
+ _fnInitComplete( settings, json );
+ }, settings );
}
-
- /* If the number of columns in the DOM equals the number that we have to process in
- * DataTables, then we can use the offsets that are created by the web-browser. No custom
- * sizes can be set in order for this to happen, nor scrolling used
- */
- if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
- oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
- {
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- iTmpWidth = $(oHeaders[i]).width();
- if ( iTmpWidth !== null )
- {
- oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
- }
- }
+ else {
+ _fnProcessingDisplay( settings, false );
+ _fnInitComplete( settings );
}
- else
- {
- /* Otherwise we are going to have to do some calculations to get the width of each column.
- * Construct a 1 row table with the widest node in the data, and any user defined widths,
- * then insert it into the DOM and allow the browser to do all the hard work of
- * calculating table widths.
- */
- var
- nCalcTmp = oSettings.nTable.cloneNode( false ),
- nTheadClone = oSettings.nTHead.cloneNode(true),
- nBody = document.createElement( 'tbody' ),
- nTr = document.createElement( 'tr' ),
- nDivSizing;
-
- nCalcTmp.removeAttribute( "id" );
- nCalcTmp.appendChild( nTheadClone );
- if ( oSettings.nTFoot !== null )
- {
- nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
- _fnApplyToChildren( function(n) {
- n.style.width = "";
- }, nCalcTmp.getElementsByTagName('tr') );
- }
-
- nCalcTmp.appendChild( nBody );
- nBody.appendChild( nTr );
-
- /* Remove any sizing that was previously applied by the styles */
- var jqColSizing = $('thead th', nCalcTmp);
- if ( jqColSizing.length === 0 )
- {
- jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
- }
-
- /* Apply custom sizing to the cloned header */
- var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
- iCorrector = 0;
- for ( i=0 ; i<iColums ; i++ )
- {
- var oColumn = oSettings.aoColumns[i];
- if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
- {
- nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
- }
- else if ( oColumn.bVisible )
- {
- nThs[i-iCorrector].style.width = "";
- }
- else
- {
- iCorrector++;
- }
- }
-
- /* Find the biggest td for each column and put it into the table */
- for ( i=0 ; i<iColums ; i++ )
- {
- if ( oSettings.aoColumns[i].bVisible )
- {
- var nTd = _fnGetWidestNode( oSettings, i );
- if ( nTd !== null )
- {
- nTd = nTd.cloneNode(true);
- if ( oSettings.aoColumns[i].sContentPadding !== "" )
- {
- nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
- }
- nTr.appendChild( nTd );
- }
- }
- }
-
- /* Build the table and 'display' it */
- nWrapper.appendChild( nCalcTmp );
-
- /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
- * when not scrolling leave the table width as it is. This results in slightly different,
- * but I think correct behaviour
- */
- if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
- {
- nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
- }
- else if ( oSettings.oScroll.sX !== "" )
- {
- nCalcTmp.style.width = "";
- if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
- {
- nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
- }
- }
- else if ( oSettings.oScroll.sY !== "" )
- {
- nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
- }
- else if ( widthAttr )
- {
- nCalcTmp.style.width = _fnStringToCss( widthAttr );
- }
- nCalcTmp.style.visibility = "hidden";
-
- /* Scrolling considerations */
- _fnScrollingWidthAdjust( oSettings, nCalcTmp );
-
- /* Read the width's calculated by the browser and store them for use by the caller. We
- * first of all try to use the elements in the body, but it is possible that there are
- * no elements there, under which circumstances we use the header elements
- */
- var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
- if ( oNodes.length === 0 )
- {
- oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
- }
-
- /* Browsers need a bit of a hand when a width is assigned to any columns when
- * x-scrolling as they tend to collapse the table to the min-width, even if
- * we sent the column widths. So we need to keep track of what the table width
- * should be by summing the user given values, and the automatic values
- */
- if ( oSettings.oScroll.sX !== "" )
- {
- var iTotal = 0;
- iCorrector = 0;
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- if ( oSettings.aoColumns[i].bVisible )
- {
- if ( oSettings.aoColumns[i].sWidthOrig === null )
- {
- iTotal += $(oNodes[iCorrector]).outerWidth();
- }
- else
- {
- iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
- ($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
- }
- iCorrector++;
+ }
+ }
+
+
+ /**
+ * Draw the table for the first time, adding all required features
+ * @param {object} oSettings dataTables settings object
+ * @param {object} [json] JSON from the server that completed the table, if using Ajax source
+ * with client-side processing (optional)
+ * @memberof DataTable#oApi
+ */
+ function _fnInitComplete ( settings, json )
+ {
+ settings._bInitComplete = true;
+
+ // When data was added after the initialisation (data or Ajax) we need to
+ // calculate the column sizing
+ if ( json || settings.oInit.aaData ) {
+ _fnAdjustColumnSizing( settings );
+ }
+
+ _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
+ _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
+ }
+
+
+ function _fnLengthChange ( settings, val )
+ {
+ var len = parseInt( val, 10 );
+ settings._iDisplayLength = len;
+
+ _fnLengthOverflow( settings );
+
+ // Fire length change event
+ _fnCallbackFire( settings, null, 'length', [settings, len] );
+ }
+
+
+ /**
+ * Generate the node required for user display length changing
+ * @param {object} settings dataTables settings object
+ * @returns {node} Display length feature node
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlLength ( settings )
+ {
+ var
+ classes = settings.oClasses,
+ tableId = settings.sTableId,
+ menu = settings.aLengthMenu,
+ d2 = $.isArray( menu[0] ),
+ lengths = d2 ? menu[0] : menu,
+ language = d2 ? menu[1] : menu;
+
+ var select = $('<select/>', {
+ 'name': tableId+'_length',
+ 'aria-controls': tableId,
+ 'class': classes.sLengthSelect
+ } );
+
+ for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
+ select[0][ i ] = new Option( language[i], lengths[i] );
+ }
+
+ var div = $('<div><label/></div>').addClass( classes.sLength );
+ if ( ! settings.aanFeatures.l ) {
+ div[0].id = tableId+'_length';
+ }
+
+ div.children().append(
+ settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
+ );
+
+ // Can't use `select` variable as user might provide their own and the
+ // reference is broken by the use of outerHTML
+ $('select', div)
+ .val( settings._iDisplayLength )
+ .bind( 'change.DT', function(e) {
+ _fnLengthChange( settings, $(this).val() );
+ _fnDraw( settings );
+ } );
+
+ // Update node value whenever anything changes the table's length
+ $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
+ if ( settings === s ) {
+ $('select', div).val( len );
+ }
+ } );
+
+ return div[0];
+ }
+
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Note that most of the paging logic is done in
+ * DataTable.ext.pager
+ */
+
+ /**
+ * Generate the node required for default pagination
+ * @param {object} oSettings dataTables settings object
+ * @returns {node} Pagination feature node
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlPaginate ( settings )
+ {
+ var
+ type = settings.sPaginationType,
+ plugin = DataTable.ext.pager[ type ],
+ modern = typeof plugin === 'function',
+ redraw = function( settings ) {
+ _fnDraw( settings );
+ },
+ node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
+ features = settings.aanFeatures;
+
+ if ( ! modern ) {
+ plugin.fnInit( settings, node, redraw );
+ }
+
+ /* Add a draw callback for the pagination on first instance, to update the paging display */
+ if ( ! features.p )
+ {
+ node.id = settings.sTableId+'_paginate';
+
+ settings.aoDrawCallback.push( {
+ "fn": function( settings ) {
+ if ( modern ) {
+ var
+ start = settings._iDisplayStart,
+ len = settings._iDisplayLength,
+ visRecords = settings.fnRecordsDisplay(),
+ all = len === -1,
+ page = all ? 0 : Math.ceil( start / len ),
+ pages = all ? 1 : Math.ceil( visRecords / len ),
+ buttons = plugin(page, pages),
+ i, ien;
+
+ for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
+ _fnRenderer( settings, 'pageButton' )(
+ settings, features.p[i], i, buttons, page, pages
+ );
}
}
-
- nCalcTmp.style.width = _fnStringToCss( iTotal );
- oSettings.nTable.style.width = _fnStringToCss( iTotal );
- }
-
- iCorrector = 0;
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- if ( oSettings.aoColumns[i].bVisible )
- {
- iWidth = $(oNodes[iCorrector]).width();
- if ( iWidth !== null && iWidth > 0 )
- {
- oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
- }
- iCorrector++;
+ else {
+ plugin.fnUpdate( settings, redraw );
}
- }
-
- var cssWidth = $(nCalcTmp).css('width');
- oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
- cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
- nCalcTmp.parentNode.removeChild( nCalcTmp );
- }
-
- if ( widthAttr )
+ },
+ "sName": "pagination"
+ } );
+ }
+
+ return node;
+ }
+
+
+ /**
+ * Alter the display settings to change the page
+ * @param {object} settings DataTables settings object
+ * @param {string|int} action Paging action to take: "first", "previous",
+ * "next" or "last" or page number to jump to (integer)
+ * @param [bool] redraw Automatically draw the update or not
+ * @returns {bool} true page has changed, false - no change
+ * @memberof DataTable#oApi
+ */
+ function _fnPageChange ( settings, action, redraw )
+ {
+ var
+ start = settings._iDisplayStart,
+ len = settings._iDisplayLength,
+ records = settings.fnRecordsDisplay();
+
+ if ( records === 0 || len === -1 )
+ {
+ start = 0;
+ }
+ else if ( typeof action === "number" )
+ {
+ start = action * len;
+
+ if ( start > records )
{
- oSettings.nTable.style.width = _fnStringToCss( widthAttr );
+ start = 0;
}
}
-
-
- /**
- * Adjust a table's width to take account of scrolling
- * @param {object} oSettings dataTables settings object
- * @param {node} n table node
- * @memberof DataTable#oApi
- */
- function _fnScrollingWidthAdjust ( oSettings, n )
+ else if ( action == "first" )
+ {
+ start = 0;
+ }
+ else if ( action == "previous" )
{
- if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
+ start = len >= 0 ?
+ start - len :
+ 0;
+
+ if ( start < 0 )
{
- /* When y-scrolling only, we want to remove the width of the scroll bar so the table
- * + scroll bar will fit into the area avaialble.
- */
- var iOrigWidth = $(n).width();
- n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
+ start = 0;
}
- else if ( oSettings.oScroll.sX !== "" )
+ }
+ else if ( action == "next" )
+ {
+ if ( start + len < records )
{
- /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
- n.style.width = _fnStringToCss( $(n).outerWidth() );
+ start += len;
}
}
+ else if ( action == "last" )
+ {
+ start = Math.floor( (records-1) / len) * len;
+ }
+ else
+ {
+ _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
+ }
+
+ var changed = settings._iDisplayStart !== start;
+ settings._iDisplayStart = start;
+
+ if ( changed ) {
+ _fnCallbackFire( settings, null, 'page', [settings] );
+
+ if ( redraw ) {
+ _fnDraw( settings );
+ }
+ }
+
+ return changed;
+ }
+
+
+
+ /**
+ * Generate the node required for the processing node
+ * @param {object} settings dataTables settings object
+ * @returns {node} Processing element
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlProcessing ( settings )
+ {
+ return $('<div/>', {
+ 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
+ 'class': settings.oClasses.sProcessing
+ } )
+ .html( settings.oLanguage.sProcessing )
+ .insertBefore( settings.nTable )[0];
+ }
+
+
+ /**
+ * Display or hide the processing indicator
+ * @param {object} settings dataTables settings object
+ * @param {bool} show Show the processing indicator (true) or not (false)
+ * @memberof DataTable#oApi
+ */
+ function _fnProcessingDisplay ( settings, show )
+ {
+ if ( settings.oFeatures.bProcessing ) {
+ $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
+ }
+
+ _fnCallbackFire( settings, null, 'processing', [settings, show] );
+ }
+
+ /**
+ * Add any control elements for the table - specifically scrolling
+ * @param {object} settings dataTables settings object
+ * @returns {node} Node to add to the DOM
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlTable ( settings )
+ {
+ var table = $(settings.nTable);
+
+ // Add the ARIA grid role to the table
+ table.attr( 'role', 'grid' );
+
+ // Scrolling from here on in
+ var scroll = settings.oScroll;
+
+ if ( scroll.sX === '' && scroll.sY === '' ) {
+ return settings.nTable;
+ }
+
+ var scrollX = scroll.sX;
+ var scrollY = scroll.sY;
+ var classes = settings.oClasses;
+ var caption = table.children('caption');
+ var captionSide = caption.length ? caption[0]._captionSide : null;
+ var headerClone = $( table[0].cloneNode(false) );
+ var footerClone = $( table[0].cloneNode(false) );
+ var footer = table.children('tfoot');
+ var _div = '<div/>';
+ var size = function ( s ) {
+ return !s ? null : _fnStringToCss( s );
+ };
+
+ if ( ! footer.length ) {
+ footer = null;
+ }
+
+ /*
+ * The HTML structure that we want to generate in this function is:
+ * div - scroller
+ * div - scroll head
+ * div - scroll head inner
+ * table - scroll head table
+ * thead - thead
+ * div - scroll body
+ * table - table (master table)
+ * thead - thead clone for sizing
+ * tbody - tbody
+ * div - scroll foot
+ * div - scroll foot inner
+ * table - scroll foot table
+ * tfoot - tfoot
+ */
+ var scroller = $( _div, { 'class': classes.sScrollWrapper } )
+ .append(
+ $(_div, { 'class': classes.sScrollHead } )
+ .css( {
+ overflow: 'hidden',
+ position: 'relative',
+ border: 0,
+ width: scrollX ? size(scrollX) : '100%'
+ } )
+ .append(
+ $(_div, { 'class': classes.sScrollHeadInner } )
+ .css( {
+ 'box-sizing': 'content-box',
+ width: scroll.sXInner || '100%'
+ } )
+ .append(
+ headerClone
+ .removeAttr('id')
+ .css( 'margin-left', 0 )
+ .append( captionSide === 'top' ? caption : null )
+ .append(
+ table.children('thead')
+ )
+ )
+ )
+ )
+ .append(
+ $(_div, { 'class': classes.sScrollBody } )
+ .css( {
+ position: 'relative',
+ overflow: 'auto',
+ width: size( scrollX )
+ } )
+ .append( table )
+ );
+
+ if ( footer ) {
+ scroller.append(
+ $(_div, { 'class': classes.sScrollFoot } )
+ .css( {
+ overflow: 'hidden',
+ border: 0,
+ width: scrollX ? size(scrollX) : '100%'
+ } )
+ .append(
+ $(_div, { 'class': classes.sScrollFootInner } )
+ .append(
+ footerClone
+ .removeAttr('id')
+ .css( 'margin-left', 0 )
+ .append( captionSide === 'bottom' ? caption : null )
+ .append(
+ table.children('tfoot')
+ )
+ )
+ )
+ );
+ }
+
+ var children = scroller.children();
+ var scrollHead = children[0];
+ var scrollBody = children[1];
+ var scrollFoot = footer ? children[2] : null;
+
+ // When the body is scrolled, then we also want to scroll the headers
+ if ( scrollX ) {
+ $(scrollBody).on( 'scroll.DT', function (e) {
+ var scrollLeft = this.scrollLeft;
+
+ scrollHead.scrollLeft = scrollLeft;
+
+ if ( footer ) {
+ scrollFoot.scrollLeft = scrollLeft;
+ }
+ } );
+ }
+
+ $(scrollBody).css(
+ scrollY && scroll.bCollapse ? 'max-height' : 'height',
+ scrollY
+ );
+
+ settings.nScrollHead = scrollHead;
+ settings.nScrollBody = scrollBody;
+ settings.nScrollFoot = scrollFoot;
+
+ // On redraw - align columns
+ settings.aoDrawCallback.push( {
+ "fn": _fnScrollDraw,
+ "sName": "scrolling"
+ } );
+
+ return scroller[0];
+ }
+
+
+
+ /**
+ * Update the header, footer and body tables for resizing - i.e. column
+ * alignment.
+ *
+ * Welcome to the most horrible function DataTables. The process that this
+ * function follows is basically:
+ * 1. Re-create the table inside the scrolling div
+ * 2. Take live measurements from the DOM
+ * 3. Apply the measurements to align the columns
+ * 4. Clean up
+ *
+ * @param {object} settings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnScrollDraw ( settings )
+ {
+ // Given that this is such a monster function, a lot of variables are use
+ // to try and keep the minimised size as small as possible
+ var
+ scroll = settings.oScroll,
+ scrollX = scroll.sX,
+ scrollXInner = scroll.sXInner,
+ scrollY = scroll.sY,
+ barWidth = scroll.iBarWidth,
+ divHeader = $(settings.nScrollHead),
+ divHeaderStyle = divHeader[0].style,
+ divHeaderInner = divHeader.children('div'),
+ divHeaderInnerStyle = divHeaderInner[0].style,
+ divHeaderTable = divHeaderInner.children('table'),
+ divBodyEl = settings.nScrollBody,
+ divBody = $(divBodyEl),
+ divBodyStyle = divBodyEl.style,
+ divFooter = $(settings.nScrollFoot),
+ divFooterInner = divFooter.children('div'),
+ divFooterTable = divFooterInner.children('table'),
+ header = $(settings.nTHead),
+ table = $(settings.nTable),
+ tableEl = table[0],
+ tableStyle = tableEl.style,
+ footer = settings.nTFoot ? $(settings.nTFoot) : null,
+ browser = settings.oBrowser,
+ ie67 = browser.bScrollOversize,
+ dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
+ headerTrgEls, footerTrgEls,
+ headerSrcEls, footerSrcEls,
+ headerCopy, footerCopy,
+ headerWidths=[], footerWidths=[],
+ headerContent=[], footerContent=[],
+ idx, correction, sanityWidth,
+ zeroOut = function(nSizer) {
+ var style = nSizer.style;
+ style.paddingTop = "0";
+ style.paddingBottom = "0";
+ style.borderTopWidth = "0";
+ style.borderBottomWidth = "0";
+ style.height = 0;
+ };
+
+ // If the scrollbar visibility has changed from the last draw, we need to
+ // adjust the column sizes as the table width will have changed to account
+ // for the scrollbar
+ var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
-
- /**
- * Get the widest node
- * @param {object} oSettings dataTables settings object
- * @param {int} iCol column of interest
- * @returns {node} widest table node
- * @memberof DataTable#oApi
+ if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
+ settings.scrollBarVis = scrollBarVis;
+ _fnAdjustColumnSizing( settings );
+ return; // adjust column sizing will call this function again
+ }
+ else {
+ settings.scrollBarVis = scrollBarVis;
+ }
+
+ /*
+ * 1. Re-create the table inside the scrolling div
*/
- function _fnGetWidestNode( oSettings, iCol )
+
+ // Remove the old minimised thead and tfoot elements in the inner table
+ table.children('thead, tfoot').remove();
+
+ if ( footer ) {
+ footerCopy = footer.clone().prependTo( table );
+ footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
+ footerSrcEls = footerCopy.find('tr');
+ }
+
+ // Clone the current header and footer elements and then place it into the inner table
+ headerCopy = header.clone().prependTo( table );
+ headerTrgEls = header.find('tr'); // original header is in its own table
+ headerSrcEls = headerCopy.find('tr');
+ headerCopy.find('th, td').removeAttr('tabindex');
+
+
+ /*
+ * 2. Take live measurements from the DOM - do not alter the DOM itself!
+ */
+
+ // Remove old sizing and apply the calculated column widths
+ // Get the unique column headers in the newly created (cloned) header. We want to apply the
+ // calculated sizes to this header
+ if ( ! scrollX )
{
- var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
- if ( iMaxIndex < 0 )
- {
- return null;
+ divBodyStyle.width = '100%';
+ divHeader[0].style.width = '100%';
+ }
+
+ $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
+ idx = _fnVisibleToColumnIndex( settings, i );
+ el.style.width = settings.aoColumns[idx].sWidth;
+ } );
+
+ if ( footer ) {
+ _fnApplyToChildren( function(n) {
+ n.style.width = "";
+ }, footerSrcEls );
+ }
+
+ // Size the table as a whole
+ sanityWidth = table.outerWidth();
+ if ( scrollX === "" ) {
+ // No x scrolling
+ tableStyle.width = "100%";
+
+ // IE7 will make the width of the table when 100% include the scrollbar
+ // - which is shouldn't. When there is a scrollbar we need to take this
+ // into account.
+ if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
+ divBody.css('overflow-y') == "scroll")
+ ) {
+ tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
}
-
- if ( oSettings.aoData[iMaxIndex].nTr === null )
- {
- var n = document.createElement('td');
- n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
- return n;
+
+ // Recalculate the sanity width
+ sanityWidth = table.outerWidth();
+ }
+ else if ( scrollXInner !== "" ) {
+ // legacy x scroll inner has been given - use it
+ tableStyle.width = _fnStringToCss(scrollXInner);
+
+ // Recalculate the sanity width
+ sanityWidth = table.outerWidth();
+ }
+
+ // Hidden header should have zero height, so remove padding and borders. Then
+ // set the width based on the real headers
+
+ // Apply all styles in one pass
+ _fnApplyToChildren( zeroOut, headerSrcEls );
+
+ // Read all widths in next pass
+ _fnApplyToChildren( function(nSizer) {
+ headerContent.push( nSizer.innerHTML );
+ headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
+ }, headerSrcEls );
+
+ // Apply all widths in final pass
+ _fnApplyToChildren( function(nToSize, i) {
+ // Only apply widths to the DataTables detected header cells - this
+ // prevents complex headers from having contradictory sizes applied
+ if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
+ nToSize.style.width = headerWidths[i];
}
- return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
+ }, headerTrgEls );
+
+ $(headerSrcEls).height(0);
+
+ /* Same again with the footer if we have one */
+ if ( footer )
+ {
+ _fnApplyToChildren( zeroOut, footerSrcEls );
+
+ _fnApplyToChildren( function(nSizer) {
+ footerContent.push( nSizer.innerHTML );
+ footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
+ }, footerSrcEls );
+
+ _fnApplyToChildren( function(nToSize, i) {
+ nToSize.style.width = footerWidths[i];
+ }, footerTrgEls );
+
+ $(footerSrcEls).height(0);
}
-
-
- /**
- * Get the maximum strlen for each data column
- * @param {object} oSettings dataTables settings object
- * @param {int} iCol column of interest
- * @returns {string} max string length for each column
- * @memberof DataTable#oApi
+
+
+ /*
+ * 3. Apply the measurements
*/
- function _fnGetMaxLenString( oSettings, iCol )
+
+ // "Hide" the header and footer that we used for the sizing. We need to keep
+ // the content of the cell so that the width applied to the header and body
+ // both match, but we want to hide it completely. We want to also fix their
+ // width to what they currently are
+ _fnApplyToChildren( function(nSizer, i) {
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
+ nSizer.style.width = headerWidths[i];
+ }, headerSrcEls );
+
+ if ( footer )
{
- var iMax = -1;
- var iMaxIndex = -1;
-
- for ( var i=0 ; i<oSettings.aoData.length ; i++ )
- {
- var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
- s = s.replace( /<.*?>/g, "" );
- if ( s.length > iMax )
- {
- iMax = s.length;
- iMaxIndex = i;
- }
+ _fnApplyToChildren( function(nSizer, i) {
+ nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
+ nSizer.style.width = footerWidths[i];
+ }, footerSrcEls );
+ }
+
+ // Sanity check that the table is of a sensible width. If not then we are going to get
+ // misalignment - try to prevent this by not allowing the table to shrink below its min width
+ if ( table.outerWidth() < sanityWidth )
+ {
+ // The min width depends upon if we have a vertical scrollbar visible or not */
+ correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
+ divBody.css('overflow-y') == "scroll")) ?
+ sanityWidth+barWidth :
+ sanityWidth;
+
+ // IE6/7 are a law unto themselves...
+ if ( ie67 && (divBodyEl.scrollHeight >
+ divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
+ ) {
+ tableStyle.width = _fnStringToCss( correction-barWidth );
+ }
+
+ // And give the user a warning that we've stopped the table getting too small
+ if ( scrollX === "" || scrollXInner !== "" ) {
+ _fnLog( settings, 1, 'Possible column misalignment', 6 );
}
-
- return iMaxIndex;
}
-
-
- /**
- * Append a CSS unit (only if required) to a string
- * @param {array} aArray1 first array
- * @param {array} aArray2 second array
- * @returns {int} 0 if match, 1 if length is different, 2 if no match
- * @memberof DataTable#oApi
- */
- function _fnStringToCss( s )
+ else
{
- if ( s === null )
- {
- return "0px";
+ correction = '100%';
+ }
+
+ // Apply to the container elements
+ divBodyStyle.width = _fnStringToCss( correction );
+ divHeaderStyle.width = _fnStringToCss( correction );
+
+ if ( footer ) {
+ settings.nScrollFoot.style.width = _fnStringToCss( correction );
+ }
+
+
+ /*
+ * 4. Clean up
+ */
+ if ( ! scrollY ) {
+ /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
+ * the scrollbar height from the visible display, rather than adding it on. We need to
+ * set the height in order to sort this. Don't want to do it in any other browsers.
+ */
+ if ( ie67 ) {
+ divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
}
-
- if ( typeof s == 'number' )
- {
- if ( s < 0 )
- {
- return "0px";
+ }
+
+ /* Finally set the width's of the header and footer tables */
+ var iOuterWidth = table.outerWidth();
+ divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
+ divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
+
+ // Figure out if there are scrollbar present - if so then we need a the header and footer to
+ // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
+ var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
+ var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
+ divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
+
+ if ( footer ) {
+ divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
+ divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
+ divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
+ }
+
+ // Correct DOM ordering for colgroup - comes before the thead
+ table.children('colgroup').insertBefore( table.children('thead') );
+
+ /* Adjust the position of the header in case we loose the y-scrollbar */
+ divBody.scroll();
+
+ // If sorting or filtering has occurred, jump the scrolling back to the top
+ // only if we aren't holding the position
+ if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
+ divBodyEl.scrollTop = 0;
+ }
+ }
+
+
+
+ /**
+ * Apply a given function to the display child nodes of an element array (typically
+ * TD children of TR rows
+ * @param {function} fn Method to apply to the objects
+ * @param array {nodes} an1 List of elements to look through for display children
+ * @param array {nodes} an2 Another list (identical structure to the first) - optional
+ * @memberof DataTable#oApi
+ */
+ function _fnApplyToChildren( fn, an1, an2 )
+ {
+ var index=0, i=0, iLen=an1.length;
+ var nNode1, nNode2;
+
+ while ( i < iLen ) {
+ nNode1 = an1[i].firstChild;
+ nNode2 = an2 ? an2[i].firstChild : null;
+
+ while ( nNode1 ) {
+ if ( nNode1.nodeType === 1 ) {
+ if ( an2 ) {
+ fn( nNode1, nNode2, index );
+ }
+ else {
+ fn( nNode1, index );
+ }
+
+ index++;
}
- return s+"px";
- }
-
- /* Check if the last character is not 0-9 */
- var c = s.charCodeAt( s.length-1 );
- if (c < 0x30 || c > 0x39)
- {
- return s;
+
+ nNode1 = nNode1.nextSibling;
+ nNode2 = an2 ? nNode2.nextSibling : null;
}
- return s+"px";
+
+ i++;
}
-
-
- /**
- * Get the width of a scroll bar in this browser being used
- * @returns {int} width in pixels
- * @memberof DataTable#oApi
- */
- function _fnScrollBarWidth ()
- {
- var inner = document.createElement('p');
- var style = inner.style;
- style.width = "100%";
- style.height = "200px";
- style.padding = "0px";
-
- var outer = document.createElement('div');
- style = outer.style;
- style.position = "absolute";
- style.top = "0px";
- style.left = "0px";
- style.visibility = "hidden";
- style.width = "200px";
- style.height = "150px";
- style.padding = "0px";
- style.overflow = "hidden";
- outer.appendChild(inner);
-
- document.body.appendChild(outer);
- var w1 = inner.offsetWidth;
- outer.style.overflow = 'scroll';
- var w2 = inner.offsetWidth;
- if ( w1 == w2 )
- {
- w2 = outer.clientWidth;
+ }
+
+
+
+ var __re_html_remove = /<.*?>/g;
+
+
+ /**
+ * Calculate the width of columns for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnCalculateColumnWidths ( oSettings )
+ {
+ var
+ table = oSettings.nTable,
+ columns = oSettings.aoColumns,
+ scroll = oSettings.oScroll,
+ scrollY = scroll.sY,
+ scrollX = scroll.sX,
+ scrollXInner = scroll.sXInner,
+ columnCount = columns.length,
+ visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
+ headerCells = $('th', oSettings.nTHead),
+ tableWidthAttr = table.getAttribute('width'), // from DOM element
+ tableContainer = table.parentNode,
+ userInputs = false,
+ i, column, columnIdx, width, outerWidth,
+ browser = oSettings.oBrowser,
+ ie67 = browser.bScrollOversize;
+
+ var styleWidth = table.style.width;
+ if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
+ tableWidthAttr = styleWidth;
+ }
+
+ /* Convert any user input sizes into pixel sizes */
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ column = columns[ visibleColumns[i] ];
+
+ if ( column.sWidth !== null ) {
+ column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
+
+ userInputs = true;
}
-
- document.body.removeChild(outer);
- return (w1 - w2);
}
-
- /**
- * Change the order of the table
- * @param {object} oSettings dataTables settings object
- * @param {bool} bApplyClasses optional - should we apply classes or not
- * @memberof DataTable#oApi
+
+ /* If the number of columns in the DOM equals the number that we have to
+ * process in DataTables, then we can use the offsets that are created by
+ * the web- browser. No custom sizes can be set in order for this to happen,
+ * nor scrolling used
*/
- function _fnSort ( oSettings, bApplyClasses )
- {
- var
- i, iLen, j, jLen, k, kLen,
- sDataType, nTh,
- aaSort = [],
- aiOrig = [],
- oSort = DataTable.ext.oSort,
- aoData = oSettings.aoData,
- aoColumns = oSettings.aoColumns,
- oAria = oSettings.oLanguage.oAria;
-
- /* No sorting required if server-side or no sorting array */
- if ( !oSettings.oFeatures.bServerSide &&
- (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
- {
- aaSort = ( oSettings.aaSortingFixed !== null ) ?
- oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
- oSettings.aaSorting.slice();
-
- /* If there is a sorting data type, and a function belonging to it, then we need to
- * get the data from the developer's function and apply it for this column
- */
- for ( i=0 ; i<aaSort.length ; i++ )
- {
- var iColumn = aaSort[i][0];
- var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
- sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
- if ( DataTable.ext.afnSortData[sDataType] )
- {
- var aData = DataTable.ext.afnSortData[sDataType].call(
- oSettings.oInstance, oSettings, iColumn, iVisColumn
- );
- if ( aData.length === aoData.length )
- {
- for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
- {
- _fnSetCellData( oSettings, j, iColumn, aData[j] );
- }
- }
- else
- {
- _fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
- }
- }
- }
-
- /* Create a value - key array of the current row positions such that we can use their
- * current position during the sort, if values match, in order to perform stable sorting
- */
- for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
- {
- aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
- }
-
- /* Build an internal data array which is specific to the sort, so we can get and prep
- * the data to be sorted only once, rather than needing to do it every time the sorting
- * function runs. This make the sorting function a very simple comparison
- */
- var iSortLen = aaSort.length;
- var fnSortFormat, aDataSort;
- for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- for ( j=0 ; j<iSortLen ; j++ )
- {
- aDataSort = aoColumns[ aaSort[j][0] ].aDataSort;
-
- for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
- {
- sDataType = aoColumns[ aDataSort[k] ].sType;
- fnSortFormat = oSort[ (sDataType ? sDataType : 'string')+"-pre" ];
-
- aoData[i]._aSortData[ aDataSort[k] ] = fnSortFormat ?
- fnSortFormat( _fnGetCellData( oSettings, i, aDataSort[k], 'sort' ) ) :
- _fnGetCellData( oSettings, i, aDataSort[k], 'sort' );
- }
- }
+ if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
+ columnCount == _fnVisbleColumns( oSettings ) &&
+ columnCount == headerCells.length
+ ) {
+ for ( i=0 ; i<columnCount ; i++ ) {
+ var colIdx = _fnVisibleToColumnIndex( oSettings, i );
+
+ if ( colIdx !== null ) {
+ columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
}
-
- /* Do the sort - here we want multi-column sorting based on a given data source (column)
- * and sorting function (from oSort) in a certain direction. It's reasonably complex to
- * follow on it's own, but this is what we want (example two column sorting):
- * fnLocalSorting = function(a,b){
- * var iTest;
- * iTest = oSort['string-asc']('data11', 'data12');
- * if (iTest !== 0)
- * return iTest;
- * iTest = oSort['numeric-desc']('data21', 'data22');
- * if (iTest !== 0)
- * return iTest;
- * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
- * }
- * Basically we have a test for each sorting column, if the data in that column is equal,
- * test the next column. If all columns match, then we use a numeric sort on the row
- * positions in the original data array to provide a stable sort.
- */
- oSettings.aiDisplayMaster.sort( function ( a, b ) {
- var k, l, lLen, iTest, aDataSort, sDataType;
- for ( k=0 ; k<iSortLen ; k++ )
+ }
+ }
+ else
+ {
+ // Otherwise construct a single row, worst case, table with the widest
+ // node in the data, assign any user defined widths, then insert it into
+ // the DOM and allow the browser to do all the hard work of calculating
+ // table widths
+ var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
+ .css( 'visibility', 'hidden' )
+ .removeAttr( 'id' );
+
+ // Clean up the table body
+ tmpTable.find('tbody tr').remove();
+ var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
+
+ // Clone the table header and footer - we can't use the header / footer
+ // from the cloned table, since if scrolling is active, the table's
+ // real header and footer are contained in different table tags
+ tmpTable.find('thead, tfoot').remove();
+ tmpTable
+ .append( $(oSettings.nTHead).clone() )
+ .append( $(oSettings.nTFoot).clone() );
+
+ // Remove any assigned widths from the footer (from scrolling)
+ tmpTable.find('tfoot th, tfoot td').css('width', '');
+
+ // Apply custom sizing to the cloned header
+ headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
+
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ column = columns[ visibleColumns[i] ];
+
+ headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
+ _fnStringToCss( column.sWidthOrig ) :
+ '';
+
+ // For scrollX we need to force the column width otherwise the
+ // browser will collapse it. If this width is smaller than the
+ // width the column requires, then it will have no effect
+ if ( column.sWidthOrig && scrollX ) {
+ $( headerCells[i] ).append( $('<div/>').css( {
+ width: column.sWidthOrig,
+ margin: 0,
+ padding: 0,
+ border: 0,
+ height: 1
+ } ) );
+ }
+ }
+
+ // Find the widest cell for each column and put it into the table
+ if ( oSettings.aoData.length ) {
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ columnIdx = visibleColumns[i];
+ column = columns[ columnIdx ];
+
+ $( _fnGetWidestNode( oSettings, columnIdx ) )
+ .clone( false )
+ .append( column.sContentPadding )
+ .appendTo( tr );
+ }
+ }
+
+ // Tidy the temporary table - remove name attributes so there aren't
+ // duplicated in the dom (radio elements for example)
+ $('[name]', tmpTable).removeAttr('name');
+
+ // Table has been built, attach to the document so we can work with it.
+ // A holding element is used, positioned at the top of the container
+ // with minimal height, so it has no effect on if the container scrolls
+ // or not. Otherwise it might trigger scrolling when it actually isn't
+ // needed
+ var holder = $('<div/>').css( scrollX || scrollY ?
{
- aDataSort = aoColumns[ aaSort[k][0] ].aDataSort;
-
- for ( l=0, lLen=aDataSort.length ; l<lLen ; l++ )
- {
- sDataType = aoColumns[ aDataSort[l] ].sType;
-
- iTest = oSort[ (sDataType ? sDataType : 'string')+"-"+aaSort[k][1] ](
- aoData[a]._aSortData[ aDataSort[l] ],
- aoData[b]._aSortData[ aDataSort[l] ]
- );
-
- if ( iTest !== 0 )
- {
- return iTest;
- }
- }
- }
-
- return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
- } );
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ height: 1,
+ right: 0,
+ overflow: 'hidden'
+ } :
+ {}
+ )
+ .append( tmpTable )
+ .appendTo( tableContainer );
+
+ // When scrolling (X or Y) we want to set the width of the table as
+ // appropriate. However, when not scrolling leave the table width as it
+ // is. This results in slightly different, but I think correct behaviour
+ if ( scrollX && scrollXInner ) {
+ tmpTable.width( scrollXInner );
+ }
+ else if ( scrollX ) {
+ tmpTable.css( 'width', 'auto' );
+ tmpTable.removeAttr('width');
+
+ // If there is no width attribute or style, then allow the table to
+ // collapse
+ if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
+ tmpTable.width( tableContainer.clientWidth );
+ }
+ }
+ else if ( scrollY ) {
+ tmpTable.width( tableContainer.clientWidth );
+ }
+ else if ( tableWidthAttr ) {
+ tmpTable.width( tableWidthAttr );
+ }
+
+ // Get the width of each column in the constructed table - we need to
+ // know the inner width (so it can be assigned to the other table's
+ // cells) and the outer width so we can calculate the full width of the
+ // table. This is safe since DataTables requires a unique cell for each
+ // column, but if ever a header can span multiple columns, this will
+ // need to be modified.
+ var total = 0;
+ for ( i=0 ; i<visibleColumns.length ; i++ ) {
+ var cell = $(headerCells[i]);
+ var border = cell.outerWidth() - cell.width();
+
+ // Use getBounding... where possible (not IE8-) because it can give
+ // sub-pixel accuracy, which we then want to round up!
+ var bounding = browser.bBounding ?
+ Math.ceil( headerCells[i].getBoundingClientRect().width ) :
+ cell.outerWidth();
+
+ // Total is tracked to remove any sub-pixel errors as the outerWidth
+ // of the table might not equal the total given here (IE!).
+ total += bounding;
+
+ // Width for each column to use
+ columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
+ }
+
+ table.style.width = _fnStringToCss( total );
+
+ // Finished with the table - ditch it
+ holder.remove();
+ }
+
+ // If there is a width attr, we want to attach an event listener which
+ // allows the table sizing to automatically adjust when the window is
+ // resized. Use the width attr rather than CSS, since we can't know if the
+ // CSS is a relative value or absolute - DOM read is always px.
+ if ( tableWidthAttr ) {
+ table.style.width = _fnStringToCss( tableWidthAttr );
+ }
+
+ if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
+ var bindResize = function () {
+ $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
+ _fnAdjustColumnSizing( oSettings );
+ } ) );
+ };
+
+ // IE6/7 will crash if we bind a resize event handler on page load.
+ // To be removed in 1.11 which drops IE6/7 support
+ if ( ie67 ) {
+ setTimeout( bindResize, 1000 );
}
-
- /* Alter the sorting classes to take account of the changes */
- if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
- {
- _fnSortingClasses( oSettings );
+ else {
+ bindResize();
}
-
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- var sTitle = aoColumns[i].sTitle.replace( /<.*?>/g, "" );
- nTh = aoColumns[i].nTh;
- nTh.removeAttribute('aria-sort');
- nTh.removeAttribute('aria-label');
-
- /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
- if ( aoColumns[i].bSortable )
- {
- if ( aaSort.length > 0 && aaSort[0][0] == i )
- {
- nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" );
-
- var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ?
- aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0];
- nTh.setAttribute('aria-label', sTitle+
- (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
- }
- else
- {
- nTh.setAttribute('aria-label', sTitle+
- (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
- }
- }
- else
- {
- nTh.setAttribute('aria-label', sTitle);
- }
+
+ oSettings._reszEvt = true;
+ }
+ }
+
+
+ /**
+ * Throttle the calls to a function. Arguments and context are maintained for
+ * the throttled function
+ * @param {function} fn Function to be called
+ * @param {int} [freq=200] call frequency in mS
+ * @returns {function} wrapped function
+ * @memberof DataTable#oApi
+ */
+ function _fnThrottle( fn, freq ) {
+ var
+ frequency = freq !== undefined ? freq : 200,
+ last,
+ timer;
+
+ return function () {
+ var
+ that = this,
+ now = +new Date(),
+ args = arguments;
+
+ if ( last && now < last + frequency ) {
+ clearTimeout( timer );
+
+ timer = setTimeout( function () {
+ last = undefined;
+ fn.apply( that, args );
+ }, frequency );
}
-
- /* Tell the draw function that we have sorted the data */
- oSettings.bSorted = true;
- $(oSettings.oInstance).trigger('sort', oSettings);
-
- /* Copy the master data into the draw array and re-draw */
- if ( oSettings.oFeatures.bFilter )
- {
- /* _fnFilter() will redraw the table for us */
- _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+ else {
+ last = now;
+ fn.apply( that, args );
}
- else
- {
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
- oSettings._iDisplayStart = 0; /* reset display back to page 0 */
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
+ };
+ }
+
+
+ /**
+ * Convert a CSS unit width to pixels (e.g. 2em)
+ * @param {string} width width to be converted
+ * @param {node} parent parent to get the with for (required for relative widths) - optional
+ * @returns {int} width in pixels
+ * @memberof DataTable#oApi
+ */
+ function _fnConvertToWidth ( width, parent )
+ {
+ if ( ! width ) {
+ return 0;
+ }
+
+ var n = $('<div/>')
+ .css( 'width', _fnStringToCss( width ) )
+ .appendTo( parent || document.body );
+
+ var val = n[0].offsetWidth;
+ n.remove();
+
+ return val;
+ }
+
+
+ /**
+ * Get the widest node
+ * @param {object} settings dataTables settings object
+ * @param {int} colIdx column of interest
+ * @returns {node} widest table node
+ * @memberof DataTable#oApi
+ */
+ function _fnGetWidestNode( settings, colIdx )
+ {
+ var idx = _fnGetMaxLenString( settings, colIdx );
+ if ( idx < 0 ) {
+ return null;
+ }
+
+ var data = settings.aoData[ idx ];
+ return ! data.nTr ? // Might not have been created when deferred rendering
+ $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
+ data.anCells[ colIdx ];
+ }
+
+
+ /**
+ * Get the maximum strlen for each data column
+ * @param {object} settings dataTables settings object
+ * @param {int} colIdx column of interest
+ * @returns {string} max string length for each column
+ * @memberof DataTable#oApi
+ */
+ function _fnGetMaxLenString( settings, colIdx )
+ {
+ var s, max=-1, maxIdx = -1;
+
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
+ s = s.replace( __re_html_remove, '' );
+ s = s.replace( /&nbsp;/g, ' ' );
+
+ if ( s.length > max ) {
+ max = s.length;
+ maxIdx = i;
}
}
-
-
- /**
- * Attach a sort handler (click) to a node
- * @param {object} oSettings dataTables settings object
- * @param {node} nNode node to attach the handler to
- * @param {int} iDataIndex column sorting index
- * @param {function} [fnCallback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
- {
- _fnBindAction( nNode, {}, function (e) {
- /* If the column is not sortable - don't to anything */
- if ( oSettings.aoColumns[iDataIndex].bSortable === false )
- {
- return;
- }
-
- /*
- * This is a little bit odd I admit... I declare a temporary function inside the scope of
- * _fnBuildHead and the click handler in order that the code presented here can be used
- * twice - once for when bProcessing is enabled, and another time for when it is
- * disabled, as we need to perform slightly different actions.
- * Basically the issue here is that the Javascript engine in modern browsers don't
- * appear to allow the rendering engine to update the display while it is still executing
- * it's thread (well - it does but only after long intervals). This means that the
- * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
- * I force an execution break by using setTimeout - but this breaks the expected
- * thread continuation for the end-developer's point of view (their code would execute
- * too early), so we only do it when we absolutely have to.
- */
- var fnInnerSorting = function () {
- var iColumn, iNextSort;
-
- /* If the shift key is pressed then we are multiple column sorting */
- if ( e.shiftKey )
- {
- /* Are we already doing some kind of sort on this column? */
- var bFound = false;
- for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
- {
- if ( oSettings.aaSorting[i][0] == iDataIndex )
- {
- bFound = true;
- iColumn = oSettings.aaSorting[i][0];
- iNextSort = oSettings.aaSorting[i][2]+1;
-
- if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
- {
- /* Reached the end of the sorting options, remove from multi-col sort */
- oSettings.aaSorting.splice( i, 1 );
- }
- else
- {
- /* Move onto next sorting direction */
- oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
- oSettings.aaSorting[i][2] = iNextSort;
- }
- break;
- }
- }
-
- /* No sort yet - add it in */
- if ( bFound === false )
- {
- oSettings.aaSorting.push( [ iDataIndex,
- oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
- }
- }
- else
- {
- /* If no shift key then single column sort */
- if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
- {
- iColumn = oSettings.aaSorting[0][0];
- iNextSort = oSettings.aaSorting[0][2]+1;
- if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
- {
- iNextSort = 0;
- }
- oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
- oSettings.aaSorting[0][2] = iNextSort;
- }
- else
- {
- oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
- oSettings.aaSorting.push( [ iDataIndex,
- oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
- }
- }
-
- /* Run the sort */
- _fnSort( oSettings );
- }; /* /fnInnerSorting */
-
- if ( !oSettings.oFeatures.bProcessing )
- {
- fnInnerSorting();
- }
- else
- {
- _fnProcessingDisplay( oSettings, true );
- setTimeout( function() {
- fnInnerSorting();
- if ( !oSettings.oFeatures.bServerSide )
- {
- _fnProcessingDisplay( oSettings, false );
- }
- }, 0 );
- }
-
- /* Call the user specified callback function - used for async user interaction */
- if ( typeof fnCallback == 'function' )
- {
- fnCallback( oSettings );
+
+ return maxIdx;
+ }
+
+
+ /**
+ * Append a CSS unit (only if required) to a string
+ * @param {string} value to css-ify
+ * @returns {string} value with css unit
+ * @memberof DataTable#oApi
+ */
+ function _fnStringToCss( s )
+ {
+ if ( s === null ) {
+ return '0px';
+ }
+
+ if ( typeof s == 'number' ) {
+ return s < 0 ?
+ '0px' :
+ s+'px';
+ }
+
+ // Check it has a unit character already
+ return s.match(/\d$/) ?
+ s+'px' :
+ s;
+ }
+
+
+
+ function _fnSortFlatten ( settings )
+ {
+ var
+ i, iLen, k, kLen,
+ aSort = [],
+ aiOrig = [],
+ aoColumns = settings.aoColumns,
+ aDataSort, iCol, sType, srcCol,
+ fixed = settings.aaSortingFixed,
+ fixedObj = $.isPlainObject( fixed ),
+ nestedSort = [],
+ add = function ( a ) {
+ if ( a.length && ! $.isArray( a[0] ) ) {
+ // 1D array
+ nestedSort.push( a );
+ }
+ else {
+ // 2D array
+ $.merge( nestedSort, a );
}
- } );
+ };
+
+ // Build the sort array, with pre-fix and post-fix options if they have been
+ // specified
+ if ( $.isArray( fixed ) ) {
+ add( fixed );
}
-
-
- /**
- * Set the sorting classes on the header, Note: it is safe to call this function
- * when bSort and bSortClasses are false
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSortingClasses( oSettings )
+
+ if ( fixedObj && fixed.pre ) {
+ add( fixed.pre );
+ }
+
+ add( settings.aaSorting );
+
+ if (fixedObj && fixed.post ) {
+ add( fixed.post );
+ }
+
+ for ( i=0 ; i<nestedSort.length ; i++ )
{
- var i, iLen, j, jLen, iFound;
- var aaSort, sClass;
- var iColumns = oSettings.aoColumns.length;
- var oClasses = oSettings.oClasses;
-
- for ( i=0 ; i<iColumns ; i++ )
+ srcCol = nestedSort[i][0];
+ aDataSort = aoColumns[ srcCol ].aDataSort;
+
+ for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
{
- if ( oSettings.aoColumns[i].bSortable )
- {
- $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
- " "+ oSettings.aoColumns[i].sSortingClass );
+ iCol = aDataSort[k];
+ sType = aoColumns[ iCol ].sType || 'string';
+
+ if ( nestedSort[i]._idx === undefined ) {
+ nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
}
+
+ aSort.push( {
+ src: srcCol,
+ col: iCol,
+ dir: nestedSort[i][1],
+ index: nestedSort[i]._idx,
+ type: sType,
+ formatter: DataTable.ext.type.order[ sType+"-pre" ]
+ } );
}
-
- if ( oSettings.aaSortingFixed !== null )
- {
- aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
- }
- else
- {
- aaSort = oSettings.aaSorting.slice();
- }
-
- /* Apply the required classes to the header */
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- if ( oSettings.aoColumns[i].bSortable )
- {
- sClass = oSettings.aoColumns[i].sSortingClass;
- iFound = -1;
- for ( j=0 ; j<aaSort.length ; j++ )
- {
- if ( aaSort[j][0] == i )
- {
- sClass = ( aaSort[j][1] == "asc" ) ?
- oClasses.sSortAsc : oClasses.sSortDesc;
- iFound = j;
- break;
+ }
+
+ return aSort;
+ }
+
+ /**
+ * Change the order of the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ * @todo This really needs split up!
+ */
+ function _fnSort ( oSettings )
+ {
+ var
+ i, ien, iLen, j, jLen, k, kLen,
+ sDataType, nTh,
+ aiOrig = [],
+ oExtSort = DataTable.ext.type.order,
+ aoData = oSettings.aoData,
+ aoColumns = oSettings.aoColumns,
+ aDataSort, data, iCol, sType, oSort,
+ formatters = 0,
+ sortCol,
+ displayMaster = oSettings.aiDisplayMaster,
+ aSort;
+
+ // Resolve any column types that are unknown due to addition or invalidation
+ // @todo Can this be moved into a 'data-ready' handler which is called when
+ // data is going to be used in the table?
+ _fnColumnTypes( oSettings );
+
+ aSort = _fnSortFlatten( oSettings );
+
+ for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
+ sortCol = aSort[i];
+
+ // Track if we can use the fast sort algorithm
+ if ( sortCol.formatter ) {
+ formatters++;
+ }
+
+ // Load the data needed for the sort, for each cell
+ _fnSortData( oSettings, sortCol.col );
+ }
+
+ /* No sorting required if server-side or no sorting array */
+ if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
+ {
+ // Create a value - key array of the current row positions such that we can use their
+ // current position during the sort, if values match, in order to perform stable sorting
+ for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
+ aiOrig[ displayMaster[i] ] = i;
+ }
+
+ /* Do the sort - here we want multi-column sorting based on a given data source (column)
+ * and sorting function (from oSort) in a certain direction. It's reasonably complex to
+ * follow on it's own, but this is what we want (example two column sorting):
+ * fnLocalSorting = function(a,b){
+ * var iTest;
+ * iTest = oSort['string-asc']('data11', 'data12');
+ * if (iTest !== 0)
+ * return iTest;
+ * iTest = oSort['numeric-desc']('data21', 'data22');
+ * if (iTest !== 0)
+ * return iTest;
+ * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
+ * }
+ * Basically we have a test for each sorting column, if the data in that column is equal,
+ * test the next column. If all columns match, then we use a numeric sort on the row
+ * positions in the original data array to provide a stable sort.
+ *
+ * Note - I know it seems excessive to have two sorting methods, but the first is around
+ * 15% faster, so the second is only maintained for backwards compatibility with sorting
+ * methods which do not have a pre-sort formatting function.
+ */
+ if ( formatters === aSort.length ) {
+ // All sort types have formatting functions
+ displayMaster.sort( function ( a, b ) {
+ var
+ x, y, k, test, sort,
+ len=aSort.length,
+ dataA = aoData[a]._aSortData,
+ dataB = aoData[b]._aSortData;
+
+ for ( k=0 ; k<len ; k++ ) {
+ sort = aSort[k];
+
+ x = dataA[ sort.col ];
+ y = dataB[ sort.col ];
+
+ test = x<y ? -1 : x>y ? 1 : 0;
+ if ( test !== 0 ) {
+ return sort.dir === 'asc' ? test : -test;
}
}
- $(oSettings.aoColumns[i].nTh).addClass( sClass );
-
- if ( oSettings.bJUI )
- {
- /* jQuery UI uses extra markup */
- var jqSpan = $("span."+oClasses.sSortIcon, oSettings.aoColumns[i].nTh);
- jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+
- oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
-
- var sSpanClass;
- if ( iFound == -1 )
- {
- sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
- }
- else if ( aaSort[iFound][1] == "asc" )
- {
- sSpanClass = oClasses.sSortJUIAsc;
- }
- else
- {
- sSpanClass = oClasses.sSortJUIDesc;
+
+ x = aiOrig[a];
+ y = aiOrig[b];
+ return x<y ? -1 : x>y ? 1 : 0;
+ } );
+ }
+ else {
+ // Depreciated - remove in 1.11 (providing a plug-in option)
+ // Not all sort types have formatting methods, so we have to call their sorting
+ // methods.
+ displayMaster.sort( function ( a, b ) {
+ var
+ x, y, k, l, test, sort, fn,
+ len=aSort.length,
+ dataA = aoData[a]._aSortData,
+ dataB = aoData[b]._aSortData;
+
+ for ( k=0 ; k<len ; k++ ) {
+ sort = aSort[k];
+
+ x = dataA[ sort.col ];
+ y = dataB[ sort.col ];
+
+ fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
+ test = fn( x, y );
+ if ( test !== 0 ) {
+ return test;
}
-
- jqSpan.addClass( sSpanClass );
}
- }
- else
- {
- /* No sorting on this column, so add the base class. This will have been assigned by
- * _fnAddColumn
- */
- $(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
- }
+
+ x = aiOrig[a];
+ y = aiOrig[b];
+ return x<y ? -1 : x>y ? 1 : 0;
+ } );
}
-
- /*
- * Apply the required classes to the table body
- * Note that this is given as a feature switch since it can significantly slow down a sort
- * on large data sets (adding and removing of classes is always slow at the best of times..)
- * Further to this, note that this code is admittedly fairly ugly. It could be made a lot
- * simpler using jQuery selectors and add/removeClass, but that is significantly slower
- * (on the order of 5 times slower) - hence the direct DOM manipulation here.
- * Note that for deferred drawing we do use jQuery - the reason being that taking the first
- * row found to see if the whole column needs processed can miss classes since the first
- * column might be new.
- */
- sClass = oClasses.sSortColumn;
-
- if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
- {
- var nTds = _fnGetTdNodes( oSettings );
-
- /* Determine what the sorting class for each column should be */
- var iClass, iTargetCol;
- var asClasses = [];
- for (i = 0; i < iColumns; i++)
- {
- asClasses.push("");
+ }
+
+ /* Tell the draw function that we have sorted the data */
+ oSettings.bSorted = true;
+ }
+
+
+ function _fnSortAria ( settings )
+ {
+ var label;
+ var nextSort;
+ var columns = settings.aoColumns;
+ var aSort = _fnSortFlatten( settings );
+ var oAria = settings.oLanguage.oAria;
+
+ // ARIA attributes - need to loop all columns, to update all (removing old
+ // attributes as needed)
+ for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
+ {
+ var col = columns[i];
+ var asSorting = col.asSorting;
+ var sTitle = col.sTitle.replace( /<.*?>/g, "" );
+ var th = col.nTh;
+
+ // IE7 is throwing an error when setting these properties with jQuery's
+ // attr() and removeAttr() methods...
+ th.removeAttribute('aria-sort');
+
+ /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
+ if ( col.bSortable ) {
+ if ( aSort.length > 0 && aSort[0].col == i ) {
+ th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
+ nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
+ }
+ else {
+ nextSort = asSorting[0];
+ }
+
+ label = sTitle + ( nextSort === "asc" ?
+ oAria.sSortAscending :
+ oAria.sSortDescending
+ );
+ }
+ else {
+ label = sTitle;
+ }
+
+ th.setAttribute('aria-label', label);
+ }
+ }
+
+
+ /**
+ * Function to run on user sort request
+ * @param {object} settings dataTables settings object
+ * @param {node} attachTo node to attach the handler to
+ * @param {int} colIdx column sorting index
+ * @param {boolean} [append=false] Append the requested sort to the existing
+ * sort if true (i.e. multi-column sort)
+ * @param {function} [callback] callback function
+ * @memberof DataTable#oApi
+ */
+ function _fnSortListener ( settings, colIdx, append, callback )
+ {
+ var col = settings.aoColumns[ colIdx ];
+ var sorting = settings.aaSorting;
+ var asSorting = col.asSorting;
+ var nextSortIdx;
+ var next = function ( a, overflow ) {
+ var idx = a._idx;
+ if ( idx === undefined ) {
+ idx = $.inArray( a[1], asSorting );
+ }
+
+ return idx+1 < asSorting.length ?
+ idx+1 :
+ overflow ?
+ null :
+ 0;
+ };
+
+ // Convert to 2D array if needed
+ if ( typeof sorting[0] === 'number' ) {
+ sorting = settings.aaSorting = [ sorting ];
+ }
+
+ // If appending the sort then we are multi-column sorting
+ if ( append && settings.oFeatures.bSortMulti ) {
+ // Are we already doing some kind of sort on this column?
+ var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
+
+ if ( sortIdx !== -1 ) {
+ // Yes, modify the sort
+ nextSortIdx = next( sorting[sortIdx], true );
+
+ if ( nextSortIdx === null && sorting.length === 1 ) {
+ nextSortIdx = 0; // can't remove sorting completely
}
- for (i = 0, iClass = 1; i < aaSort.length; i++)
- {
- iTargetCol = parseInt( aaSort[i][0], 10 );
- asClasses[iTargetCol] = sClass + iClass;
-
- if ( iClass < 3 )
- {
- iClass++;
- }
+
+ if ( nextSortIdx === null ) {
+ sorting.splice( sortIdx, 1 );
}
-
- /* Make changes to the classes for each cell as needed */
- var reClass = new RegExp(sClass + "[123]");
- var sTmpClass, sCurrentClass, sNewClass;
- for ( i=0, iLen=nTds.length; i<iLen; i++ )
- {
- /* Determine which column we're looking at */
- iTargetCol = i % iColumns;
-
- /* What is the full list of classes now */
- sCurrentClass = nTds[i].className;
- /* What sorting class should be applied? */
- sNewClass = asClasses[iTargetCol];
- /* What would the new full list be if we did a replacement? */
- sTmpClass = sCurrentClass.replace(reClass, sNewClass);
-
- if ( sTmpClass != sCurrentClass )
- {
- /* We changed something */
- nTds[i].className = $.trim( sTmpClass );
- }
- else if ( sNewClass.length > 0 && sCurrentClass.indexOf(sNewClass) == -1 )
- {
- /* We need to add a class */
- nTds[i].className = sCurrentClass + " " + sNewClass;
- }
+ else {
+ sorting[sortIdx][1] = asSorting[ nextSortIdx ];
+ sorting[sortIdx]._idx = nextSortIdx;
}
}
- }
-
-
-
- /**
- * Save the state of a table in a cookie such that the page can be reloaded
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSaveState ( oSettings )
- {
- if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying )
- {
- return;
- }
-
- /* Store the interesting variables */
- var i, iLen, bInfinite=oSettings.oScroll.bInfinite;
- var oState = {
- "iCreate": new Date().getTime(),
- "iStart": (bInfinite ? 0 : oSettings._iDisplayStart),
- "iEnd": (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd),
- "iLength": oSettings._iDisplayLength,
- "aaSorting": $.extend( true, [], oSettings.aaSorting ),
- "oSearch": $.extend( true, {}, oSettings.oPreviousSearch ),
- "aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ),
- "abVisCols": []
- };
-
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- oState.abVisCols.push( oSettings.aoColumns[i].bVisible );
+ else {
+ // No sort on this column yet
+ sorting.push( [ colIdx, asSorting[0], 0 ] );
+ sorting[sorting.length-1]._idx = 0;
}
-
- _fnCallbackFire( oSettings, "aoStateSaveParams", 'stateSaveParams', [oSettings, oState] );
-
- oSettings.fnStateSave.call( oSettings.oInstance, oSettings, oState );
}
-
-
- /**
- * Attempt to load a saved table state from a cookie
- * @param {object} oSettings dataTables settings object
- * @param {object} oInit DataTables init object so we can override settings
- * @memberof DataTable#oApi
- */
- function _fnLoadState ( oSettings, oInit )
- {
- if ( !oSettings.oFeatures.bStateSave )
- {
+ else if ( sorting.length && sorting[0][0] == colIdx ) {
+ // Single column - already sorting on this column, modify the sort
+ nextSortIdx = next( sorting[0] );
+
+ sorting.length = 1;
+ sorting[0][1] = asSorting[ nextSortIdx ];
+ sorting[0]._idx = nextSortIdx;
+ }
+ else {
+ // Single column - sort only on this column
+ sorting.length = 0;
+ sorting.push( [ colIdx, asSorting[0] ] );
+ sorting[0]._idx = 0;
+ }
+
+ // Run the sort by calling a full redraw
+ _fnReDraw( settings );
+
+ // callback used for async user interaction
+ if ( typeof callback == 'function' ) {
+ callback( settings );
+ }
+ }
+
+
+ /**
+ * Attach a sort handler (click) to a node
+ * @param {object} settings dataTables settings object
+ * @param {node} attachTo node to attach the handler to
+ * @param {int} colIdx column sorting index
+ * @param {function} [callback] callback function
+ * @memberof DataTable#oApi
+ */
+ function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
+ {
+ var col = settings.aoColumns[ colIdx ];
+
+ _fnBindAction( attachTo, {}, function (e) {
+ /* If the column is not sortable - don't to anything */
+ if ( col.bSortable === false ) {
return;
}
-
- var oData = oSettings.fnStateLoad.call( oSettings.oInstance, oSettings );
- if ( !oData )
- {
- return;
+
+ // If processing is enabled use a timeout to allow the processing
+ // display to be shown - otherwise to it synchronously
+ if ( settings.oFeatures.bProcessing ) {
+ _fnProcessingDisplay( settings, true );
+
+ setTimeout( function() {
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
+
+ // In server-side processing, the draw callback will remove the
+ // processing display
+ if ( _fnDataSource( settings ) !== 'ssp' ) {
+ _fnProcessingDisplay( settings, false );
+ }
+ }, 0 );
}
-
- /* Allow custom and plug-in manipulation functions to alter the saved data set and
- * cancelling of loading by returning false
- */
- var abStateLoad = _fnCallbackFire( oSettings, 'aoStateLoadParams', 'stateLoadParams', [oSettings, oData] );
- if ( $.inArray( false, abStateLoad ) !== -1 )
- {
- return;
+ else {
+ _fnSortListener( settings, colIdx, e.shiftKey, callback );
}
-
- /* Store the saved state so it might be accessed at any time */
- oSettings.oLoadedState = $.extend( true, {}, oData );
-
- /* Restore key features */
- oSettings._iDisplayStart = oData.iStart;
- oSettings.iInitDisplayStart = oData.iStart;
- oSettings._iDisplayEnd = oData.iEnd;
- oSettings._iDisplayLength = oData.iLength;
- oSettings.aaSorting = oData.aaSorting.slice();
- oSettings.saved_aaSorting = oData.aaSorting.slice();
-
- /* Search filtering */
- $.extend( oSettings.oPreviousSearch, oData.oSearch );
- $.extend( true, oSettings.aoPreSearchCols, oData.aoSearchCols );
-
- /* Column visibility state
- * Pass back visibility settings to the init handler, but to do not here override
- * the init object that the user might have passed in
- */
- oInit.saved_aoColumns = [];
- for ( var i=0 ; i<oData.abVisCols.length ; i++ )
- {
- oInit.saved_aoColumns[i] = {};
- oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
+ } );
+ }
+
+
+ /**
+ * Set the sorting classes on table's body, Note: it is safe to call this function
+ * when bSort and bSortClasses are false
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnSortingClasses( settings )
+ {
+ var oldSort = settings.aLastSort;
+ var sortClass = settings.oClasses.sSortColumn;
+ var sort = _fnSortFlatten( settings );
+ var features = settings.oFeatures;
+ var i, ien, colIdx;
+
+ if ( features.bSort && features.bSortClasses ) {
+ // Remove old sorting classes
+ for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
+ colIdx = oldSort[i].src;
+
+ // Remove column sorting
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
+ .removeClass( sortClass + (i<2 ? i+1 : 3) );
}
-
- _fnCallbackFire( oSettings, 'aoStateLoaded', 'stateLoaded', [oSettings, oData] );
- }
-
-
- /**
- * Create a new cookie with a value to store the state of a table
- * @param {string} sName name of the cookie to create
- * @param {string} sValue the value the cookie should take
- * @param {int} iSecs duration of the cookie
- * @param {string} sBaseName sName is made up of the base + file name - this is the base
- * @param {function} fnCallback User definable function to modify the cookie
- * @memberof DataTable#oApi
- */
- function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
- {
- var date = new Date();
- date.setTime( date.getTime()+(iSecs*1000) );
-
- /*
- * Shocking but true - it would appear IE has major issues with having the path not having
- * a trailing slash on it. We need the cookie to be available based on the path, so we
- * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
- * patch to use at least some of the path
- */
- var aParts = window.location.pathname.split('/');
- var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
- var sFullCookie, oData;
-
- if ( fnCallback !== null )
- {
- oData = (typeof $.parseJSON === 'function') ?
- $.parseJSON( sValue ) : eval( '('+sValue+')' );
- sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
- aParts.join('/')+"/" );
+
+ // Add new column sorting
+ for ( i=0, ien=sort.length ; i<ien ; i++ ) {
+ colIdx = sort[i].src;
+
+ $( _pluck( settings.aoData, 'anCells', colIdx ) )
+ .addClass( sortClass + (i<2 ? i+1 : 3) );
}
- else
- {
- sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
- "; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
+ }
+
+ settings.aLastSort = sort;
+ }
+
+
+ // Get the data to sort a column, be it from cache, fresh (populating the
+ // cache), or from a sort formatter
+ function _fnSortData( settings, idx )
+ {
+ // Custom sorting function - provided by the sort data type
+ var column = settings.aoColumns[ idx ];
+ var customSort = DataTable.ext.order[ column.sSortDataType ];
+ var customData;
+
+ if ( customSort ) {
+ customData = customSort.call( settings.oInstance, settings, idx,
+ _fnColumnIndexToVisible( settings, idx )
+ );
+ }
+
+ // Use / populate cache
+ var row, cellData;
+ var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
+
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ row = settings.aoData[i];
+
+ if ( ! row._aSortData ) {
+ row._aSortData = [];
}
-
- /* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
- * belonging to DataTables.
- */
- var
- aCookies =document.cookie.split(';'),
- iNewCookieLen = sFullCookie.split(';')[0].length,
- aOldCookies = [];
-
- if ( iNewCookieLen+document.cookie.length+10 > 4096 ) /* Magic 10 for padding */
- {
- for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
- {
- if ( aCookies[i].indexOf( sBaseName ) != -1 )
- {
- /* It's a DataTables cookie, so eval it and check the time stamp */
- var aSplitCookie = aCookies[i].split('=');
- try {
- oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' );
-
- if ( oData && oData.iCreate )
- {
- aOldCookies.push( {
- "name": aSplitCookie[0],
- "time": oData.iCreate
- } );
- }
- }
- catch( e ) {}
- }
- }
-
- // Make sure we delete the oldest ones first
- aOldCookies.sort( function (a, b) {
- return b.time - a.time;
- } );
-
- // Eliminate as many old DataTables cookies as we need to
- while ( iNewCookieLen + document.cookie.length + 10 > 4096 ) {
- if ( aOldCookies.length === 0 ) {
- // Deleted all DT cookies and still not enough space. Can't state save
- return;
- }
-
- var old = aOldCookies.pop();
- document.cookie = old.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
- aParts.join('/') + "/";
- }
+
+ if ( ! row._aSortData[idx] || customSort ) {
+ cellData = customSort ?
+ customData[i] : // If there was a custom sort function, use data from there
+ _fnGetCellData( settings, i, idx, 'sort' );
+
+ row._aSortData[ idx ] = formatter ?
+ formatter( cellData ) :
+ cellData;
}
-
- document.cookie = sFullCookie;
}
-
-
- /**
- * Read an old cookie to get a cookie with an old table state
- * @param {string} sName name of the cookie to read
- * @returns {string} contents of the cookie - or null if no cookie with that name found
- * @memberof DataTable#oApi
- */
- function _fnReadCookie ( sName )
+ }
+
+
+
+ /**
+ * Save the state of a table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnSaveState ( settings )
+ {
+ if ( !settings.oFeatures.bStateSave || settings.bDestroying )
{
- var
- aParts = window.location.pathname.split('/'),
- sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
- sCookieContents = document.cookie.split(';');
-
- for( var i=0 ; i<sCookieContents.length ; i++ )
- {
- var c = sCookieContents[i];
-
- while (c.charAt(0)==' ')
- {
- c = c.substring(1,c.length);
- }
-
- if (c.indexOf(sNameEQ) === 0)
- {
- return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
- }
- }
- return null;
+ return;
}
-
-
- /**
- * Return the settings object for a particular table
- * @param {node} nTable table we are using as a dataTable
- * @returns {object} Settings object - or null if not found
- * @memberof DataTable#oApi
- */
- function _fnSettingsFromNode ( nTable )
- {
- for ( var i=0 ; i<DataTable.settings.length ; i++ )
- {
- if ( DataTable.settings[i].nTable === nTable )
- {
- return DataTable.settings[i];
- }
- }
-
- return null;
+
+ /* Store the interesting variables */
+ var state = {
+ time: +new Date(),
+ start: settings._iDisplayStart,
+ length: settings._iDisplayLength,
+ order: $.extend( true, [], settings.aaSorting ),
+ search: _fnSearchToCamel( settings.oPreviousSearch ),
+ columns: $.map( settings.aoColumns, function ( col, i ) {
+ return {
+ visible: col.bVisible,
+ search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
+ };
+ } )
+ };
+
+ _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
+
+ settings.oSavedState = state;
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
+ }
+
+
+ /**
+ * Attempt to load a saved table state
+ * @param {object} oSettings dataTables settings object
+ * @param {object} oInit DataTables init object so we can override settings
+ * @memberof DataTable#oApi
+ */
+ function _fnLoadState ( settings, oInit )
+ {
+ var i, ien;
+ var columns = settings.aoColumns;
+
+ if ( ! settings.oFeatures.bStateSave ) {
+ return;
}
-
-
- /**
- * Return an array with the TR nodes for the table
- * @param {object} oSettings dataTables settings object
- * @returns {array} TR array
- * @memberof DataTable#oApi
- */
- function _fnGetTrNodes ( oSettings )
- {
- var aNodes = [];
- var aoData = oSettings.aoData;
- for ( var i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- if ( aoData[i].nTr !== null )
- {
- aNodes.push( aoData[i].nTr );
- }
- }
- return aNodes;
+
+ var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
+ if ( ! state || ! state.time ) {
+ return;
}
-
-
- /**
- * Return an flat array with all TD nodes for the table, or row
- * @param {object} oSettings dataTables settings object
- * @param {int} [iIndividualRow] aoData index to get the nodes for - optional
- * if not given then the return array will contain all nodes for the table
- * @returns {array} TD array
- * @memberof DataTable#oApi
+
+ /* Allow custom and plug-in manipulation functions to alter the saved data set and
+ * cancelling of loading by returning false
*/
- function _fnGetTdNodes ( oSettings, iIndividualRow )
- {
- var anReturn = [];
- var iCorrector;
- var anTds, nTd;
- var iRow, iRows=oSettings.aoData.length,
- iColumn, iColumns, oData, sNodeName, iStart=0, iEnd=iRows;
-
- /* Allow the collection to be limited to just one row */
- if ( iIndividualRow !== undefined )
- {
- iStart = iIndividualRow;
- iEnd = iIndividualRow+1;
+ var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
+ if ( $.inArray( false, abStateLoad ) !== -1 ) {
+ return;
+ }
+
+ /* Reject old data */
+ var duration = settings.iStateDuration;
+ if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
+ return;
+ }
+
+ // Number of columns have changed - all bets are off, no restore of settings
+ if ( columns.length !== state.columns.length ) {
+ return;
+ }
+
+ // Store the saved state so it might be accessed at any time
+ settings.oLoadedState = $.extend( true, {}, state );
+
+ // Restore key features - todo - for 1.11 this needs to be done by
+ // subscribed events
+ if ( state.start !== undefined ) {
+ settings._iDisplayStart = state.start;
+ settings.iInitDisplayStart = state.start;
+ }
+ if ( state.length !== undefined ) {
+ settings._iDisplayLength = state.length;
+ }
+
+ // Order
+ if ( state.order !== undefined ) {
+ settings.aaSorting = [];
+ $.each( state.order, function ( i, col ) {
+ settings.aaSorting.push( col[0] >= columns.length ?
+ [ 0, col[1] ] :
+ col
+ );
+ } );
+ }
+
+ // Search
+ if ( state.search !== undefined ) {
+ $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
+ }
+
+ // Columns
+ for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
+ var col = state.columns[i];
+
+ // Visibility
+ if ( col.visible !== undefined ) {
+ columns[i].bVisible = col.visible;
}
-
- for ( iRow=iStart ; iRow<iEnd ; iRow++ )
- {
- oData = oSettings.aoData[iRow];
- if ( oData.nTr !== null )
- {
- /* get the TD child nodes - taking into account text etc nodes */
- anTds = [];
- nTd = oData.nTr.firstChild;
- while ( nTd )
- {
- sNodeName = nTd.nodeName.toLowerCase();
- if ( sNodeName == 'td' || sNodeName == 'th' )
- {
- anTds.push( nTd );
- }
- nTd = nTd.nextSibling;
- }
-
- iCorrector = 0;
- for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
- {
- if ( oSettings.aoColumns[iColumn].bVisible )
- {
- anReturn.push( anTds[iColumn-iCorrector] );
- }
- else
- {
- anReturn.push( oData._anHidden[iColumn] );
- iCorrector++;
- }
- }
- }
+
+ // Search
+ if ( col.search !== undefined ) {
+ $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
}
-
- return anReturn;
}
-
-
- /**
- * Log an error message
- * @param {object} oSettings dataTables settings object
- * @param {int} iLevel log error messages, or display them to the user
- * @param {string} sMesg error message
- * @memberof DataTable#oApi
- */
- function _fnLog( oSettings, iLevel, sMesg )
- {
- var sAlert = (oSettings===null) ?
- "DataTables warning: "+sMesg :
- "DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg;
-
- if ( iLevel === 0 )
- {
- if ( DataTable.ext.sErrMode == 'alert' )
- {
- alert( sAlert );
- }
- else
- {
- throw new Error(sAlert);
- }
- return;
+
+ _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
+ }
+
+
+ /**
+ * Return the settings object for a particular table
+ * @param {node} table table we are using as a dataTable
+ * @returns {object} Settings object - or null if not found
+ * @memberof DataTable#oApi
+ */
+ function _fnSettingsFromNode ( table )
+ {
+ var settings = DataTable.settings;
+ var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
+
+ return idx !== -1 ?
+ settings[ idx ] :
+ null;
+ }
+
+
+ /**
+ * Log an error message
+ * @param {object} settings dataTables settings object
+ * @param {int} level log error messages, or display them to the user
+ * @param {string} msg error message
+ * @param {int} tn Technical note id to get more information about the error.
+ * @memberof DataTable#oApi
+ */
+ function _fnLog( settings, level, msg, tn )
+ {
+ msg = 'DataTables warning: '+
+ (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
+
+ if ( tn ) {
+ msg += '. For more information about this error, please see '+
+ 'http://datatables.net/tn/'+tn;
+ }
+
+ if ( ! level ) {
+ // Backwards compatibility pre 1.10
+ var ext = DataTable.ext;
+ var type = ext.sErrMode || ext.errMode;
+
+ if ( settings ) {
+ _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
}
- else if ( window.console && console.log )
- {
- console.log( sAlert );
+
+ if ( type == 'alert' ) {
+ alert( msg );
}
- }
-
-
- /**
- * See if a property is defined on one object, if so assign it to the other object
- * @param {object} oRet target object
- * @param {object} oSrc source object
- * @param {string} sName property
- * @param {string} [sMappedName] name to map too - optional, sName used if not given
- * @memberof DataTable#oApi
- */
- function _fnMap( oRet, oSrc, sName, sMappedName )
- {
- if ( sMappedName === undefined )
- {
- sMappedName = sName;
+ else if ( type == 'throw' ) {
+ throw new Error(msg);
}
- if ( oSrc[sName] !== undefined )
- {
- oRet[sMappedName] = oSrc[sName];
+ else if ( typeof type == 'function' ) {
+ type( settings, tn, msg );
}
}
-
-
- /**
- * Extend objects - very similar to jQuery.extend, but deep copy objects, and shallow
- * copy arrays. The reason we need to do this, is that we don't want to deep copy array
- * init values (such as aaSorting) since the dev wouldn't be able to override them, but
- * we do want to deep copy arrays.
- * @param {object} oOut Object to extend
- * @param {object} oExtender Object from which the properties will be applied to oOut
- * @returns {object} oOut Reference, just for convenience - oOut === the return.
- * @memberof DataTable#oApi
- * @todo This doesn't take account of arrays inside the deep copied objects.
- */
- function _fnExtend( oOut, oExtender )
- {
- var val;
-
- for ( var prop in oExtender )
- {
- if ( oExtender.hasOwnProperty(prop) )
- {
- val = oExtender[prop];
-
- if ( typeof oInit[prop] === 'object' && val !== null && $.isArray(val) === false )
- {
- $.extend( true, oOut[prop], val );
- }
- else
- {
- oOut[prop] = val;
+ else if ( window.console && console.log ) {
+ console.log( msg );
+ }
+ }
+
+
+ /**
+ * See if a property is defined on one object, if so assign it to the other object
+ * @param {object} ret target object
+ * @param {object} src source object
+ * @param {string} name property
+ * @param {string} [mappedName] name to map too - optional, name used if not given
+ * @memberof DataTable#oApi
+ */
+ function _fnMap( ret, src, name, mappedName )
+ {
+ if ( $.isArray( name ) ) {
+ $.each( name, function (i, val) {
+ if ( $.isArray( val ) ) {
+ _fnMap( ret, src, val[0], val[1] );
+ }
+ else {
+ _fnMap( ret, src, val );
+ }
+ } );
+
+ return;
+ }
+
+ if ( mappedName === undefined ) {
+ mappedName = name;
+ }
+
+ if ( src[name] !== undefined ) {
+ ret[mappedName] = src[name];
+ }
+ }
+
+
+ /**
+ * Extend objects - very similar to jQuery.extend, but deep copy objects, and
+ * shallow copy arrays. The reason we need to do this, is that we don't want to
+ * deep copy array init values (such as aaSorting) since the dev wouldn't be
+ * able to override them, but we do want to deep copy arrays.
+ * @param {object} out Object to extend
+ * @param {object} extender Object from which the properties will be applied to
+ * out
+ * @param {boolean} breakRefs If true, then arrays will be sliced to take an
+ * independent copy with the exception of the `data` or `aaData` parameters
+ * if they are present. This is so you can pass in a collection to
+ * DataTables and have that used as your data source without breaking the
+ * references
+ * @returns {object} out Reference, just for convenience - out === the return.
+ * @memberof DataTable#oApi
+ * @todo This doesn't take account of arrays inside the deep copied objects.
+ */
+ function _fnExtend( out, extender, breakRefs )
+ {
+ var val;
+
+ for ( var prop in extender ) {
+ if ( extender.hasOwnProperty(prop) ) {
+ val = extender[prop];
+
+ if ( $.isPlainObject( val ) ) {
+ if ( ! $.isPlainObject( out[prop] ) ) {
+ out[prop] = {};
}
+ $.extend( true, out[prop], val );
+ }
+ else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
+ out[prop] = val.slice();
+ }
+ else {
+ out[prop] = val;
}
}
-
- return oOut;
}
-
-
- /**
- * Bind an event handers to allow a click or return key to activate the callback.
- * This is good for accessibility since a return on the keyboard will have the
- * same effect as a click, if the element has focus.
- * @param {element} n Element to bind the action to
- * @param {object} oData Data object to pass to the triggered function
- * @param {function} fn Callback function for when the event is triggered
- * @memberof DataTable#oApi
- */
- function _fnBindAction( n, oData, fn )
- {
- $(n)
- .bind( 'click.DT', oData, function (e) {
- n.blur(); // Remove focus outline for mouse users
- fn(e);
- } )
- .bind( 'keypress.DT', oData, function (e){
+
+ return out;
+ }
+
+
+ /**
+ * Bind an event handers to allow a click or return key to activate the callback.
+ * This is good for accessibility since a return on the keyboard will have the
+ * same effect as a click, if the element has focus.
+ * @param {element} n Element to bind the action to
+ * @param {object} oData Data object to pass to the triggered function
+ * @param {function} fn Callback function for when the event is triggered
+ * @memberof DataTable#oApi
+ */
+ function _fnBindAction( n, oData, fn )
+ {
+ $(n)
+ .bind( 'click.DT', oData, function (e) {
+ n.blur(); // Remove focus outline for mouse users
+ fn(e);
+ } )
+ .bind( 'keypress.DT', oData, function (e){
if ( e.which === 13 ) {
+ e.preventDefault();
fn(e);
- } } )
- .bind( 'selectstart.DT', function () {
+ }
+ } )
+ .bind( 'selectstart.DT', function () {
/* Take the brutal approach to cancelling text selection */
return false;
- } );
- }
-
-
- /**
- * Register a callback function. Easily allows a callback function to be added to
- * an array store of callback functions that can then all be called together.
- * @param {object} oSettings dataTables settings object
- * @param {string} sStore Name of the array storage for the callbacks in oSettings
- * @param {function} fn Function to be called back
- * @param {string} sName Identifying name for the callback (i.e. a label)
- * @memberof DataTable#oApi
- */
- function _fnCallbackReg( oSettings, sStore, fn, sName )
- {
- if ( fn )
- {
- oSettings[sStore].push( {
- "fn": fn,
- "sName": sName
} );
- }
- }
-
-
- /**
- * Fire callback functions and trigger events. Note that the loop over the callback
- * array store is done backwards! Further note that you do not want to fire off triggers
- * in time sensitive applications (for example cell creation) as its slow.
- * @param {object} oSettings dataTables settings object
- * @param {string} sStore Name of the array storage for the callbacks in oSettings
- * @param {string} sTrigger Name of the jQuery custom event to trigger. If null no trigger
- * is fired
- * @param {array} aArgs Array of arguments to pass to the callback function / trigger
- * @memberof DataTable#oApi
- */
- function _fnCallbackFire( oSettings, sStore, sTrigger, aArgs )
+ }
+
+
+ /**
+ * Register a callback function. Easily allows a callback function to be added to
+ * an array store of callback functions that can then all be called together.
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sStore Name of the array storage for the callbacks in oSettings
+ * @param {function} fn Function to be called back
+ * @param {string} sName Identifying name for the callback (i.e. a label)
+ * @memberof DataTable#oApi
+ */
+ function _fnCallbackReg( oSettings, sStore, fn, sName )
+ {
+ if ( fn )
{
- var aoStore = oSettings[sStore];
- var aRet =[];
-
- for ( var i=aoStore.length-1 ; i>=0 ; i-- )
- {
- aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) );
- }
-
- if ( sTrigger !== null )
- {
- $(oSettings.oInstance).trigger(sTrigger, aArgs);
- }
-
- return aRet;
+ oSettings[sStore].push( {
+ "fn": fn,
+ "sName": sName
+ } );
}
-
-
- /**
- * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other
- * library, then we use that as it is fast, safe and accurate. If the function isn't
- * available then we need to built it ourselves - the inspiration for this function comes
- * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is
- * not perfect and absolutely should not be used as a replacement to json2.js - but it does
- * do what we need, without requiring a dependency for DataTables.
- * @param {object} o JSON object to be converted
- * @returns {string} JSON string
- * @memberof DataTable#oApi
- */
- var _fnJsonString = (window.JSON) ? JSON.stringify : function( o )
+ }
+
+
+ /**
+ * Fire callback functions and trigger events. Note that the loop over the
+ * callback array store is done backwards! Further note that you do not want to
+ * fire off triggers in time sensitive applications (for example cell creation)
+ * as its slow.
+ * @param {object} settings dataTables settings object
+ * @param {string} callbackArr Name of the array storage for the callbacks in
+ * oSettings
+ * @param {string} eventName Name of the jQuery custom event to trigger. If
+ * null no trigger is fired
+ * @param {array} args Array of arguments to pass to the callback function /
+ * trigger
+ * @memberof DataTable#oApi
+ */
+ function _fnCallbackFire( settings, callbackArr, eventName, args )
+ {
+ var ret = [];
+
+ if ( callbackArr ) {
+ ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
+ return val.fn.apply( settings.oInstance, args );
+ } );
+ }
+
+ if ( eventName !== null ) {
+ var e = $.Event( eventName+'.dt' );
+
+ $(settings.nTable).trigger( e, args );
+
+ ret.push( e.result );
+ }
+
+ return ret;
+ }
+
+
+ function _fnLengthOverflow ( settings )
+ {
+ var
+ start = settings._iDisplayStart,
+ end = settings.fnDisplayEnd(),
+ len = settings._iDisplayLength;
+
+ /* If we have space to show extra rows (backing up from the end point - then do so */
+ if ( start >= end )
{
- /* Not an object or array */
- var sType = typeof o;
- if (sType !== "object" || o === null)
- {
- // simple data type
- if (sType === "string")
- {
- o = '"'+o+'"';
- }
- return o+"";
- }
-
- /* If object or array, need to recurse over it */
- var
- sProp, mValue,
- json = [],
- bArr = $.isArray(o);
-
- for (sProp in o)
- {
- mValue = o[sProp];
- sType = typeof mValue;
-
- if (sType === "string")
- {
- mValue = '"'+mValue+'"';
- }
- else if (sType === "object" && mValue !== null)
- {
- mValue = _fnJsonString(mValue);
- }
-
- json.push((bArr ? "" : '"'+sProp+'":') + mValue);
- }
-
- return (bArr ? "[" : "{") + json + (bArr ? "]" : "}");
- };
-
-
- /**
- * From some browsers (specifically IE6/7) we need special handling to work around browser
- * bugs - this function is used to detect when these workarounds are needed.
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBrowserDetect( oSettings )
+ start = end - len;
+ }
+
+ // Keep the start record on the current page
+ start -= (start % len);
+
+ if ( len === -1 || start < 0 )
{
- /* IE6/7 will oversize a width 100% element inside a scrolling element, to include the
- * width of the scrollbar, while other browsers ensure the inner element is contained
- * without forcing scrolling
- */
- var n = $(
- '<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden">'+
- '<div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;">'+
- '<div id="DT_BrowserTest" style="width:100%; height:10px;"></div>'+
- '</div>'+
- '</div>')[0];
-
- document.body.appendChild( n );
- oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false;
- document.body.removeChild( n );
+ start = 0;
}
-
+
+ settings._iDisplayStart = start;
+ }
+
+
+ function _fnRenderer( settings, type )
+ {
+ var renderer = settings.renderer;
+ var host = DataTable.ext.renderer[type];
+
+ if ( $.isPlainObject( renderer ) && renderer[type] ) {
+ // Specific renderer for this type. If available use it, otherwise use
+ // the default.
+ return host[renderer[type]] || host._;
+ }
+ else if ( typeof renderer === 'string' ) {
+ // Common renderer - if there is one available for this type use it,
+ // otherwise use the default
+ return host[renderer] || host._;
+ }
+
+ // Use the default
+ return host._;
+ }
+
+
+ /**
+ * Detect the data source being used for the table. Used to simplify the code
+ * a little (ajax) and to make it compress a little smaller.
+ *
+ * @param {object} settings dataTables settings object
+ * @returns {string} Data source
+ * @memberof DataTable#oApi
+ */
+ function _fnDataSource ( settings )
+ {
+ if ( settings.oFeatures.bServerSide ) {
+ return 'ssp';
+ }
+ else if ( settings.ajax || settings.sAjaxSource ) {
+ return 'ajax';
+ }
+ return 'dom';
+ }
+
+ DataTable = function( options )
+ {
/**
* Perform a jQuery selector action on the table's TR elements (from the tbody) and
* return the resulting jQuery object.
@@ -4948,10 +5391,11 @@
* Can be either 'current', whereby the current sorting of the table is used, or
* 'original' whereby the original order the data was read into the table is used.
* @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
* 'current' and filter is 'applied', regardless of what they might be given as.
* @returns {object} jQuery object, filtered by the given selector.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -4968,100 +5412,13 @@
* // Filter to rows with 'Webkit' in them, add a background colour and then
* // remove the filter, thus highlighting the 'Webkit' rows only.
* oTable.fnFilter('Webkit');
- * oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue');
+ * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
* oTable.fnFilter('');
* } );
*/
this.$ = function ( sSelector, oOpts )
{
- var i, iLen, a = [], tr;
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var aoData = oSettings.aoData;
- var aiDisplay = oSettings.aiDisplay;
- var aiDisplayMaster = oSettings.aiDisplayMaster;
-
- if ( !oOpts )
- {
- oOpts = {};
- }
-
- oOpts = $.extend( {}, {
- "filter": "none", // applied
- "order": "current", // "original"
- "page": "all" // current
- }, oOpts );
-
- // Current page implies that order=current and fitler=applied, since it is fairly
- // senseless otherwise
- if ( oOpts.page == 'current' )
- {
- for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i<iLen ; i++ )
- {
- tr = aoData[ aiDisplay[i] ].nTr;
- if ( tr )
- {
- a.push( tr );
- }
- }
- }
- else if ( oOpts.order == "current" && oOpts.filter == "none" )
- {
- for ( i=0, iLen=aiDisplayMaster.length ; i<iLen ; i++ )
- {
- tr = aoData[ aiDisplayMaster[i] ].nTr;
- if ( tr )
- {
- a.push( tr );
- }
- }
- }
- else if ( oOpts.order == "current" && oOpts.filter == "applied" )
- {
- for ( i=0, iLen=aiDisplay.length ; i<iLen ; i++ )
- {
- tr = aoData[ aiDisplay[i] ].nTr;
- if ( tr )
- {
- a.push( tr );
- }
- }
- }
- else if ( oOpts.order == "original" && oOpts.filter == "none" )
- {
- for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- tr = aoData[ i ].nTr ;
- if ( tr )
- {
- a.push( tr );
- }
- }
- }
- else if ( oOpts.order == "original" && oOpts.filter == "applied" )
- {
- for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- tr = aoData[ i ].nTr;
- if ( $.inArray( i, aiDisplay ) !== -1 && tr )
- {
- a.push( tr );
- }
- }
- }
- else
- {
- _fnLog( oSettings, 1, "Unknown selection options" );
- }
-
- /* We need to filter on the TR elements and also 'find' in their descendants
- * to make the selector act like it would in a full table - so we need
- * to build both results and then combine them together
- */
- var jqA = $(a);
- var jqTRs = jqA.filter( sSelector );
- var jqDescendants = jqA.find( sSelector );
-
- return $( [].concat($.makeArray(jqTRs), $.makeArray(jqDescendants)) );
+ return this.api(true).$( sSelector, oOpts );
};
@@ -5069,7 +5426,7 @@
* Almost identical to $ in operation, but in this case returns the data for the matched
* rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
* rather than any descendants, so the data can be obtained for the row/cell. If matching
- * rows are found, the data returned is the original data array/object that was used to
+ * rows are found, the data returned is the original data array/object that was used to
* create the row (or a generated array if from a DOM source).
*
* This method is often useful in-combination with $ where both functions are given the
@@ -5082,12 +5439,13 @@
* Can be either 'current', whereby the current sorting of the table is used, or
* 'original' whereby the original order the data was read into the table is used.
* @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
+ * ("current") or not ("all"). If 'current' is given, then order is assumed to be
* 'current' and filter is 'applied', regardless of what they might be given as.
* @returns {array} Data for the matched elements. If any elements, as a result of the
- * selector, were not TR, TD or TH elements in the DataTable, they will have a null
+ * selector, were not TR, TD or TH elements in the DataTable, they will have a null
* entry in the array.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -5104,55 +5462,66 @@
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
*
- * // Filter to 'Webkit' and get all data for
+ * // Filter to 'Webkit' and get all data for
* oTable.fnFilter('Webkit');
- * var data = oTable._('tr', {"filter": "applied"});
- *
+ * var data = oTable._('tr', {"search": "applied"});
+ *
* // Do something with the data
- * alert( data.length+" rows matched the filter" );
+ * alert( data.length+" rows matched the search" );
* } );
*/
this._ = function ( sSelector, oOpts )
{
- var aOut = [];
- var i, iLen, iIndex;
- var aTrs = this.$( sSelector, oOpts );
+ return this.api(true).rows( sSelector, oOpts ).data();
+ };
- for ( i=0, iLen=aTrs.length ; i<iLen ; i++ )
- {
- aOut.push( this.fnGetData(aTrs[i]) );
- }
- return aOut;
+ /**
+ * Create a DataTables Api instance, with the currently selected tables for
+ * the Api's context.
+ * @param {boolean} [traditional=false] Set the API instance's context to be
+ * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
+ * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
+ * or if all tables captured in the jQuery object should be used.
+ * @return {DataTables.Api}
+ */
+ this.api = function ( traditional )
+ {
+ return traditional ?
+ new _Api(
+ _fnSettingsFromNode( this[ _ext.iApiIndex ] )
+ ) :
+ new _Api( this );
};
/**
* Add a single new row or multiple rows of data to the table. Please note
- * that this is suitable for client-side processing only - if you are using
+ * that this is suitable for client-side processing only - if you are using
* server-side processing (i.e. "bServerSide": true), then to add data, you
* must add it to the data source, i.e. the server-side, through an Ajax call.
- * @param {array|object} mData The data to be added to the table. This can be:
+ * @param {array|object} data The data to be added to the table. This can be:
* <ul>
* <li>1D array of data - add a single row with the data provided</li>
* <li>2D array of arrays - add multiple rows in a single call</li>
* <li>object - data object when using <i>mData</i></li>
* <li>array of objects - multiple data objects when using <i>mData</i></li>
* </ul>
- * @param {bool} [bRedraw=true] redraw the table or not
- * @returns {array} An array of integers, representing the list of indexes in
- * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
+ * @param {bool} [redraw=true] redraw the table or not
+ * @returns {array} An array of integers, representing the list of indexes in
+ * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
* the table.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* // Global var for counter
* var giCount = 2;
- *
+ *
* $(document).ready(function() {
* $('#example').dataTable();
* } );
- *
+ *
* function fnClickAddRow() {
* $('#example').dataTable().fnAddData( [
* giCount+".1",
@@ -5160,63 +5529,35 @@
* giCount+".3",
* giCount+".4" ]
* );
- *
+ *
* giCount++;
* }
*/
- this.fnAddData = function( mData, bRedraw )
+ this.fnAddData = function( data, redraw )
{
- if ( mData.length === 0 )
- {
- return [];
- }
-
- var aiReturn = [];
- var iTest;
-
- /* Find settings from table node */
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
+ var api = this.api( true );
+
/* Check if we want to add multiple rows or not */
- if ( typeof mData[0] === "object" && mData[0] !== null )
- {
- for ( var i=0 ; i<mData.length ; i++ )
- {
- iTest = _fnAddData( oSettings, mData[i] );
- if ( iTest == -1 )
- {
- return aiReturn;
- }
- aiReturn.push( iTest );
- }
- }
- else
- {
- iTest = _fnAddData( oSettings, mData );
- if ( iTest == -1 )
- {
- return aiReturn;
- }
- aiReturn.push( iTest );
- }
-
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-
- if ( bRedraw === undefined || bRedraw )
- {
- _fnReDraw( oSettings );
+ var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
+ api.rows.add( data ) :
+ api.row.add( data );
+
+ if ( redraw === undefined || redraw ) {
+ api.draw();
}
- return aiReturn;
+
+ return rows.flatten().toArray();
};
/**
- * This function will make DataTables recalculate the column sizes, based on the data
- * contained in the table and the sizes applied to the columns (in the DOM, CSS or
- * through the sWidth parameter). This can be useful when the width of the table's
+ * This function will make DataTables recalculate the column sizes, based on the data
+ * contained in the table and the sizes applied to the columns (in the DOM, CSS or
+ * through the sWidth parameter). This can be useful when the width of the table's
* parent element changes (for example a window resize).
* @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -5224,7 +5565,7 @@
* "sScrollY": "200px",
* "bPaginate": false
* } );
- *
+ *
* $(window).bind('resize', function () {
* oTable.fnAdjustColumnSizing();
* } );
@@ -5232,17 +5573,16 @@
*/
this.fnAdjustColumnSizing = function ( bRedraw )
{
- var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
- _fnAdjustColumnSizing( oSettings );
-
- if ( bRedraw === undefined || bRedraw )
- {
- this.fnDraw( false );
+ var api = this.api( true ).columns.adjust();
+ var settings = api.settings()[0];
+ var scroll = settings.oScroll;
+
+ if ( bRedraw === undefined || bRedraw ) {
+ api.draw( false );
}
- else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
- {
+ else if ( scroll.sX !== "" || scroll.sY !== "" ) {
/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
- this.oApi._fnScrollDraw(oSettings);
+ _fnScrollDraw( settings );
}
};
@@ -5251,39 +5591,38 @@
* Quickly and simply clear a table
* @param {bool} [bRedraw=true] redraw the table or not
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
* oTable.fnClearTable();
* } );
*/
this.fnClearTable = function( bRedraw )
{
- /* Find settings from table node */
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- _fnClearTable( oSettings );
-
- if ( bRedraw === undefined || bRedraw )
- {
- _fnDraw( oSettings );
+ var api = this.api( true ).clear();
+
+ if ( bRedraw === undefined || bRedraw ) {
+ api.draw();
}
};
/**
- * The exact opposite of 'opening' a row, this function will close any rows which
+ * The exact opposite of 'opening' a row, this function will close any rows which
* are currently 'open'.
* @param {node} nTr the table row to 'close'
* @returns {int} 0 on success, or 1 if failed (can't find the row)
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable;
- *
+ *
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
@@ -5292,110 +5631,61 @@
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
- *
+ *
* oTable = $('#example').dataTable();
* } );
*/
this.fnClose = function( nTr )
{
- /* Find settings from table node */
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
- for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
- {
- if ( oSettings.aoOpenRows[i].nParent == nTr )
- {
- var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
- if ( nTrParent )
- {
- /* Remove it if it is currently on display */
- nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
- }
- oSettings.aoOpenRows.splice( i, 1 );
- return 0;
- }
- }
- return 1;
+ this.api( true ).row( nTr ).child.hide();
};
/**
* Remove a row for the table
- * @param {mixed} mTarget The index of the row from aoData to be deleted, or
+ * @param {mixed} target The index of the row from aoData to be deleted, or
* the TR element you want to delete
- * @param {function|null} [fnCallBack] Callback function
- * @param {bool} [bRedraw=true] Redraw the table or not
+ * @param {function|null} [callBack] Callback function
+ * @param {bool} [redraw=true] Redraw the table or not
* @returns {array} The row that was deleted
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Immediately remove the first row
* oTable.fnDeleteRow( 0 );
* } );
*/
- this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
+ this.fnDeleteRow = function( target, callback, redraw )
{
- /* Find settings from table node */
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var i, iLen, iAODataIndex;
-
- iAODataIndex = (typeof mTarget === 'object') ?
- _fnNodeToDataIndex(oSettings, mTarget) : mTarget;
-
- /* Return the data array from this row */
- var oData = oSettings.aoData.splice( iAODataIndex, 1 );
+ var api = this.api( true );
+ var rows = api.rows( target );
+ var settings = rows.settings()[0];
+ var data = settings.aoData[ rows[0][0] ];
- /* Update the _DT_RowIndex parameter */
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- if ( oSettings.aoData[i].nTr !== null )
- {
- oSettings.aoData[i].nTr._DT_RowIndex = i;
- }
- }
-
- /* Remove the target row from the search array */
- var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
- oSettings.asDataSearch.splice( iDisplayIndex, 1 );
-
- /* Delete from the display arrays */
- _fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
- _fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
-
- /* If there is a user callback function - call it */
- if ( typeof fnCallBack === "function" )
- {
- fnCallBack.call( this, oSettings, oData );
- }
-
- /* Check for an 'overflow' they case for displaying the table */
- if ( oSettings._iDisplayStart >= oSettings.fnRecordsDisplay() )
- {
- oSettings._iDisplayStart -= oSettings._iDisplayLength;
- if ( oSettings._iDisplayStart < 0 )
- {
- oSettings._iDisplayStart = 0;
- }
+ rows.remove();
+
+ if ( callback ) {
+ callback.call( this, settings, data );
}
-
- if ( bRedraw === undefined || bRedraw )
- {
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
+
+ if ( redraw === undefined || redraw ) {
+ api.draw();
}
-
- return oData;
+
+ return data;
};
/**
- * Restore the table to it's original state in the DOM by removing all of DataTables
+ * Restore the table to it's original state in the DOM by removing all of DataTables
* enhancements, alterations to the DOM structure of the table and event listeners.
- * @param {boolean} [bRemove=false] Completely remove the table from the DOM
+ * @param {boolean} [remove=false] Completely remove the table from the DOM
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -5404,159 +5694,31 @@
* oTable.fnDestroy();
* } );
*/
- this.fnDestroy = function ( bRemove )
+ this.fnDestroy = function ( remove )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var nOrig = oSettings.nTableWrapper.parentNode;
- var nBody = oSettings.nTBody;
- var i, iLen;
-
- bRemove = (bRemove===undefined) ? false : bRemove;
-
- /* Flag to note that the table is currently being destroyed - no action should be taken */
- oSettings.bDestroying = true;
-
- /* Fire off the destroy callbacks for plug-ins etc */
- _fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] );
-
- /* If the table is not being removed, restore the hidden columns */
- if ( !bRemove )
- {
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- if ( oSettings.aoColumns[i].bVisible === false )
- {
- this.fnSetColumnVis( i, true );
- }
- }
- }
-
- /* Blitz all DT events */
- $(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
-
- /* If there is an 'empty' indicator row, remove it */
- $('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
-
- /* When scrolling we had to break the table up - restore it */
- if ( oSettings.nTable != oSettings.nTHead.parentNode )
- {
- $(oSettings.nTable).children('thead').remove();
- oSettings.nTable.appendChild( oSettings.nTHead );
- }
-
- if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
- {
- $(oSettings.nTable).children('tfoot').remove();
- oSettings.nTable.appendChild( oSettings.nTFoot );
- }
-
- /* Remove the DataTables generated nodes, events and classes */
- oSettings.nTable.parentNode.removeChild( oSettings.nTable );
- $(oSettings.nTableWrapper).remove();
-
- oSettings.aaSorting = [];
- oSettings.aaSortingFixed = [];
- _fnSortingClasses( oSettings );
-
- $(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') );
-
- $('th, td', oSettings.nTHead).removeClass( [
- oSettings.oClasses.sSortable,
- oSettings.oClasses.sSortableAsc,
- oSettings.oClasses.sSortableDesc,
- oSettings.oClasses.sSortableNone ].join(' ')
- );
- if ( oSettings.bJUI )
- {
- $('th span.'+oSettings.oClasses.sSortIcon
- + ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove();
-
- $('th, td', oSettings.nTHead).each( function () {
- var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this);
- var kids = jqWrapper.contents();
- $(this).append( kids );
- jqWrapper.remove();
- } );
- }
-
- /* Add the TR elements back into the table in their original order */
- if ( !bRemove && oSettings.nTableReinsertBefore )
- {
- nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore );
- }
- else if ( !bRemove )
- {
- nOrig.appendChild( oSettings.nTable );
- }
-
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- if ( oSettings.aoData[i].nTr !== null )
- {
- nBody.appendChild( oSettings.aoData[i].nTr );
- }
- }
-
- /* Restore the width of the original table */
- if ( oSettings.oFeatures.bAutoWidth === true )
- {
- oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
- }
-
- /* If the were originally stripe classes - then we add them back here. Note
- * this is not fool proof (for example if not all rows had stripe classes - but
- * it's a good effort without getting carried away
- */
- iLen = oSettings.asDestroyStripes.length;
- if (iLen)
- {
- var anRows = $(nBody).children('tr');
- for ( i=0 ; i<iLen ; i++ )
- {
- anRows.filter(':nth-child(' + iLen + 'n + ' + i + ')').addClass( oSettings.asDestroyStripes[i] );
- }
- }
-
- /* Remove the settings object from the settings array */
- for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
- {
- if ( DataTable.settings[i] == oSettings )
- {
- DataTable.settings.splice( i, 1 );
- }
- }
-
- /* End it all */
- oSettings = null;
- oInit = null;
+ this.api( true ).destroy( remove );
};
/**
* Redraw the table
- * @param {bool} [bComplete=true] Re-filter and resort (if enabled) the table before the draw.
+ * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
* oTable.fnDraw();
* } );
*/
- this.fnDraw = function( bComplete )
+ this.fnDraw = function( complete )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- if ( bComplete === false )
- {
- _fnCalculateEnd( oSettings );
- _fnDraw( oSettings );
- }
- else
- {
- _fnReDraw( oSettings );
- }
+ // Note that this isn't an exact match to the old call to _fnDraw - it takes
+ // into account the new data, but can hold position.
+ this.api( true ).draw( complete );
};
@@ -5569,100 +5731,45 @@
* @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
* @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Sometime later - filter...
* oTable.fnFilter( 'test string' );
* } );
*/
this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
- if ( !oSettings.oFeatures.bFilter )
- {
- return;
- }
-
- if ( bRegex === undefined || bRegex === null )
- {
- bRegex = false;
- }
-
- if ( bSmart === undefined || bSmart === null )
- {
- bSmart = true;
- }
-
- if ( bShowGlobal === undefined || bShowGlobal === null )
- {
- bShowGlobal = true;
- }
-
- if ( bCaseInsensitive === undefined || bCaseInsensitive === null )
- {
- bCaseInsensitive = true;
- }
-
- if ( iColumn === undefined || iColumn === null )
- {
- /* Global filter */
- _fnFilterComplete( oSettings, {
- "sSearch":sInput+"",
- "bRegex": bRegex,
- "bSmart": bSmart,
- "bCaseInsensitive": bCaseInsensitive
- }, 1 );
-
- if ( bShowGlobal && oSettings.aanFeatures.f )
- {
- var n = oSettings.aanFeatures.f;
- for ( var i=0, iLen=n.length ; i<iLen ; i++ )
- {
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame...
- try {
- if ( n[i]._DT_Input != document.activeElement )
- {
- $(n[i]._DT_Input).val( sInput );
- }
- }
- catch ( e ) {
- $(n[i]._DT_Input).val( sInput );
- }
- }
- }
+ var api = this.api( true );
+
+ if ( iColumn === null || iColumn === undefined ) {
+ api.search( sInput, bRegex, bSmart, bCaseInsensitive );
}
- else
- {
- /* Single column filter */
- $.extend( oSettings.aoPreSearchCols[ iColumn ], {
- "sSearch": sInput+"",
- "bRegex": bRegex,
- "bSmart": bSmart,
- "bCaseInsensitive": bCaseInsensitive
- } );
- _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
+ else {
+ api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
}
+
+ api.draw();
};
/**
- * Get the data for the whole table, an individual row or an individual cell based on the
+ * Get the data for the whole table, an individual row or an individual cell based on the
* provided parameters.
- * @param {int|node} [mRow] A TR row node, TD/TH cell node or an integer. If given as
+ * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
* a TR node then the data source for the whole row will be returned. If given as a
* TD/TH cell node then iCol will be automatically calculated and the data for the
* cell returned. If given as an integer, then this is treated as the aoData internal
* data index for the row (see fnGetPosition) and the data for that row used.
- * @param {int} [iCol] Optional column index that you want the data of.
+ * @param {int} [col] Optional column index that you want the data of.
* @returns {array|object|string} If mRow is undefined, then the data for all rows is
* returned. If mRow is defined, just data for that row, and is iCol is
* defined, only data for the designated cell is returned.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* // Row data
@@ -5686,108 +5793,94 @@
* } );
* } );
*/
- this.fnGetData = function( mRow, iCol )
+ this.fnGetData = function( src, col )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
- if ( mRow !== undefined )
- {
- var iRow = mRow;
- if ( typeof mRow === 'object' )
- {
- var sNode = mRow.nodeName.toLowerCase();
- if (sNode === "tr" )
- {
- iRow = _fnNodeToDataIndex(oSettings, mRow);
- }
- else if ( sNode === "td" )
- {
- iRow = _fnNodeToDataIndex(oSettings, mRow.parentNode);
- iCol = _fnNodeToColumnIndex( oSettings, iRow, mRow );
- }
- }
+ var api = this.api( true );
- if ( iCol !== undefined )
- {
- return _fnGetCellData( oSettings, iRow, iCol, '' );
- }
- return (oSettings.aoData[iRow]!==undefined) ?
- oSettings.aoData[iRow]._aData : null;
+ if ( src !== undefined ) {
+ var type = src.nodeName ? src.nodeName.toLowerCase() : '';
+
+ return col !== undefined || type == 'td' || type == 'th' ?
+ api.cell( src, col ).data() :
+ api.row( src ).data() || null;
}
- return _fnGetDataMaster( oSettings );
+
+ return api.data().toArray();
};
/**
- * Get an array of the TR nodes that are used in the table's body. Note that you will
- * typically want to use the '$' API method in preference to this as it is more
+ * Get an array of the TR nodes that are used in the table's body. Note that you will
+ * typically want to use the '$' API method in preference to this as it is more
* flexible.
* @param {int} [iRow] Optional row index for the TR element you want
* @returns {array|node} If iRow is undefined, returns an array of all TR elements
* in the table's body, or iRow is defined, just the TR element requested.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Get the nodes from the table
* var nNodes = oTable.fnGetNodes( );
* } );
*/
this.fnGetNodes = function( iRow )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
- if ( iRow !== undefined ) {
- return (oSettings.aoData[iRow]!==undefined) ?
- oSettings.aoData[iRow].nTr : null;
- }
- return _fnGetTrNodes( oSettings );
+ var api = this.api( true );
+
+ return iRow !== undefined ?
+ api.row( iRow ).node() :
+ api.rows().nodes().flatten().toArray();
};
/**
* Get the array indexes of a particular cell from it's DOM element
* and column index including hidden columns
- * @param {node} nNode this can either be a TR, TD or TH in the table's body
+ * @param {node} node this can either be a TR, TD or TH in the table's body
* @returns {int} If nNode is given as a TR, then a single index is returned, or
- * if given as a cell, an array of [row index, column index (visible),
+ * if given as a cell, an array of [row index, column index (visible),
* column index (all)] is given.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* $('#example tbody td').click( function () {
* // Get the position of the current data from the node
* var aPos = oTable.fnGetPosition( this );
- *
+ *
* // Get the data array for this row
* var aData = oTable.fnGetData( aPos[0] );
- *
+ *
* // Update the data array and return the value
* aData[ aPos[1] ] = 'clicked';
* this.innerHTML = 'clicked';
* } );
- *
+ *
* // Init DataTables
* oTable = $('#example').dataTable();
* } );
*/
- this.fnGetPosition = function( nNode )
+ this.fnGetPosition = function( node )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var sNodeName = nNode.nodeName.toUpperCase();
-
- if ( sNodeName == "TR" )
- {
- return _fnNodeToDataIndex(oSettings, nNode);
+ var api = this.api( true );
+ var nodeName = node.nodeName.toUpperCase();
+
+ if ( nodeName == 'TR' ) {
+ return api.row( node ).index();
}
- else if ( sNodeName == "TD" || sNodeName == "TH" )
- {
- var iDataIndex = _fnNodeToDataIndex( oSettings, nNode.parentNode );
- var iColumnIndex = _fnNodeToColumnIndex( oSettings, iDataIndex, nNode );
- return [ iDataIndex, _fnColumnIndexToVisible(oSettings, iColumnIndex ), iColumnIndex ];
+ else if ( nodeName == 'TD' || nodeName == 'TH' ) {
+ var cell = api.cell( node ).index();
+
+ return [
+ cell.row,
+ cell.columnVisible,
+ cell.column
+ ];
}
return null;
};
@@ -5798,11 +5891,12 @@
* @param {node} nTr the table row to check
* @returns {boolean} true if the row is currently open, false otherwise
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable;
- *
+ *
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
@@ -5811,30 +5905,20 @@
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
- *
+ *
* oTable = $('#example').dataTable();
* } );
*/
this.fnIsOpen = function( nTr )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var aoOpenRows = oSettings.aoOpenRows;
-
- for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
- {
- if ( oSettings.aoOpenRows[i].nParent == nTr )
- {
- return true;
- }
- }
- return false;
+ return this.api( true ).row( nTr ).child.isShown();
};
/**
- * This function will place a new row directly after a row which is currently
- * on display on the page, with the HTML contents that is passed into the
- * function. This can be used, for example, to ask for confirmation that a
+ * This function will place a new row directly after a row which is currently
+ * on display on the page, with the HTML contents that is passed into the
+ * function. This can be used, for example, to ask for confirmation that a
* particular record should be deleted.
* @param {node} nTr The table row to 'open'
* @param {string|node|jQuery} mHtml The HTML to put into the row
@@ -5843,11 +5927,12 @@
* first parameter, is not found in the table, this method will silently
* return.
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable;
- *
+ *
* // 'open' an information row when a row is clicked on
* $('#example tbody tr').click( function () {
* if ( oTable.fnIsOpen(this) ) {
@@ -5856,64 +5941,29 @@
* oTable.fnOpen( this, "Temporary row opened", "info_row" );
* }
* } );
- *
+ *
* oTable = $('#example').dataTable();
* } );
*/
this.fnOpen = function( nTr, mHtml, sClass )
{
- /* Find settings from table node */
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
-
- /* Check that the row given is in the table */
- var nTableRows = _fnGetTrNodes( oSettings );
- if ( $.inArray(nTr, nTableRows) === -1 )
- {
- return;
- }
-
- /* the old open one if there is one */
- this.fnClose( nTr );
-
- var nNewRow = document.createElement("tr");
- var nNewCell = document.createElement("td");
- nNewRow.appendChild( nNewCell );
- nNewCell.className = sClass;
- nNewCell.colSpan = _fnVisbleColumns( oSettings );
-
- if (typeof mHtml === "string")
- {
- nNewCell.innerHTML = mHtml;
- }
- else
- {
- $(nNewCell).html( mHtml );
- }
-
- /* If the nTr isn't on the page at the moment - then we don't insert at the moment */
- var nTrs = $('tr', oSettings.nTBody);
- if ( $.inArray(nTr, nTrs) != -1 )
- {
- $(nNewRow).insertAfter(nTr);
- }
-
- oSettings.aoOpenRows.push( {
- "nTr": nNewRow,
- "nParent": nTr
- } );
-
- return nNewRow;
+ return this.api( true )
+ .row( nTr )
+ .child( mHtml, sClass )
+ .show()
+ .child()[0];
};
/**
- * Change the pagination - provides the internal logic for pagination in a simple API
- * function. With this function you can have a DataTables table go to the next,
+ * Change the pagination - provides the internal logic for pagination in a simple API
+ * function. With this function you can have a DataTables table go to the next,
* previous, first or last pages.
* @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
* or page number to jump to (integer), note that page 0 is the first page.
* @param {bool} [bRedraw=true] Redraw the table or not
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -5923,13 +5973,10 @@
*/
this.fnPageChange = function ( mAction, bRedraw )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- _fnPageChange( oSettings, mAction );
- _fnCalculateEnd( oSettings );
-
- if ( bRedraw === undefined || bRedraw )
- {
- _fnDraw( oSettings );
+ var api = this.api( true ).page( mAction );
+
+ if ( bRedraw === undefined || bRedraw ) {
+ api.draw(false);
}
};
@@ -5940,159 +5987,66 @@
* @param {bool} bShow Show (true) or hide (false) the column
* @param {bool} [bRedraw=true] Redraw the table or not
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Hide the second column after initialisation
* oTable.fnSetColumnVis( 1, false );
* } );
*/
this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var i, iLen;
- var aoColumns = oSettings.aoColumns;
- var aoData = oSettings.aoData;
- var nTd, bAppend, iBefore;
-
- /* No point in doing anything if we are requesting what is already true */
- if ( aoColumns[iCol].bVisible == bShow )
- {
- return;
- }
-
- /* Show the column */
- if ( bShow )
- {
- var iInsert = 0;
- for ( i=0 ; i<iCol ; i++ )
- {
- if ( aoColumns[i].bVisible )
- {
- iInsert++;
- }
- }
-
- /* Need to decide if we should use appendChild or insertBefore */
- bAppend = (iInsert >= _fnVisbleColumns( oSettings ));
-
- /* Which coloumn should we be inserting before? */
- if ( !bAppend )
- {
- for ( i=iCol ; i<aoColumns.length ; i++ )
- {
- if ( aoColumns[i].bVisible )
- {
- iBefore = i;
- break;
- }
- }
- }
+ var api = this.api( true ).column( iCol ).visible( bShow );
- for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- if ( aoData[i].nTr !== null )
- {
- if ( bAppend )
- {
- aoData[i].nTr.appendChild(
- aoData[i]._anHidden[iCol]
- );
- }
- else
- {
- aoData[i].nTr.insertBefore(
- aoData[i]._anHidden[iCol],
- _fnGetTdNodes( oSettings, i )[iBefore] );
- }
- }
- }
+ if ( bRedraw === undefined || bRedraw ) {
+ api.columns.adjust().draw();
}
- else
- {
- /* Remove a column from display */
- for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
- {
- if ( aoData[i].nTr !== null )
- {
- nTd = _fnGetTdNodes( oSettings, i )[iCol];
- aoData[i]._anHidden[iCol] = nTd;
- nTd.parentNode.removeChild( nTd );
- }
- }
- }
-
- /* Clear to set the visible flag */
- aoColumns[iCol].bVisible = bShow;
-
- /* Redraw the header and footer based on the new column visibility */
- _fnDrawHead( oSettings, oSettings.aoHeader );
- if ( oSettings.nTFoot )
- {
- _fnDrawHead( oSettings, oSettings.aoFooter );
- }
-
- /* If there are any 'open' rows, then we need to alter the colspan for this col change */
- for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
- {
- oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
- }
-
- /* Do a redraw incase anything depending on the table columns needs it
- * (built-in: scrolling)
- */
- if ( bRedraw === undefined || bRedraw )
- {
- _fnAdjustColumnSizing( oSettings );
- _fnDraw( oSettings );
- }
-
- _fnSaveState( oSettings );
};
/**
* Get the settings for a particular table for external manipulation
- * @returns {object} DataTables settings object. See
+ * @returns {object} DataTables settings object. See
* {@link DataTable.models.oSettings}
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* var oSettings = oTable.fnSettings();
- *
+ *
* // Show an example parameter from the settings
* alert( oSettings._iDisplayStart );
* } );
*/
this.fnSettings = function()
{
- return _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
+ return _fnSettingsFromNode( this[_ext.iApiIndex] );
};
/**
* Sort the table by a particular column
- * @param {int} iCol the data index to sort on. Note that this will not match the
+ * @param {int} iCol the data index to sort on. Note that this will not match the
* 'display index' if you have hidden data entries
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Sort immediately with columns 0 and 1
* oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
* } );
*/
this.fnSort = function( aaSort )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- oSettings.aaSorting = aaSort;
- _fnSort( oSettings );
+ this.api( true ).order( aaSort ).draw();
};
@@ -6102,19 +6056,19 @@
* @param {int} iColumn the column that a click on this node will sort on
* @param {function} [fnCallback] callback function when sort is run
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
- *
+ *
* // Sort on column 1, when 'sorter' is clicked on
* oTable.fnSortListener( document.getElementById('sorter'), 1 );
* } );
*/
this.fnSortListener = function( nNode, iColumn, fnCallback )
{
- _fnSortAttachListener( _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ), nNode, iColumn,
- fnCallback );
+ this.api( true ).order.listener( nNode, iColumn, fnCallback );
};
@@ -6125,89 +6079,38 @@
* self-referencing in order to make the multi column updates easier.
* @param {object|array|string} mData Data to update the cell/row with
* @param {node|int} mRow TR element you want to update or the aoData index
- * @param {int} [iColumn] The column to update (not used of mData is an array or object)
+ * @param {int} [iColumn] The column to update, give as null or undefined to
+ * update a whole row.
* @param {bool} [bRedraw=true] Redraw the table or not
* @param {bool} [bAction=true] Perform pre-draw actions or not
* @returns {int} 0 on success, 1 on error
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
* var oTable = $('#example').dataTable();
* oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
- * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1, 0 ); // Row
+ * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
* } );
*/
this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
{
- var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
- var i, iLen, sDisplay;
- var iRow = (typeof mRow === 'object') ?
- _fnNodeToDataIndex(oSettings, mRow) : mRow;
-
- if ( $.isArray(mData) && iColumn === undefined )
- {
- /* Array update - update the whole row */
- oSettings.aoData[iRow]._aData = mData.slice();
-
- /* Flag to the function that we are recursing */
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
- }
- }
- else if ( $.isPlainObject(mData) && iColumn === undefined )
- {
- /* Object update - update the whole row - assume the developer gets the object right */
- oSettings.aoData[iRow]._aData = $.extend( true, {}, mData );
+ var api = this.api( true );
- for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
- {
- this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
- }
+ if ( iColumn === undefined || iColumn === null ) {
+ api.row( mRow ).data( mData );
}
- else
- {
- /* Individual cell update */
- _fnSetCellData( oSettings, iRow, iColumn, mData );
- sDisplay = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
-
- var oCol = oSettings.aoColumns[iColumn];
- if ( oCol.fnRender !== null )
- {
- sDisplay = _fnRender( oSettings, iRow, iColumn );
- if ( oCol.bUseRendered )
- {
- _fnSetCellData( oSettings, iRow, iColumn, sDisplay );
- }
- }
-
- if ( oSettings.aoData[iRow].nTr !== null )
- {
- /* Do the actual HTML update */
- _fnGetTdNodes( oSettings, iRow )[iColumn].innerHTML = sDisplay;
- }
+ else {
+ api.cell( mRow, iColumn ).data( mData );
}
-
- /* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
- * will rebuild the search array - however, the redraw might be disabled by the user)
- */
- var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
- oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow(
- oSettings,
- _fnGetRowData( oSettings, iRow, 'filter', _fnGetColumns( oSettings, 'bSearchable' ) )
- );
-
- /* Perform pre-draw actions */
- if ( bAction === undefined || bAction )
- {
- _fnAdjustColumnSizing( oSettings );
+
+ if ( bAction === undefined || bAction ) {
+ api.columns.adjust();
}
-
- /* Redraw the table */
- if ( bRedraw === undefined || bRedraw )
- {
- _fnReDraw( oSettings );
+
+ if ( bRedraw === undefined || bRedraw ) {
+ api.draw();
}
return 0;
};
@@ -6222,6 +6125,7 @@
* version, or false if this version of DataTales is not suitable
* @method
* @dtopt API
+ * @deprecated Since v1.10
*
* @example
* $(document).ready(function() {
@@ -6229,187 +6133,98 @@
* alert( oTable.fnVersionCheck( '1.9.0' ) );
* } );
*/
- this.fnVersionCheck = DataTable.ext.fnVersionCheck;
-
+ this.fnVersionCheck = _ext.fnVersionCheck;
- /*
- * This is really a good bit rubbish this method of exposing the internal methods
- * publicly... - To be fixed in 2.0 using methods on the prototype
- */
-
-
- /**
- * Create a wrapper function for exporting an internal functions to an external API.
- * @param {string} sFunc API function name
- * @returns {function} wrapped function
- * @memberof DataTable#oApi
- */
- function _fnExternApiFunc (sFunc)
- {
- return function() {
- var aArgs = [_fnSettingsFromNode(this[DataTable.ext.iApiIndex])].concat(
- Array.prototype.slice.call(arguments) );
- return DataTable.ext.oApi[sFunc].apply( this, aArgs );
- };
+
+ var _that = this;
+ var emptyInit = options === undefined;
+ var len = this.length;
+
+ if ( emptyInit ) {
+ options = {};
}
-
-
- /**
- * Reference to internal functions for use by plug-in developers. Note that these
- * methods are references to internal functions and are considered to be private.
- * If you use these methods, be aware that they are liable to change between versions
- * (check the upgrade notes).
- * @namespace
- */
- this.oApi = {
- "_fnExternApiFunc": _fnExternApiFunc,
- "_fnInitialise": _fnInitialise,
- "_fnInitComplete": _fnInitComplete,
- "_fnLanguageCompat": _fnLanguageCompat,
- "_fnAddColumn": _fnAddColumn,
- "_fnColumnOptions": _fnColumnOptions,
- "_fnAddData": _fnAddData,
- "_fnCreateTr": _fnCreateTr,
- "_fnGatherData": _fnGatherData,
- "_fnBuildHead": _fnBuildHead,
- "_fnDrawHead": _fnDrawHead,
- "_fnDraw": _fnDraw,
- "_fnReDraw": _fnReDraw,
- "_fnAjaxUpdate": _fnAjaxUpdate,
- "_fnAjaxParameters": _fnAjaxParameters,
- "_fnAjaxUpdateDraw": _fnAjaxUpdateDraw,
- "_fnServerParams": _fnServerParams,
- "_fnAddOptionsHtml": _fnAddOptionsHtml,
- "_fnFeatureHtmlTable": _fnFeatureHtmlTable,
- "_fnScrollDraw": _fnScrollDraw,
- "_fnAdjustColumnSizing": _fnAdjustColumnSizing,
- "_fnFeatureHtmlFilter": _fnFeatureHtmlFilter,
- "_fnFilterComplete": _fnFilterComplete,
- "_fnFilterCustom": _fnFilterCustom,
- "_fnFilterColumn": _fnFilterColumn,
- "_fnFilter": _fnFilter,
- "_fnBuildSearchArray": _fnBuildSearchArray,
- "_fnBuildSearchRow": _fnBuildSearchRow,
- "_fnFilterCreateSearch": _fnFilterCreateSearch,
- "_fnDataToSearch": _fnDataToSearch,
- "_fnSort": _fnSort,
- "_fnSortAttachListener": _fnSortAttachListener,
- "_fnSortingClasses": _fnSortingClasses,
- "_fnFeatureHtmlPaginate": _fnFeatureHtmlPaginate,
- "_fnPageChange": _fnPageChange,
- "_fnFeatureHtmlInfo": _fnFeatureHtmlInfo,
- "_fnUpdateInfo": _fnUpdateInfo,
- "_fnFeatureHtmlLength": _fnFeatureHtmlLength,
- "_fnFeatureHtmlProcessing": _fnFeatureHtmlProcessing,
- "_fnProcessingDisplay": _fnProcessingDisplay,
- "_fnVisibleToColumnIndex": _fnVisibleToColumnIndex,
- "_fnColumnIndexToVisible": _fnColumnIndexToVisible,
- "_fnNodeToDataIndex": _fnNodeToDataIndex,
- "_fnVisbleColumns": _fnVisbleColumns,
- "_fnCalculateEnd": _fnCalculateEnd,
- "_fnConvertToWidth": _fnConvertToWidth,
- "_fnCalculateColumnWidths": _fnCalculateColumnWidths,
- "_fnScrollingWidthAdjust": _fnScrollingWidthAdjust,
- "_fnGetWidestNode": _fnGetWidestNode,
- "_fnGetMaxLenString": _fnGetMaxLenString,
- "_fnStringToCss": _fnStringToCss,
- "_fnDetectType": _fnDetectType,
- "_fnSettingsFromNode": _fnSettingsFromNode,
- "_fnGetDataMaster": _fnGetDataMaster,
- "_fnGetTrNodes": _fnGetTrNodes,
- "_fnGetTdNodes": _fnGetTdNodes,
- "_fnEscapeRegex": _fnEscapeRegex,
- "_fnDeleteIndex": _fnDeleteIndex,
- "_fnReOrderIndex": _fnReOrderIndex,
- "_fnColumnOrdering": _fnColumnOrdering,
- "_fnLog": _fnLog,
- "_fnClearTable": _fnClearTable,
- "_fnSaveState": _fnSaveState,
- "_fnLoadState": _fnLoadState,
- "_fnCreateCookie": _fnCreateCookie,
- "_fnReadCookie": _fnReadCookie,
- "_fnDetectHeader": _fnDetectHeader,
- "_fnGetUniqueThs": _fnGetUniqueThs,
- "_fnScrollBarWidth": _fnScrollBarWidth,
- "_fnApplyToChildren": _fnApplyToChildren,
- "_fnMap": _fnMap,
- "_fnGetRowData": _fnGetRowData,
- "_fnGetCellData": _fnGetCellData,
- "_fnSetCellData": _fnSetCellData,
- "_fnGetObjectDataFn": _fnGetObjectDataFn,
- "_fnSetObjectDataFn": _fnSetObjectDataFn,
- "_fnApplyColumnDefs": _fnApplyColumnDefs,
- "_fnBindAction": _fnBindAction,
- "_fnExtend": _fnExtend,
- "_fnCallbackReg": _fnCallbackReg,
- "_fnCallbackFire": _fnCallbackFire,
- "_fnJsonString": _fnJsonString,
- "_fnRender": _fnRender,
- "_fnNodeToColumnIndex": _fnNodeToColumnIndex,
- "_fnInfoMacros": _fnInfoMacros,
- "_fnBrowserDetect": _fnBrowserDetect,
- "_fnGetColumns": _fnGetColumns
- };
-
- $.extend( DataTable.ext.oApi, this.oApi );
-
- for ( var sFunc in DataTable.ext.oApi )
- {
- if ( sFunc )
- {
- this[sFunc] = _fnExternApiFunc(sFunc);
+
+ this.oApi = this.internal = _ext.internal;
+
+ // Extend with old style plug-in API methods
+ for ( var fn in DataTable.ext.internal ) {
+ if ( fn ) {
+ this[fn] = _fnExternApiFunc(fn);
}
}
-
-
- var _that = this;
+
this.each(function() {
+ // For each initialisation we want to give it a clean initialisation
+ // object that can be bashed around
+ var o = {};
+ var oInit = len > 1 ? // optimisation for single table case
+ _fnExtend( o, options, true ) :
+ options;
+
+ /*global oInit,_that,emptyInit*/
var i=0, iLen, j, jLen, k, kLen;
var sId = this.getAttribute( 'id' );
var bInitHandedOff = false;
- var bUsePassedData = false;
+ var defaults = DataTable.defaults;
+ var $this = $(this);
/* Sanity check */
if ( this.nodeName.toLowerCase() != 'table' )
{
- _fnLog( null, 0, "Attempted to initialise DataTables on a node which is not a "+
- "table: "+this.nodeName );
+ _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
return;
}
+ /* Backwards compatibility for the defaults */
+ _fnCompatOpts( defaults );
+ _fnCompatCols( defaults.column );
+
+ /* Convert the camel-case defaults to Hungarian */
+ _fnCamelToHungarian( defaults, defaults, true );
+ _fnCamelToHungarian( defaults.column, defaults.column, true );
+
+ /* Setting up the initialisation object */
+ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
+
+
+
/* Check to see if we are re-initialising a table */
- for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
+ var allSettings = DataTable.settings;
+ for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
{
+ var s = allSettings[i];
+
/* Base check on table node */
- if ( DataTable.settings[i].nTable == this )
+ if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
{
- if ( oInit === undefined || oInit.bRetrieve )
+ var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
+ var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
+
+ if ( emptyInit || bRetrieve )
{
- return DataTable.settings[i].oInstance;
+ return s.oInstance;
}
- else if ( oInit.bDestroy )
+ else if ( bDestroy )
{
- DataTable.settings[i].oInstance.fnDestroy();
+ s.oInstance.fnDestroy();
break;
}
else
{
- _fnLog( DataTable.settings[i], 0, "Cannot reinitialise DataTable.\n\n"+
- "To retrieve the DataTables object for this table, pass no arguments or see "+
- "the docs for bRetrieve and bDestroy" );
+ _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
return;
}
}
-
+
/* If the element we are initialising has the same ID as a table which was previously
* initialised, but the table nodes don't match (from before) then we destroy the old
* instance by simply deleting it. This is under the assumption that the table has been
* destroyed by other methods. Anyone using non-id selectors will need to do this manually
*/
- if ( DataTable.settings[i].sTableId == this.id )
+ if ( s.sTableId == this.id )
{
- DataTable.settings.splice( i, 1 );
+ allSettings.splice( i, 1 );
break;
}
}
@@ -6417,80 +6232,93 @@
/* Ensure the table has an ID - required for accessibility */
if ( sId === null || sId === "" )
{
- sId = "DataTables_Table_"+(DataTable.ext._oExternConfig.iNextUnique++);
+ sId = "DataTables_Table_"+(DataTable.ext._unique++);
this.id = sId;
}
/* Create the settings object for this table and set some of the default parameters */
var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
- "nTable": this,
- "oApi": _that.oApi,
- "oInit": oInit,
- "sDestroyWidth": $(this).width(),
+ "sDestroyWidth": $this[0].style.width,
"sInstance": sId,
"sTableId": sId
} );
- DataTable.settings.push( oSettings );
+ oSettings.nTable = this;
+ oSettings.oApi = _that.internal;
+ oSettings.oInit = oInit;
+
+ allSettings.push( oSettings );
// Need to add the instance after the instance after the settings object has been added
// to the settings array, so we can self reference the table instance if more than one
- oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
-
- /* Setting up the initialisation object */
- if ( !oInit )
- {
- oInit = {};
- }
+ oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
// Backwards compatibility, before we apply all the defaults
+ _fnCompatOpts( oInit );
+
if ( oInit.oLanguage )
{
_fnLanguageCompat( oInit.oLanguage );
}
- oInit = _fnExtend( $.extend(true, {}, DataTable.defaults), oInit );
+ // If the length menu is given, but the init display length is not, use the length menu
+ if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
+ {
+ oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
+ oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
+ }
+
+ // Apply the defaults and init options to make a single init object will all
+ // options defined from defaults and instance options.
+ oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
+
// Map the initialisation options onto the settings object
- _fnMap( oSettings.oFeatures, oInit, "bPaginate" );
- _fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
- _fnMap( oSettings.oFeatures, oInit, "bFilter" );
- _fnMap( oSettings.oFeatures, oInit, "bSort" );
- _fnMap( oSettings.oFeatures, oInit, "bInfo" );
- _fnMap( oSettings.oFeatures, oInit, "bProcessing" );
- _fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
- _fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
- _fnMap( oSettings.oFeatures, oInit, "bServerSide" );
- _fnMap( oSettings.oFeatures, oInit, "bDeferRender" );
- _fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
- _fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
- _fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
- _fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
- _fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
- _fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
- _fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
- _fnMap( oSettings, oInit, "asStripeClasses" );
- _fnMap( oSettings, oInit, "asStripClasses", "asStripeClasses" ); // legacy
- _fnMap( oSettings, oInit, "fnServerData" );
- _fnMap( oSettings, oInit, "fnFormatNumber" );
- _fnMap( oSettings, oInit, "sServerMethod" );
- _fnMap( oSettings, oInit, "aaSorting" );
- _fnMap( oSettings, oInit, "aaSortingFixed" );
- _fnMap( oSettings, oInit, "aLengthMenu" );
- _fnMap( oSettings, oInit, "sPaginationType" );
- _fnMap( oSettings, oInit, "sAjaxSource" );
- _fnMap( oSettings, oInit, "sAjaxDataProp" );
- _fnMap( oSettings, oInit, "iCookieDuration" );
- _fnMap( oSettings, oInit, "sCookiePrefix" );
- _fnMap( oSettings, oInit, "sDom" );
- _fnMap( oSettings, oInit, "bSortCellsTop" );
- _fnMap( oSettings, oInit, "iTabIndex" );
- _fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
- _fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
- _fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
- _fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
- _fnMap( oSettings, oInit, "fnCookieCallback" );
- _fnMap( oSettings, oInit, "fnStateLoad" );
- _fnMap( oSettings, oInit, "fnStateSave" );
+ _fnMap( oSettings.oFeatures, oInit, [
+ "bPaginate",
+ "bLengthChange",
+ "bFilter",
+ "bSort",
+ "bSortMulti",
+ "bInfo",
+ "bProcessing",
+ "bAutoWidth",
+ "bSortClasses",
+ "bServerSide",
+ "bDeferRender"
+ ] );
+ _fnMap( oSettings, oInit, [
+ "asStripeClasses",
+ "ajax",
+ "fnServerData",
+ "fnFormatNumber",
+ "sServerMethod",
+ "aaSorting",
+ "aaSortingFixed",
+ "aLengthMenu",
+ "sPaginationType",
+ "sAjaxSource",
+ "sAjaxDataProp",
+ "iStateDuration",
+ "sDom",
+ "bSortCellsTop",
+ "iTabIndex",
+ "fnStateLoadCallback",
+ "fnStateSaveCallback",
+ "renderer",
+ "searchDelay",
+ "rowId",
+ [ "iCookieDuration", "iStateDuration" ], // backwards compat
+ [ "oSearch", "oPreviousSearch" ],
+ [ "aoSearchCols", "aoPreSearchCols" ],
+ [ "iDisplayLength", "_iDisplayLength" ],
+ [ "bJQueryUI", "bJUI" ]
+ ] );
+ _fnMap( oSettings.oScroll, oInit, [
+ [ "sScrollX", "sX" ],
+ [ "sScrollXInner", "sXInner" ],
+ [ "sScrollY", "sY" ],
+ [ "bScrollCollapse", "bCollapse" ]
+ ] );
_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
/* Callback functions which are array driven */
@@ -6506,43 +6334,40 @@
_fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
_fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
- if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
- oSettings.oFeatures.bSortClasses )
- {
- /* Enable sort classes for server-side processing. Safe to do it here, since server-side
- * processing must be enabled by the developer
- */
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'server_side_sort_classes' );
- }
- else if ( oSettings.oFeatures.bDeferRender )
- {
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'defer_sort_classes' );
- }
+ oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
+
+ /* Browser support detection */
+ _fnBrowserDetect( oSettings );
+ var oClasses = oSettings.oClasses;
+
+ // @todo Remove in 1.11
if ( oInit.bJQueryUI )
{
- /* Use the JUI classes object for display. You could clone the oStdClasses object if
- * you want to have multiple tables with multiple independent classes
+ /* Use the JUI classes object for display. You could clone the oStdClasses object if
+ * you want to have multiple tables with multiple independent classes
*/
- $.extend( oSettings.oClasses, DataTable.ext.oJUIClasses );
-
- if ( oInit.sDom === DataTable.defaults.sDom && DataTable.defaults.sDom === "lfrtip" )
+ $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
+
+ if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
{
/* Set the DOM to use a layout suitable for jQuery UI's theming */
oSettings.sDom = '<"H"lfr>t<"F"ip>';
}
+
+ if ( ! oSettings.renderer ) {
+ oSettings.renderer = 'jqueryui';
+ }
+ else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
+ oSettings.renderer.header = 'jqueryui';
+ }
}
else
{
- $.extend( oSettings.oClasses, DataTable.ext.oStdClasses );
+ $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
}
- $(this).addClass( oSettings.oClasses.sTable );
+ $this.addClass( oClasses.sTable );
- /* Calculate the scroll bar width and cache it for use later on */
- if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
- {
- oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
- }
if ( oSettings.iInitDisplayStart === undefined )
{
@@ -6551,14 +6376,6 @@
oSettings._iDisplayStart = oInit.iDisplayStart;
}
- /* Must be done after everything which can be overridden by a cookie! */
- if ( oInit.bStateSave )
- {
- oSettings.oFeatures.bStateSave = true;
- _fnLoadState( oSettings, oInit );
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
- }
-
if ( oInit.iDeferLoading !== null )
{
oSettings.bDeferLoading = true;
@@ -6567,31 +6384,32 @@
oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
}
- if ( oInit.aaData !== null )
- {
- bUsePassedData = true;
- }
-
/* Language definitions */
- if ( oInit.oLanguage.sUrl !== "" )
+ var oLanguage = oSettings.oLanguage;
+ $.extend( true, oLanguage, oInit.oLanguage );
+
+ if ( oLanguage.sUrl !== "" )
{
/* Get the language definitions from a file - because this Ajax call makes the language
- * get async to the remainder of this function we use bInitHandedOff to indicate that
+ * get async to the remainder of this function we use bInitHandedOff to indicate that
* _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
*/
- oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
- $.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
- _fnLanguageCompat( json );
- $.extend( true, oSettings.oLanguage, oInit.oLanguage, json );
- _fnInitialise( oSettings );
+ $.ajax( {
+ dataType: 'json',
+ url: oLanguage.sUrl,
+ success: function ( json ) {
+ _fnLanguageCompat( json );
+ _fnCamelToHungarian( defaults.oLanguage, json );
+ $.extend( true, oLanguage, json );
+ _fnInitialise( oSettings );
+ },
+ error: function () {
+ // Error occurred loading language file, continue on as best we can
+ _fnInitialise( oSettings );
+ }
} );
bInitHandedOff = true;
}
- else
- {
- $.extend( true, oSettings.oLanguage, oInit.oLanguage );
- }
-
/*
* Stripes
@@ -6599,33 +6417,19 @@
if ( oInit.asStripeClasses === null )
{
oSettings.asStripeClasses =[
- oSettings.oClasses.sStripeOdd,
- oSettings.oClasses.sStripeEven
+ oClasses.sStripeOdd,
+ oClasses.sStripeEven
];
}
/* Remove row stripe classes if they are already on the table row */
- iLen=oSettings.asStripeClasses.length;
- oSettings.asDestroyStripes = [];
- if (iLen)
- {
- var bStripeRemove = false;
- var anRows = $(this).children('tbody').children('tr:lt(' + iLen + ')');
- for ( i=0 ; i<iLen ; i++ )
- {
- if ( anRows.hasClass( oSettings.asStripeClasses[i] ) )
- {
- bStripeRemove = true;
-
- /* Store the classes which we are about to remove so they can be re-added on destroy */
- oSettings.asDestroyStripes.push( oSettings.asStripeClasses[i] );
- }
- }
-
- if ( bStripeRemove )
- {
- anRows.removeClass( oSettings.asStripeClasses.join(' ') );
- }
+ var stripeClasses = oSettings.asStripeClasses;
+ var rowOne = $this.children('tbody').find('tr').eq(0);
+ if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
+ return rowOne.hasClass(el);
+ } ) ) !== -1 ) {
+ $('tbody tr', this).removeClass( stripeClasses.join(' ') );
+ oSettings.asDestroyStripes = stripeClasses.slice();
}
/*
@@ -6658,16 +6462,6 @@
/* Add the columns */
for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
{
- /* Short cut - use the loop to check if we have column visibility state to restore */
- if ( oInit.saved_aoColumns !== undefined && oInit.saved_aoColumns.length == iLen )
- {
- if ( aoColumnsInit[i] === null )
- {
- aoColumnsInit[i] = {};
- }
- aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
- }
-
_fnAddColumn( oSettings, anThs ? anThs[i] : null );
}
@@ -6676,107 +6470,146 @@
_fnColumnOptions( oSettings, iCol, oDef );
} );
+ /* HTML5 attribute detection - build an mData object automatically if the
+ * attributes are found
+ */
+ if ( rowOne.length ) {
+ var a = function ( cell, name ) {
+ return cell.getAttribute( 'data-'+name ) !== null ? name : null;
+ };
+
+ $( rowOne[0] ).children('th, td').each( function (i, cell) {
+ var col = oSettings.aoColumns[i];
+
+ if ( col.mData === i ) {
+ var sort = a( cell, 'sort' ) || a( cell, 'order' );
+ var filter = a( cell, 'filter' ) || a( cell, 'search' );
+
+ if ( sort !== null || filter !== null ) {
+ col.mData = {
+ _: i+'.display',
+ sort: sort !== null ? i+'.@data-'+sort : undefined,
+ type: sort !== null ? i+'.@data-'+sort : undefined,
+ filter: filter !== null ? i+'.@data-'+filter : undefined
+ };
+
+ _fnColumnOptions( oSettings, i );
+ }
+ }
+ } );
+ }
+
+ var features = oSettings.oFeatures;
+
+ /* Must be done after everything which can be overridden by the state saving! */
+ if ( oInit.bStateSave )
+ {
+ features.bStateSave = true;
+ _fnLoadState( oSettings, oInit );
+ _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
+ }
+
/*
* Sorting
- * Check the aaSorting array
+ * @todo For modularisation (1.11) this needs to do into a sort start up handler
*/
- for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
+
+ // If aaSorting is not defined, then we use the first indicator in asSorting
+ // in case that has been altered, so the default sort reflects that option
+ if ( oInit.aaSorting === undefined )
{
- if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
- {
- oSettings.aaSorting[i][0] = 0;
- }
- var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
-
- /* Add a default sorting index */
- if ( oSettings.aaSorting[i][2] === undefined )
- {
- oSettings.aaSorting[i][2] = 0;
- }
-
- /* If aaSorting is not defined, then we use the first indicator in asSorting */
- if ( oInit.aaSorting === undefined && oSettings.saved_aaSorting === undefined )
+ var sorting = oSettings.aaSorting;
+ for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
{
- oSettings.aaSorting[i][1] = oColumn.asSorting[0];
- }
-
- /* Set the current sorting index based on aoColumns.asSorting */
- for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
- {
- if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
- {
- oSettings.aaSorting[i][2] = j;
- break;
- }
+ sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
}
}
-
+
/* Do a first pass on the sorting classes (allows any size changes to be taken into
* account, and also will apply sorting disabled classes if disabled
*/
_fnSortingClasses( oSettings );
+ if ( features.bSort )
+ {
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
+ if ( oSettings.bSorted ) {
+ var aSort = _fnSortFlatten( oSettings );
+ var sortedColumns = {};
+
+ $.each( aSort, function (i, val) {
+ sortedColumns[ val.src ] = val.dir;
+ } );
+
+ _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
+ _fnSortAria( oSettings );
+ }
+ } );
+ }
+
+ _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
+ if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
+ _fnSortingClasses( oSettings );
+ }
+ }, 'sc' );
+
/*
* Final init
* Cache the header, body and footer as required, creating them if needed
*/
- /* Browser support detection */
- _fnBrowserDetect( oSettings );
-
// Work around for Webkit bug 83867 - store the caption-side before removing from doc
- var captions = $(this).children('caption').each( function () {
- this._captionSide = $(this).css('caption-side');
+ var captions = $this.children('caption').each( function () {
+ this._captionSide = $this.css('caption-side');
} );
- var thead = $(this).children('thead');
+ var thead = $this.children('thead');
if ( thead.length === 0 )
{
- thead = [ document.createElement( 'thead' ) ];
- this.appendChild( thead[0] );
+ thead = $('<thead/>').appendTo(this);
}
oSettings.nTHead = thead[0];
- var tbody = $(this).children('tbody');
+ var tbody = $this.children('tbody');
if ( tbody.length === 0 )
{
- tbody = [ document.createElement( 'tbody' ) ];
- this.appendChild( tbody[0] );
+ tbody = $('<tbody/>').appendTo(this);
}
oSettings.nTBody = tbody[0];
- oSettings.nTBody.setAttribute( "role", "alert" );
- oSettings.nTBody.setAttribute( "aria-live", "polite" );
- oSettings.nTBody.setAttribute( "aria-relevant", "all" );
- var tfoot = $(this).children('tfoot');
+ var tfoot = $this.children('tfoot');
if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
{
// If we are a scrolling table, and no footer has been given, then we need to create
// a tfoot element for the caption element to be appended to
- tfoot = [ document.createElement( 'tfoot' ) ];
- this.appendChild( tfoot[0] );
+ tfoot = $('<tfoot/>').appendTo(this);
}
- if ( tfoot.length > 0 )
- {
+ if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
+ $this.addClass( oClasses.sNoFooter );
+ }
+ else if ( tfoot.length > 0 ) {
oSettings.nTFoot = tfoot[0];
_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
}
/* Check if there is data passing into the constructor */
- if ( bUsePassedData )
+ if ( oInit.aaData )
{
for ( i=0 ; i<oInit.aaData.length ; i++ )
{
_fnAddData( oSettings, oInit.aaData[ i ] );
}
}
- else
+ else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
{
- /* Grab the data from the page */
- _fnGatherData( oSettings );
+ /* Grab the data from the page - only do this when deferred loading or no Ajax
+ * source since there is no point in reading the DOM data if we are then going
+ * to replace it with Ajax data
+ */
+ _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
}
/* Copy the data index array */
@@ -6800,660 +6633,2754 @@
/**
- * Provide a common method for plug-ins to check the version of DataTables being used, in order
- * to ensure compatibility.
- * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
- * formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to the required
- * version, or false if this version of DataTales is not suitable
- * @static
- * @dtopt API-Static
+ * Computed structure of the DataTables API, defined by the options passed to
+ * `DataTable.Api.register()` when building the API.
*
- * @example
- * alert( $.fn.dataTable.fnVersionCheck( '1.9.0' ) );
+ * The structure is built in order to speed creation and extension of the Api
+ * objects since the extensions are effectively pre-parsed.
+ *
+ * The array is an array of objects with the following structure, where this
+ * base array represents the Api prototype base:
+ *
+ * [
+ * {
+ * name: 'data' -- string - Property name
+ * val: function () {}, -- function - Api method (or undefined if just an object
+ * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
+ * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
+ * },
+ * {
+ * name: 'row'
+ * val: {},
+ * methodExt: [ ... ],
+ * propExt: [
+ * {
+ * name: 'data'
+ * val: function () {},
+ * methodExt: [ ... ],
+ * propExt: [ ... ]
+ * },
+ * ...
+ * ]
+ * }
+ * ]
+ *
+ * @type {Array}
+ * @ignore
*/
- DataTable.fnVersionCheck = function( sVersion )
+ var __apiStruct = [];
+
+
+ /**
+ * `Array.prototype` reference.
+ *
+ * @type object
+ * @ignore
+ */
+ var __arrayProto = Array.prototype;
+
+
+ /**
+ * Abstraction for `context` parameter of the `Api` constructor to allow it to
+ * take several different forms for ease of use.
+ *
+ * Each of the input parameter types will be converted to a DataTables settings
+ * object where possible.
+ *
+ * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
+ * of:
+ *
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
+ * with be found and used.
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
+ * * `object` - DataTables settings object
+ * * `DataTables.Api` - API instance
+ * @return {array|null} Matching DataTables settings objects. `null` or
+ * `undefined` is returned if no matching DataTable is found.
+ * @ignore
+ */
+ var _toSettings = function ( mixed )
{
- /* This is cheap, but effective */
- var fnZPad = function (Zpad, count)
- {
- while(Zpad.length < count) {
- Zpad += '0';
+ var idx, jq;
+ var settings = DataTable.settings;
+ var tables = $.map( settings, function (el, i) {
+ return el.nTable;
+ } );
+
+ if ( ! mixed ) {
+ return [];
+ }
+ else if ( mixed.nTable && mixed.oApi ) {
+ // DataTables settings object
+ return [ mixed ];
+ }
+ else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
+ // Table node
+ idx = $.inArray( mixed, tables );
+ return idx !== -1 ? [ settings[idx] ] : null;
+ }
+ else if ( mixed && typeof mixed.settings === 'function' ) {
+ return mixed.settings().toArray();
+ }
+ else if ( typeof mixed === 'string' ) {
+ // jQuery selector
+ jq = $(mixed);
+ }
+ else if ( mixed instanceof $ ) {
+ // jQuery object (also DataTables instance)
+ jq = mixed;
+ }
+
+ if ( jq ) {
+ return jq.map( function(i) {
+ idx = $.inArray( this, tables );
+ return idx !== -1 ? settings[idx] : null;
+ } ).toArray();
+ }
+ };
+
+
+ /**
+ * DataTables API class - used to control and interface with one or more
+ * DataTables enhanced tables.
+ *
+ * The API class is heavily based on jQuery, presenting a chainable interface
+ * that you can use to interact with tables. Each instance of the API class has
+ * a "context" - i.e. the tables that it will operate on. This could be a single
+ * table, all tables on a page or a sub-set thereof.
+ *
+ * Additionally the API is designed to allow you to easily work with the data in
+ * the tables, retrieving and manipulating it as required. This is done by
+ * presenting the API class as an array like interface. The contents of the
+ * array depend upon the actions requested by each method (for example
+ * `rows().nodes()` will return an array of nodes, while `rows().data()` will
+ * return an array of objects or arrays depending upon your table's
+ * configuration). The API object has a number of array like methods (`push`,
+ * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
+ * `unique` etc) to assist your working with the data held in a table.
+ *
+ * Most methods (those which return an Api instance) are chainable, which means
+ * the return from a method call also has all of the methods available that the
+ * top level object had. For example, these two calls are equivalent:
+ *
+ * // Not chained
+ * api.row.add( {...} );
+ * api.draw();
+ *
+ * // Chained
+ * api.row.add( {...} ).draw();
+ *
+ * @class DataTable.Api
+ * @param {array|object|string|jQuery} context DataTable identifier. This is
+ * used to define which DataTables enhanced tables this API will operate on.
+ * Can be one of:
+ *
+ * * `string` - jQuery selector. Any DataTables' matching the given selector
+ * with be found and used.
+ * * `node` - `TABLE` node which has already been formed into a DataTable.
+ * * `jQuery` - A jQuery object of `TABLE` nodes.
+ * * `object` - DataTables settings object
+ * @param {array} [data] Data to initialise the Api instance with.
+ *
+ * @example
+ * // Direct initialisation during DataTables construction
+ * var api = $('#example').DataTable();
+ *
+ * @example
+ * // Initialisation using a DataTables jQuery object
+ * var api = $('#example').dataTable().api();
+ *
+ * @example
+ * // Initialisation as a constructor
+ * var api = new $.fn.DataTable.Api( 'table.dataTable' );
+ */
+ _Api = function ( context, data )
+ {
+ if ( ! (this instanceof _Api) ) {
+ return new _Api( context, data );
+ }
+
+ var settings = [];
+ var ctxSettings = function ( o ) {
+ var a = _toSettings( o );
+ if ( a ) {
+ settings = settings.concat( a );
}
- return Zpad;
};
- var aThis = DataTable.ext.sVersion.split('.');
- var aThat = sVersion.split('.');
- var sThis = '', sThat = '';
-
- for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
+
+ if ( $.isArray( context ) ) {
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
+ ctxSettings( context[i] );
+ }
+ }
+ else {
+ ctxSettings( context );
+ }
+
+ // Remove duplicates
+ this.context = _unique( settings );
+
+ // Initial data
+ if ( data ) {
+ $.merge( this, data );
+ }
+
+ // selector
+ this.selector = {
+ rows: null,
+ cols: null,
+ opts: null
+ };
+
+ _Api.extend( this, this, __apiStruct );
+ };
+
+ DataTable.Api = _Api;
+
+ // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
+ // isPlainObject.
+ $.extend( _Api.prototype, {
+ any: function ()
+ {
+ return this.count() !== 0;
+ },
+
+
+ concat: __arrayProto.concat,
+
+
+ context: [], // array of table settings objects
+
+
+ count: function ()
+ {
+ return this.flatten().length;
+ },
+
+
+ each: function ( fn )
+ {
+ for ( var i=0, ien=this.length ; i<ien; i++ ) {
+ fn.call( this, this[i], i, this );
+ }
+
+ return this;
+ },
+
+
+ eq: function ( idx )
+ {
+ var ctx = this.context;
+
+ return ctx.length > idx ?
+ new _Api( ctx[idx], this[idx] ) :
+ null;
+ },
+
+
+ filter: function ( fn )
+ {
+ var a = [];
+
+ if ( __arrayProto.filter ) {
+ a = __arrayProto.filter.call( this, fn, this );
+ }
+ else {
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
+ if ( fn.call( this, this[i], i, this ) ) {
+ a.push( this[i] );
+ }
+ }
+ }
+
+ return new _Api( this.context, a );
+ },
+
+
+ flatten: function ()
{
- sThis += fnZPad( aThis[i], 3 );
- sThat += fnZPad( aThat[i], 3 );
+ var a = [];
+ return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
+ },
+
+
+ join: __arrayProto.join,
+
+
+ indexOf: __arrayProto.indexOf || function (obj, start)
+ {
+ for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
+ if ( this[i] === obj ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ iterator: function ( flatten, type, fn, alwaysNew ) {
+ var
+ a = [], ret,
+ i, ien, j, jen,
+ context = this.context,
+ rows, items, item,
+ selector = this.selector;
+
+ // Argument shifting
+ if ( typeof flatten === 'string' ) {
+ alwaysNew = fn;
+ fn = type;
+ type = flatten;
+ flatten = false;
+ }
+
+ for ( i=0, ien=context.length ; i<ien ; i++ ) {
+ var apiInst = new _Api( context[i] );
+
+ if ( type === 'table' ) {
+ ret = fn.call( apiInst, context[i], i );
+
+ if ( ret !== undefined ) {
+ a.push( ret );
+ }
+ }
+ else if ( type === 'columns' || type === 'rows' ) {
+ // this has same length as context - one entry for each table
+ ret = fn.call( apiInst, context[i], this[i], i );
+
+ if ( ret !== undefined ) {
+ a.push( ret );
+ }
+ }
+ else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
+ // columns and rows share the same structure.
+ // 'this' is an array of column indexes for each context
+ items = this[i];
+
+ if ( type === 'column-rows' ) {
+ rows = _selector_row_indexes( context[i], selector.opts );
+ }
+
+ for ( j=0, jen=items.length ; j<jen ; j++ ) {
+ item = items[j];
+
+ if ( type === 'cell' ) {
+ ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
+ }
+ else {
+ ret = fn.call( apiInst, context[i], item, i, j, rows );
+ }
+
+ if ( ret !== undefined ) {
+ a.push( ret );
+ }
+ }
+ }
+ }
+
+ if ( a.length || alwaysNew ) {
+ var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
+ var apiSelector = api.selector;
+ apiSelector.rows = selector.rows;
+ apiSelector.cols = selector.cols;
+ apiSelector.opts = selector.opts;
+ return api;
+ }
+ return this;
+ },
+
+
+ lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
+ {
+ // Bit cheeky...
+ return this.indexOf.apply( this.toArray.reverse(), arguments );
+ },
+
+
+ length: 0,
+
+
+ map: function ( fn )
+ {
+ var a = [];
+
+ if ( __arrayProto.map ) {
+ a = __arrayProto.map.call( this, fn, this );
+ }
+ else {
+ // Compatibility for browsers without EMCA-252-5 (JS 1.6)
+ for ( var i=0, ien=this.length ; i<ien ; i++ ) {
+ a.push( fn.call( this, this[i], i ) );
+ }
+ }
+
+ return new _Api( this.context, a );
+ },
+
+
+ pluck: function ( prop )
+ {
+ return this.map( function ( el ) {
+ return el[ prop ];
+ } );
+ },
+
+ pop: __arrayProto.pop,
+
+
+ push: __arrayProto.push,
+
+
+ // Does not return an API instance
+ reduce: __arrayProto.reduce || function ( fn, init )
+ {
+ return _fnReduce( this, fn, init, 0, this.length, 1 );
+ },
+
+
+ reduceRight: __arrayProto.reduceRight || function ( fn, init )
+ {
+ return _fnReduce( this, fn, init, this.length-1, -1, -1 );
+ },
+
+
+ reverse: __arrayProto.reverse,
+
+
+ // Object with rows, columns and opts
+ selector: null,
+
+
+ shift: __arrayProto.shift,
+
+
+ sort: __arrayProto.sort, // ? name - order?
+
+
+ splice: __arrayProto.splice,
+
+
+ toArray: function ()
+ {
+ return __arrayProto.slice.call( this );
+ },
+
+
+ to$: function ()
+ {
+ return $( this );
+ },
+
+
+ toJQuery: function ()
+ {
+ return $( this );
+ },
+
+
+ unique: function ()
+ {
+ return new _Api( this.context, _unique(this) );
+ },
+
+
+ unshift: __arrayProto.unshift
+ } );
+
+
+ _Api.extend = function ( scope, obj, ext )
+ {
+ // Only extend API instances and static properties of the API
+ if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
+ return;
+ }
+
+ var
+ i, ien,
+ j, jen,
+ struct, inner,
+ methodScoping = function ( scope, fn, struc ) {
+ return function () {
+ var ret = fn.apply( scope, arguments );
+
+ // Method extension
+ _Api.extend( ret, ret, struc.methodExt );
+ return ret;
+ };
+ };
+
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
+ struct = ext[i];
+
+ // Value
+ obj[ struct.name ] = typeof struct.val === 'function' ?
+ methodScoping( scope, struct.val, struct ) :
+ $.isPlainObject( struct.val ) ?
+ {} :
+ struct.val;
+
+ obj[ struct.name ].__dt_wrapper = true;
+
+ // Property extension
+ _Api.extend( scope, obj[ struct.name ], struct.propExt );
}
-
- return parseInt(sThis, 10) >= parseInt(sThat, 10);
+ };
+
+
+ // @todo - Is there need for an augment function?
+ // _Api.augment = function ( inst, name )
+ // {
+ // // Find src object in the structure from the name
+ // var parts = name.split('.');
+
+ // _Api.extend( inst, obj );
+ // };
+
+
+ // [
+ // {
+ // name: 'data' -- string - Property name
+ // val: function () {}, -- function - Api method (or undefined if just an object
+ // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
+ // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
+ // },
+ // {
+ // name: 'row'
+ // val: {},
+ // methodExt: [ ... ],
+ // propExt: [
+ // {
+ // name: 'data'
+ // val: function () {},
+ // methodExt: [ ... ],
+ // propExt: [ ... ]
+ // },
+ // ...
+ // ]
+ // }
+ // ]
+
+ _Api.register = _api_register = function ( name, val )
+ {
+ if ( $.isArray( name ) ) {
+ for ( var j=0, jen=name.length ; j<jen ; j++ ) {
+ _Api.register( name[j], val );
+ }
+ return;
+ }
+
+ var
+ i, ien,
+ heir = name.split('.'),
+ struct = __apiStruct,
+ key, method;
+
+ var find = function ( src, name ) {
+ for ( var i=0, ien=src.length ; i<ien ; i++ ) {
+ if ( src[i].name === name ) {
+ return src[i];
+ }
+ }
+ return null;
+ };
+
+ for ( i=0, ien=heir.length ; i<ien ; i++ ) {
+ method = heir[i].indexOf('()') !== -1;
+ key = method ?
+ heir[i].replace('()', '') :
+ heir[i];
+
+ var src = find( struct, key );
+ if ( ! src ) {
+ src = {
+ name: key,
+ val: {},
+ methodExt: [],
+ propExt: []
+ };
+ struct.push( src );
+ }
+
+ if ( i === ien-1 ) {
+ src.val = val;
+ }
+ else {
+ struct = method ?
+ src.methodExt :
+ src.propExt;
+ }
+ }
+ };
+
+
+ _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
+ _Api.register( pluralName, val );
+
+ _Api.register( singularName, function () {
+ var ret = val.apply( this, arguments );
+
+ if ( ret === this ) {
+ // Returned item is the API instance that was passed in, return it
+ return this;
+ }
+ else if ( ret instanceof _Api ) {
+ // New API instance returned, want the value from the first item
+ // in the returned array for the singular result.
+ return ret.length ?
+ $.isArray( ret[0] ) ?
+ new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
+ ret[0] :
+ undefined;
+ }
+
+ // Non-API return - just fire it back
+ return ret;
+ } );
};
/**
- * Check if a TABLE node is a DataTable table already or not.
- * @param {node} nTable The TABLE node to check if it is a DataTable or not (note that other
- * node types can be passed in, but will always return false).
- * @returns {boolean} true the table given is a DataTable, or false otherwise
+ * Selector for HTML tables. Apply the given selector to the give array of
+ * DataTables settings objects.
+ *
+ * @param {string|integer} [selector] jQuery selector string or integer
+ * @param {array} Array of DataTables settings objects to be filtered
+ * @return {array}
+ * @ignore
+ */
+ var __table_selector = function ( selector, a )
+ {
+ // Integer is used to pick out a table by index
+ if ( typeof selector === 'number' ) {
+ return [ a[ selector ] ];
+ }
+
+ // Perform a jQuery selector on the table nodes
+ var nodes = $.map( a, function (el, i) {
+ return el.nTable;
+ } );
+
+ return $(nodes)
+ .filter( selector )
+ .map( function (i) {
+ // Need to translate back from the table node to the settings
+ var idx = $.inArray( this, nodes );
+ return a[ idx ];
+ } )
+ .toArray();
+ };
+
+
+
+ /**
+ * Context selector for the API's context (i.e. the tables the API instance
+ * refers to.
+ *
+ * @name DataTable.Api#tables
+ * @param {string|integer} [selector] Selector to pick which tables the iterator
+ * should operate on. If not given, all tables in the current context are
+ * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
+ * select multiple tables or as an integer to select a single table.
+ * @returns {DataTable.Api} Returns a new API instance if a selector is given.
+ */
+ _api_register( 'tables()', function ( selector ) {
+ // A new instance is created if there was a selector specified
+ return selector ?
+ new _Api( __table_selector( selector, this.context ) ) :
+ this;
+ } );
+
+
+ _api_register( 'table()', function ( selector ) {
+ var tables = this.tables( selector );
+ var ctx = tables.context;
+
+ // Truncate to the first matched table
+ return ctx.length ?
+ new _Api( ctx[0] ) :
+ tables;
+ } );
+
+
+ _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTable;
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'tables().body()', 'table().body()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTBody;
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'tables().header()', 'table().header()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTHead;
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTFoot;
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
+ return this.iterator( 'table', function ( ctx ) {
+ return ctx.nTableWrapper;
+ }, 1 );
+ } );
+
+
+
+ /**
+ * Redraw the tables in the current context.
+ */
+ _api_register( 'draw()', function ( paging ) {
+ return this.iterator( 'table', function ( settings ) {
+ if ( paging === 'page' ) {
+ _fnDraw( settings );
+ }
+ else {
+ if ( typeof paging === 'string' ) {
+ paging = paging === 'full-hold' ?
+ false :
+ true;
+ }
+
+ _fnReDraw( settings, paging===false );
+ }
+ } );
+ } );
+
+
+
+ /**
+ * Get the current page index.
+ *
+ * @return {integer} Current page index (zero based)
+ *//**
+ * Set the current page.
+ *
+ * Note that if you attempt to show a page which does not exist, DataTables will
+ * not throw an error, but rather reset the paging.
+ *
+ * @param {integer|string} action The paging action to take. This can be one of:
+ * * `integer` - The page index to jump to
+ * * `string` - An action to take:
+ * * `first` - Jump to first page.
+ * * `next` - Jump to the next page
+ * * `previous` - Jump to previous page
+ * * `last` - Jump to the last page.
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'page()', function ( action ) {
+ if ( action === undefined ) {
+ return this.page.info().page; // not an expensive call
+ }
+
+ // else, have an action to take on all tables
+ return this.iterator( 'table', function ( settings ) {
+ _fnPageChange( settings, action );
+ } );
+ } );
+
+
+ /**
+ * Paging information for the first table in the current context.
+ *
+ * If you require paging information for another table, use the `table()` method
+ * with a suitable selector.
+ *
+ * @return {object} Object with the following properties set:
+ * * `page` - Current page index (zero based - i.e. the first page is `0`)
+ * * `pages` - Total number of pages
+ * * `start` - Display index for the first record shown on the current page
+ * * `end` - Display index for the last record shown on the current page
+ * * `length` - Display length (number of records). Note that generally `start
+ * + length = end`, but this is not always true, for example if there are
+ * only 2 records to show on the final page, with a length of 10.
+ * * `recordsTotal` - Full data set length
+ * * `recordsDisplay` - Data set length once the current filtering criterion
+ * are applied.
+ */
+ _api_register( 'page.info()', function ( action ) {
+ if ( this.context.length === 0 ) {
+ return undefined;
+ }
+
+ var
+ settings = this.context[0],
+ start = settings._iDisplayStart,
+ len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
+ visRecords = settings.fnRecordsDisplay(),
+ all = len === -1;
+
+ return {
+ "page": all ? 0 : Math.floor( start / len ),
+ "pages": all ? 1 : Math.ceil( visRecords / len ),
+ "start": start,
+ "end": settings.fnDisplayEnd(),
+ "length": len,
+ "recordsTotal": settings.fnRecordsTotal(),
+ "recordsDisplay": visRecords,
+ "serverSide": _fnDataSource( settings ) === 'ssp'
+ };
+ } );
+
+
+ /**
+ * Get the current page length.
+ *
+ * @return {integer} Current page length. Note `-1` indicates that all records
+ * are to be shown.
+ *//**
+ * Set the current page length.
+ *
+ * @param {integer} Page length to set. Use `-1` to show all records.
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'page.len()', function ( len ) {
+ // Note that we can't call this function 'length()' because `length`
+ // is a Javascript property of functions which defines how many arguments
+ // the function expects.
+ if ( len === undefined ) {
+ return this.context.length !== 0 ?
+ this.context[0]._iDisplayLength :
+ undefined;
+ }
+
+ // else, set the page length
+ return this.iterator( 'table', function ( settings ) {
+ _fnLengthChange( settings, len );
+ } );
+ } );
+
+
+
+ var __reload = function ( settings, holdPosition, callback ) {
+ // Use the draw event to trigger a callback
+ if ( callback ) {
+ var api = new _Api( settings );
+
+ api.one( 'draw', function () {
+ callback( api.ajax.json() );
+ } );
+ }
+
+ if ( _fnDataSource( settings ) == 'ssp' ) {
+ _fnReDraw( settings, holdPosition );
+ }
+ else {
+ _fnProcessingDisplay( settings, true );
+
+ // Cancel an existing request
+ var xhr = settings.jqXHR;
+ if ( xhr && xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+
+ // Trigger xhr
+ _fnBuildAjax( settings, [], function( json ) {
+ _fnClearTable( settings );
+
+ var data = _fnAjaxDataSrc( settings, json );
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ _fnAddData( settings, data[i] );
+ }
+
+ _fnReDraw( settings, holdPosition );
+ _fnProcessingDisplay( settings, false );
+ } );
+ }
+ };
+
+
+ /**
+ * Get the JSON response from the last Ajax request that DataTables made to the
+ * server. Note that this returns the JSON from the first table in the current
+ * context.
+ *
+ * @return {object} JSON received from the server.
+ */
+ _api_register( 'ajax.json()', function () {
+ var ctx = this.context;
+
+ if ( ctx.length > 0 ) {
+ return ctx[0].json;
+ }
+
+ // else return undefined;
+ } );
+
+
+ /**
+ * Get the data submitted in the last Ajax request
+ */
+ _api_register( 'ajax.params()', function () {
+ var ctx = this.context;
+
+ if ( ctx.length > 0 ) {
+ return ctx[0].oAjaxData;
+ }
+
+ // else return undefined;
+ } );
+
+
+ /**
+ * Reload tables from the Ajax data source. Note that this function will
+ * automatically re-draw the table when the remote data has been loaded.
+ *
+ * @param {boolean} [reset=true] Reset (default) or hold the current paging
+ * position. A full re-sort and re-filter is performed when this method is
+ * called, which is why the pagination reset is the default action.
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
+ return this.iterator( 'table', function (settings) {
+ __reload( settings, resetPaging===false, callback );
+ } );
+ } );
+
+
+ /**
+ * Get the current Ajax URL. Note that this returns the URL from the first
+ * table in the current context.
+ *
+ * @return {string} Current Ajax source URL
+ *//**
+ * Set the Ajax URL. Note that this will set the URL for all tables in the
+ * current context.
+ *
+ * @param {string} url URL to set.
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'ajax.url()', function ( url ) {
+ var ctx = this.context;
+
+ if ( url === undefined ) {
+ // get
+ if ( ctx.length === 0 ) {
+ return undefined;
+ }
+ ctx = ctx[0];
+
+ return ctx.ajax ?
+ $.isPlainObject( ctx.ajax ) ?
+ ctx.ajax.url :
+ ctx.ajax :
+ ctx.sAjaxSource;
+ }
+
+ // set
+ return this.iterator( 'table', function ( settings ) {
+ if ( $.isPlainObject( settings.ajax ) ) {
+ settings.ajax.url = url;
+ }
+ else {
+ settings.ajax = url;
+ }
+ // No need to consider sAjaxSource here since DataTables gives priority
+ // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
+ // value of `sAjaxSource` redundant.
+ } );
+ } );
+
+
+ /**
+ * Load data from the newly set Ajax URL. Note that this method is only
+ * available when `ajax.url()` is used to set a URL. Additionally, this method
+ * has the same effect as calling `ajax.reload()` but is provided for
+ * convenience when setting a new URL. Like `ajax.reload()` it will
+ * automatically redraw the table once the remote data has been loaded.
+ *
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
+ // Same as a reload, but makes sense to present it for easy access after a
+ // url change
+ return this.iterator( 'table', function ( ctx ) {
+ __reload( ctx, resetPaging===false, callback );
+ } );
+ } );
+
+
+
+
+ var _selector_run = function ( type, selector, selectFn, settings, opts )
+ {
+ var
+ out = [], res,
+ a, i, ien, j, jen,
+ selectorType = typeof selector;
+
+ // Can't just check for isArray here, as an API or jQuery instance might be
+ // given with their array like look
+ if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
+ selector = [ selector ];
+ }
+
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
+ a = selector[i] && selector[i].split ?
+ selector[i].split(',') :
+ [ selector[i] ];
+
+ for ( j=0, jen=a.length ; j<jen ; j++ ) {
+ res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
+
+ if ( res && res.length ) {
+ out = out.concat( res );
+ }
+ }
+ }
+
+ // selector extensions
+ var ext = _ext.selector[ type ];
+ if ( ext.length ) {
+ for ( i=0, ien=ext.length ; i<ien ; i++ ) {
+ out = ext[i]( settings, opts, out );
+ }
+ }
+
+ return _unique( out );
+ };
+
+
+ var _selector_opts = function ( opts )
+ {
+ if ( ! opts ) {
+ opts = {};
+ }
+
+ // Backwards compatibility for 1.9- which used the terminology filter rather
+ // than search
+ if ( opts.filter && opts.search === undefined ) {
+ opts.search = opts.filter;
+ }
+
+ return $.extend( {
+ search: 'none',
+ order: 'current',
+ page: 'all'
+ }, opts );
+ };
+
+
+ var _selector_first = function ( inst )
+ {
+ // Reduce the API instance to the first item found
+ for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
+ if ( inst[i].length > 0 ) {
+ // Assign the first element to the first item in the instance
+ // and truncate the instance and context
+ inst[0] = inst[i];
+ inst[0].length = 1;
+ inst.length = 1;
+ inst.context = [ inst.context[i] ];
+
+ return inst;
+ }
+ }
+
+ // Not found - return an empty instance
+ inst.length = 0;
+ return inst;
+ };
+
+
+ var _selector_row_indexes = function ( settings, opts )
+ {
+ var
+ i, ien, tmp, a=[],
+ displayFiltered = settings.aiDisplay,
+ displayMaster = settings.aiDisplayMaster;
+
+ var
+ search = opts.search, // none, applied, removed
+ order = opts.order, // applied, current, index (original - compatibility with 1.9)
+ page = opts.page; // all, current
+
+ if ( _fnDataSource( settings ) == 'ssp' ) {
+ // In server-side processing mode, most options are irrelevant since
+ // rows not shown don't exist and the index order is the applied order
+ // Removed is a special case - for consistency just return an empty
+ // array
+ return search === 'removed' ?
+ [] :
+ _range( 0, displayMaster.length );
+ }
+ else if ( page == 'current' ) {
+ // Current page implies that order=current and fitler=applied, since it is
+ // fairly senseless otherwise, regardless of what order and search actually
+ // are
+ for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
+ a.push( displayFiltered[i] );
+ }
+ }
+ else if ( order == 'current' || order == 'applied' ) {
+ a = search == 'none' ?
+ displayMaster.slice() : // no search
+ search == 'applied' ?
+ displayFiltered.slice() : // applied search
+ $.map( displayMaster, function (el, i) { // removed search
+ return $.inArray( el, displayFiltered ) === -1 ? el : null;
+ } );
+ }
+ else if ( order == 'index' || order == 'original' ) {
+ for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ if ( search == 'none' ) {
+ a.push( i );
+ }
+ else { // applied | removed
+ tmp = $.inArray( i, displayFiltered );
+
+ if ((tmp === -1 && search == 'removed') ||
+ (tmp >= 0 && search == 'applied') )
+ {
+ a.push( i );
+ }
+ }
+ }
+ }
+
+ return a;
+ };
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Rows
+ *
+ * {} - no selector - use all available rows
+ * {integer} - row aoData index
+ * {node} - TR node
+ * {string} - jQuery selector to apply to the TR elements
+ * {array} - jQuery array of nodes, or simply an array of TR nodes
+ *
+ */
+
+
+ var __row_selector = function ( settings, selector, opts )
+ {
+ var run = function ( sel ) {
+ var selInt = _intVal( sel );
+ var i, ien;
+
+ // Short cut - selector is a number and no options provided (default is
+ // all records, so no need to check if the index is in there, since it
+ // must be - dev error if the index doesn't exist).
+ if ( selInt !== null && ! opts ) {
+ return [ selInt ];
+ }
+
+ var rows = _selector_row_indexes( settings, opts );
+
+ if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
+ // Selector - integer
+ return [ selInt ];
+ }
+ else if ( ! sel ) {
+ // Selector - none
+ return rows;
+ }
+
+ // Selector - function
+ if ( typeof sel === 'function' ) {
+ return $.map( rows, function (idx) {
+ var row = settings.aoData[ idx ];
+ return sel( idx, row._aData, row.nTr ) ? idx : null;
+ } );
+ }
+
+ // Get nodes in the order from the `rows` array with null values removed
+ var nodes = _removeEmpty(
+ _pluck_order( settings.aoData, rows, 'nTr' )
+ );
+
+ // Selector - node
+ if ( sel.nodeName ) {
+ if ( sel._DT_RowIndex !== undefined ) {
+ return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
+ }
+ else if ( sel._DT_CellIndex ) {
+ return [ sel._DT_CellIndex.row ];
+ }
+ else {
+ var host = $(sel).closest('*[data-dt-row]');
+ return host.length ?
+ [ host.data('dt-row') ] :
+ [];
+ }
+ }
+
+ // ID selector. Want to always be able to select rows by id, regardless
+ // of if the tr element has been created or not, so can't rely upon
+ // jQuery here - hence a custom implementation. This does not match
+ // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
+ // but to select it using a CSS selector engine (like Sizzle or
+ // querySelect) it would need to need to be escaped for some characters.
+ // DataTables simplifies this for row selectors since you can select
+ // only a row. A # indicates an id any anything that follows is the id -
+ // unescaped.
+ if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
+ // get row index from id
+ var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
+ if ( rowObj !== undefined ) {
+ return [ rowObj.idx ];
+ }
+
+ // need to fall through to jQuery in case there is DOM id that
+ // matches
+ }
+
+ // Selector - jQuery selector string, array of nodes or jQuery object/
+ // As jQuery's .filter() allows jQuery objects to be passed in filter,
+ // it also allows arrays, so this will cope with all three options
+ return $(nodes)
+ .filter( sel )
+ .map( function () {
+ return this._DT_RowIndex;
+ } )
+ .toArray();
+ };
+
+ return _selector_run( 'row', selector, run, settings, opts );
+ };
+
+
+ _api_register( 'rows()', function ( selector, opts ) {
+ // argument shifting
+ if ( selector === undefined ) {
+ selector = '';
+ }
+ else if ( $.isPlainObject( selector ) ) {
+ opts = selector;
+ selector = '';
+ }
+
+ opts = _selector_opts( opts );
+
+ var inst = this.iterator( 'table', function ( settings ) {
+ return __row_selector( settings, selector, opts );
+ }, 1 );
+
+ // Want argument shifting here and in __row_selector?
+ inst.selector.rows = selector;
+ inst.selector.opts = opts;
+
+ return inst;
+ } );
+
+ _api_register( 'rows().nodes()', function () {
+ return this.iterator( 'row', function ( settings, row ) {
+ return settings.aoData[ row ].nTr || undefined;
+ }, 1 );
+ } );
+
+ _api_register( 'rows().data()', function () {
+ return this.iterator( true, 'rows', function ( settings, rows ) {
+ return _pluck_order( settings.aoData, rows, '_aData' );
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
+ return this.iterator( 'row', function ( settings, row ) {
+ var r = settings.aoData[ row ];
+ return type === 'search' ? r._aFilterData : r._aSortData;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
+ return this.iterator( 'row', function ( settings, row ) {
+ _fnInvalidate( settings, row, src );
+ } );
+ } );
+
+ _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
+ return this.iterator( 'row', function ( settings, row ) {
+ return row;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
+ var a = [];
+ var context = this.context;
+
+ // `iterator` will drop undefined values, but in this case we want them
+ for ( var i=0, ien=context.length ; i<ien ; i++ ) {
+ for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
+ var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
+ a.push( (hash === true ? '#' : '' )+ id );
+ }
+ }
+
+ return new _Api( context, a );
+ } );
+
+ _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
+ var that = this;
+
+ this.iterator( 'row', function ( settings, row, thatIdx ) {
+ var data = settings.aoData;
+ var rowData = data[ row ];
+ var i, ien, j, jen;
+ var loopRow, loopCells;
+
+ data.splice( row, 1 );
+
+ // Update the cached indexes
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
+ loopRow = data[i];
+ loopCells = loopRow.anCells;
+
+ // Rows
+ if ( loopRow.nTr !== null ) {
+ loopRow.nTr._DT_RowIndex = i;
+ }
+
+ // Cells
+ if ( loopCells !== null ) {
+ for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
+ loopCells[j]._DT_CellIndex.row = i;
+ }
+ }
+ }
+
+ // Delete from the display arrays
+ _fnDeleteIndex( settings.aiDisplayMaster, row );
+ _fnDeleteIndex( settings.aiDisplay, row );
+ _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
+
+ // Check for an 'overflow' they case for displaying the table
+ _fnLengthOverflow( settings );
+
+ // Remove the row's ID reference if there is one
+ var id = settings.rowIdFn( rowData._aData );
+ if ( id !== undefined ) {
+ delete settings.aIds[ id ];
+ }
+ } );
+
+ this.iterator( 'table', function ( settings ) {
+ for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
+ settings.aoData[i].idx = i;
+ }
+ } );
+
+ return this;
+ } );
+
+
+ _api_register( 'rows.add()', function ( rows ) {
+ var newRows = this.iterator( 'table', function ( settings ) {
+ var row, i, ien;
+ var out = [];
+
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
+ row = rows[i];
+
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
+ out.push( _fnAddTr( settings, row )[0] );
+ }
+ else {
+ out.push( _fnAddData( settings, row ) );
+ }
+ }
+
+ return out;
+ }, 1 );
+
+ // Return an Api.rows() extended instance, so rows().nodes() etc can be used
+ var modRows = this.rows( -1 );
+ modRows.pop();
+ $.merge( modRows, newRows );
+
+ return modRows;
+ } );
+
+
+
+
+
+ /**
+ *
+ */
+ _api_register( 'row()', function ( selector, opts ) {
+ return _selector_first( this.rows( selector, opts ) );
+ } );
+
+
+ _api_register( 'row().data()', function ( data ) {
+ var ctx = this.context;
+
+ if ( data === undefined ) {
+ // Get
+ return ctx.length && this.length ?
+ ctx[0].aoData[ this[0] ]._aData :
+ undefined;
+ }
+
+ // Set
+ ctx[0].aoData[ this[0] ]._aData = data;
+
+ // Automatically invalidate
+ _fnInvalidate( ctx[0], this[0], 'data' );
+
+ return this;
+ } );
+
+
+ _api_register( 'row().node()', function () {
+ var ctx = this.context;
+
+ return ctx.length && this.length ?
+ ctx[0].aoData[ this[0] ].nTr || null :
+ null;
+ } );
+
+
+ _api_register( 'row.add()', function ( row ) {
+ // Allow a jQuery object to be passed in - only a single row is added from
+ // it though - the first element in the set
+ if ( row instanceof $ && row.length ) {
+ row = row[0];
+ }
+
+ var rows = this.iterator( 'table', function ( settings ) {
+ if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
+ return _fnAddTr( settings, row )[0];
+ }
+ return _fnAddData( settings, row );
+ } );
+
+ // Return an Api.rows() extended instance, with the newly added row selected
+ return this.row( rows[0] );
+ } );
+
+
+
+ var __details_add = function ( ctx, row, data, klass )
+ {
+ // Convert to array of TR elements
+ var rows = [];
+ var addRow = function ( r, k ) {
+ // Recursion to allow for arrays of jQuery objects
+ if ( $.isArray( r ) || r instanceof $ ) {
+ for ( var i=0, ien=r.length ; i<ien ; i++ ) {
+ addRow( r[i], k );
+ }
+ return;
+ }
+
+ // If we get a TR element, then just add it directly - up to the dev
+ // to add the correct number of columns etc
+ if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
+ rows.push( r );
+ }
+ else {
+ // Otherwise create a row with a wrapper
+ var created = $('<tr><td/></tr>').addClass( k );
+ $('td', created)
+ .addClass( k )
+ .html( r )
+ [0].colSpan = _fnVisbleColumns( ctx );
+
+ rows.push( created[0] );
+ }
+ };
+
+ addRow( data, klass );
+
+ if ( row._details ) {
+ row._details.remove();
+ }
+
+ row._details = $(rows);
+
+ // If the children were already shown, that state should be retained
+ if ( row._detailsShow ) {
+ row._details.insertAfter( row.nTr );
+ }
+ };
+
+
+ var __details_remove = function ( api, idx )
+ {
+ var ctx = api.context;
+
+ if ( ctx.length ) {
+ var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
+
+ if ( row && row._details ) {
+ row._details.remove();
+
+ row._detailsShow = undefined;
+ row._details = undefined;
+ }
+ }
+ };
+
+
+ var __details_display = function ( api, show ) {
+ var ctx = api.context;
+
+ if ( ctx.length && api.length ) {
+ var row = ctx[0].aoData[ api[0] ];
+
+ if ( row._details ) {
+ row._detailsShow = show;
+
+ if ( show ) {
+ row._details.insertAfter( row.nTr );
+ }
+ else {
+ row._details.detach();
+ }
+
+ __details_events( ctx[0] );
+ }
+ }
+ };
+
+
+ var __details_events = function ( settings )
+ {
+ var api = new _Api( settings );
+ var namespace = '.dt.DT_details';
+ var drawEvent = 'draw'+namespace;
+ var colvisEvent = 'column-visibility'+namespace;
+ var destroyEvent = 'destroy'+namespace;
+ var data = settings.aoData;
+
+ api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
+
+ if ( _pluck( data, '_details' ).length > 0 ) {
+ // On each draw, insert the required elements into the document
+ api.on( drawEvent, function ( e, ctx ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ api.rows( {page:'current'} ).eq(0).each( function (idx) {
+ // Internal data grab
+ var row = data[ idx ];
+
+ if ( row._detailsShow ) {
+ row._details.insertAfter( row.nTr );
+ }
+ } );
+ } );
+
+ // Column visibility change - update the colspan
+ api.on( colvisEvent, function ( e, ctx, idx, vis ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ // Update the colspan for the details rows (note, only if it already has
+ // a colspan)
+ var row, visible = _fnVisbleColumns( ctx );
+
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ row = data[i];
+
+ if ( row._details ) {
+ row._details.children('td[colspan]').attr('colspan', visible );
+ }
+ }
+ } );
+
+ // Table destroyed - nuke any child rows
+ api.on( destroyEvent, function ( e, ctx ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
+ if ( data[i]._details ) {
+ __details_remove( api, i );
+ }
+ }
+ } );
+ }
+ };
+
+ // Strings for the method names to help minification
+ var _emp = '';
+ var _child_obj = _emp+'row().child';
+ var _child_mth = _child_obj+'()';
+
+ // data can be:
+ // tr
+ // string
+ // jQuery or array of any of the above
+ _api_register( _child_mth, function ( data, klass ) {
+ var ctx = this.context;
+
+ if ( data === undefined ) {
+ // get
+ return ctx.length && this.length ?
+ ctx[0].aoData[ this[0] ]._details :
+ undefined;
+ }
+ else if ( data === true ) {
+ // show
+ this.child.show();
+ }
+ else if ( data === false ) {
+ // remove
+ __details_remove( this );
+ }
+ else if ( ctx.length && this.length ) {
+ // set
+ __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
+ }
+
+ return this;
+ } );
+
+
+ _api_register( [
+ _child_obj+'.show()',
+ _child_mth+'.show()' // only when `child()` was called with parameters (without
+ ], function ( show ) { // it returns an object and this method is not executed)
+ __details_display( this, true );
+ return this;
+ } );
+
+
+ _api_register( [
+ _child_obj+'.hide()',
+ _child_mth+'.hide()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_display( this, false );
+ return this;
+ } );
+
+
+ _api_register( [
+ _child_obj+'.remove()',
+ _child_mth+'.remove()' // only when `child()` was called with parameters (without
+ ], function () { // it returns an object and this method is not executed)
+ __details_remove( this );
+ return this;
+ } );
+
+
+ _api_register( _child_obj+'.isShown()', function () {
+ var ctx = this.context;
+
+ if ( ctx.length && this.length ) {
+ // _detailsShown as false or undefined will fall through to return false
+ return ctx[0].aoData[ this[0] ]._detailsShow || false;
+ }
+ return false;
+ } );
+
+
+
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Columns
+ *
+ * {integer} - column index (>=0 count from left, <0 count from right)
+ * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
+ * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
+ * "{string}:name" - column name
+ * "{string}" - jQuery selector on column header nodes
+ *
+ */
+
+ // can be an array of these items, comma separated list, or an array of comma
+ // separated lists
+
+ var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
+
+
+ // r1 and r2 are redundant - but it means that the parameters match for the
+ // iterator callback in columns().data()
+ var __columnData = function ( settings, column, r1, r2, rows ) {
+ var a = [];
+ for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
+ a.push( _fnGetCellData( settings, rows[row], column ) );
+ }
+ return a;
+ };
+
+
+ var __column_selector = function ( settings, selector, opts )
+ {
+ var
+ columns = settings.aoColumns,
+ names = _pluck( columns, 'sName' ),
+ nodes = _pluck( columns, 'nTh' );
+
+ var run = function ( s ) {
+ var selInt = _intVal( s );
+
+ // Selector - all
+ if ( s === '' ) {
+ return _range( columns.length );
+ }
+
+ // Selector - index
+ if ( selInt !== null ) {
+ return [ selInt >= 0 ?
+ selInt : // Count from left
+ columns.length + selInt // Count from right (+ because its a negative value)
+ ];
+ }
+
+ // Selector = function
+ if ( typeof s === 'function' ) {
+ var rows = _selector_row_indexes( settings, opts );
+
+ return $.map( columns, function (col, idx) {
+ return s(
+ idx,
+ __columnData( settings, idx, 0, 0, rows ),
+ nodes[ idx ]
+ ) ? idx : null;
+ } );
+ }
+
+ // jQuery or string selector
+ var match = typeof s === 'string' ?
+ s.match( __re_column_selector ) :
+ '';
+
+ if ( match ) {
+ switch( match[2] ) {
+ case 'visIdx':
+ case 'visible':
+ var idx = parseInt( match[1], 10 );
+ // Visible index given, convert to column index
+ if ( idx < 0 ) {
+ // Counting from the right
+ var visColumns = $.map( columns, function (col,i) {
+ return col.bVisible ? i : null;
+ } );
+ return [ visColumns[ visColumns.length + idx ] ];
+ }
+ // Counting from the left
+ return [ _fnVisibleToColumnIndex( settings, idx ) ];
+
+ case 'name':
+ // match by name. `names` is column index complete and in order
+ return $.map( names, function (name, i) {
+ return name === match[1] ? i : null;
+ } );
+
+ default:
+ return [];
+ }
+ }
+
+ // Cell in the table body
+ if ( s.nodeName && s._DT_CellIndex ) {
+ return [ s._DT_CellIndex.column ];
+ }
+
+ // jQuery selector on the TH elements for the columns
+ var jqResult = $( nodes )
+ .filter( s )
+ .map( function () {
+ return $.inArray( this, nodes ); // `nodes` is column index complete and in order
+ } )
+ .toArray();
+
+ if ( jqResult.length || ! s.nodeName ) {
+ return jqResult;
+ }
+
+ // Otherwise a node which might have a `dt-column` data attribute, or be
+ // a child or such an element
+ var host = $(s).closest('*[data-dt-column]');
+ return host.length ?
+ [ host.data('dt-column') ] :
+ [];
+ };
+
+ return _selector_run( 'column', selector, run, settings, opts );
+ };
+
+
+ var __setColumnVis = function ( settings, column, vis, recalc ) {
+ var
+ cols = settings.aoColumns,
+ col = cols[ column ],
+ data = settings.aoData,
+ row, cells, i, ien, tr;
+
+ // Get
+ if ( vis === undefined ) {
+ return col.bVisible;
+ }
+
+ // Set
+ // No change
+ if ( col.bVisible === vis ) {
+ return;
+ }
+
+ if ( vis ) {
+ // Insert column
+ // Need to decide if we should use appendChild or insertBefore
+ var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
+
+ for ( i=0, ien=data.length ; i<ien ; i++ ) {
+ tr = data[i].nTr;
+ cells = data[i].anCells;
+
+ if ( tr ) {
+ // insertBefore can act like appendChild if 2nd arg is null
+ tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
+ }
+ }
+ }
+ else {
+ // Remove column
+ $( _pluck( settings.aoData, 'anCells', column ) ).detach();
+ }
+
+ // Common actions
+ col.bVisible = vis;
+ _fnDrawHead( settings, settings.aoHeader );
+ _fnDrawHead( settings, settings.aoFooter );
+
+ if ( recalc === undefined || recalc ) {
+ // Automatically adjust column sizing
+ _fnAdjustColumnSizing( settings );
+ }
+
+ _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, recalc] );
+
+ _fnSaveState( settings );
+ };
+
+
+ _api_register( 'columns()', function ( selector, opts ) {
+ // argument shifting
+ if ( selector === undefined ) {
+ selector = '';
+ }
+ else if ( $.isPlainObject( selector ) ) {
+ opts = selector;
+ selector = '';
+ }
+
+ opts = _selector_opts( opts );
+
+ var inst = this.iterator( 'table', function ( settings ) {
+ return __column_selector( settings, selector, opts );
+ }, 1 );
+
+ // Want argument shifting here and in _row_selector?
+ inst.selector.cols = selector;
+ inst.selector.opts = opts;
+
+ return inst;
+ } );
+
+ _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column].nTh;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column].nTf;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().data()', 'column().data()', function () {
+ return this.iterator( 'column-rows', __columnData, 1 );
+ } );
+
+ _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
+ return this.iterator( 'column', function ( settings, column ) {
+ return settings.aoColumns[column].mData;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
+ return _pluck_order( settings.aoData, rows,
+ type === 'search' ? '_aFilterData' : '_aSortData', column
+ );
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
+ return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
+ return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
+ }, 1 );
+ } );
+
+ _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ if ( vis === undefined ) {
+ return settings.aoColumns[ column ].bVisible;
+ } // else
+ __setColumnVis( settings, column, vis, calc );
+ } );
+ } );
+
+ _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ return type === 'visible' ?
+ _fnColumnIndexToVisible( settings, column ) :
+ column;
+ }, 1 );
+ } );
+
+ _api_register( 'columns.adjust()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ _fnAdjustColumnSizing( settings );
+ }, 1 );
+ } );
+
+ _api_register( 'column.index()', function ( type, idx ) {
+ if ( this.context.length !== 0 ) {
+ var ctx = this.context[0];
+
+ if ( type === 'fromVisible' || type === 'toData' ) {
+ return _fnVisibleToColumnIndex( ctx, idx );
+ }
+ else if ( type === 'fromData' || type === 'toVisible' ) {
+ return _fnColumnIndexToVisible( ctx, idx );
+ }
+ }
+ } );
+
+ _api_register( 'column()', function ( selector, opts ) {
+ return _selector_first( this.columns( selector, opts ) );
+ } );
+
+
+
+
+ var __cell_selector = function ( settings, selector, opts )
+ {
+ var data = settings.aoData;
+ var rows = _selector_row_indexes( settings, opts );
+ var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
+ var allCells = $( [].concat.apply([], cells) );
+ var row;
+ var columns = settings.aoColumns.length;
+ var a, i, ien, j, o, host;
+
+ var run = function ( s ) {
+ var fnSelector = typeof s === 'function';
+
+ if ( s === null || s === undefined || fnSelector ) {
+ // All cells and function selectors
+ a = [];
+
+ for ( i=0, ien=rows.length ; i<ien ; i++ ) {
+ row = rows[i];
+
+ for ( j=0 ; j<columns ; j++ ) {
+ o = {
+ row: row,
+ column: j
+ };
+
+ if ( fnSelector ) {
+ // Selector - function
+ host = data[ row ];
+
+ if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
+ a.push( o );
+ }
+ }
+ else {
+ // Selector - all
+ a.push( o );
+ }
+ }
+ }
+
+ return a;
+ }
+
+ // Selector - index
+ if ( $.isPlainObject( s ) ) {
+ return [s];
+ }
+
+ // Selector - jQuery filtered cells
+ var jqResult = allCells
+ .filter( s )
+ .map( function (i, el) {
+ return { // use a new object, in case someone changes the values
+ row: el._DT_CellIndex.row,
+ column: el._DT_CellIndex.column
+ };
+ } )
+ .toArray();
+
+ if ( jqResult.length || ! s.nodeName ) {
+ return jqResult;
+ }
+
+ // Otherwise the selector is a node, and there is one last option - the
+ // element might be a child of an element which has dt-row and dt-column
+ // data attributes
+ host = $(s).closest('*[data-dt-row]');
+ return host.length ?
+ [ {
+ row: host.data('dt-row'),
+ column: host.data('dt-column')
+ } ] :
+ [];
+ };
+
+ return _selector_run( 'cell', selector, run, settings, opts );
+ };
+
+
+
+
+ _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
+ // Argument shifting
+ if ( $.isPlainObject( rowSelector ) ) {
+ // Indexes
+ if ( rowSelector.row === undefined ) {
+ // Selector options in first parameter
+ opts = rowSelector;
+ rowSelector = null;
+ }
+ else {
+ // Cell index objects in first parameter
+ opts = columnSelector;
+ columnSelector = null;
+ }
+ }
+ if ( $.isPlainObject( columnSelector ) ) {
+ opts = columnSelector;
+ columnSelector = null;
+ }
+
+ // Cell selector
+ if ( columnSelector === null || columnSelector === undefined ) {
+ return this.iterator( 'table', function ( settings ) {
+ return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
+ } );
+ }
+
+ // Row + column selector
+ var columns = this.columns( columnSelector, opts );
+ var rows = this.rows( rowSelector, opts );
+ var a, i, ien, j, jen;
+
+ var cells = this.iterator( 'table', function ( settings, idx ) {
+ a = [];
+
+ for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
+ for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
+ a.push( {
+ row: rows[idx][i],
+ column: columns[idx][j]
+ } );
+ }
+ }
+
+ return a;
+ }, 1 );
+
+ $.extend( cells.selector, {
+ cols: columnSelector,
+ rows: rowSelector,
+ opts: opts
+ } );
+
+ return cells;
+ } );
+
+
+ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ var data = settings.aoData[ row ];
+
+ return data && data.anCells ?
+ data.anCells[ column ] :
+ undefined;
+ }, 1 );
+ } );
+
+
+ _api_register( 'cells().data()', function () {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ return _fnGetCellData( settings, row, column );
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
+ type = type === 'search' ? '_aFilterData' : '_aSortData';
+
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ return settings.aoData[ row ][ type ][ column ];
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ return _fnGetCellData( settings, row, column, type );
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ return {
+ row: row,
+ column: column,
+ columnVisible: _fnColumnIndexToVisible( settings, column )
+ };
+ }, 1 );
+ } );
+
+
+ _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
+ return this.iterator( 'cell', function ( settings, row, column ) {
+ _fnInvalidate( settings, row, src, column );
+ } );
+ } );
+
+
+
+ _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
+ return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
+ } );
+
+
+ _api_register( 'cell().data()', function ( data ) {
+ var ctx = this.context;
+ var cell = this[0];
+
+ if ( data === undefined ) {
+ // Get
+ return ctx.length && cell.length ?
+ _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
+ undefined;
+ }
+
+ // Set
+ _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
+ _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
+
+ return this;
+ } );
+
+
+
+ /**
+ * Get current ordering (sorting) that has been applied to the table.
+ *
+ * @returns {array} 2D array containing the sorting information for the first
+ * table in the current context. Each element in the parent array represents
+ * a column being sorted upon (i.e. multi-sorting with two columns would have
+ * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
+ * the column index that the sorting condition applies to, the second is the
+ * direction of the sort (`desc` or `asc`) and, optionally, the third is the
+ * index of the sorting order from the `column.sorting` initialisation array.
+ *//**
+ * Set the ordering for the table.
+ *
+ * @param {integer} order Column index to sort upon.
+ * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
+ * @returns {DataTables.Api} this
+ *//**
+ * Set the ordering for the table.
+ *
+ * @param {array} order 1D array of sorting information to be applied.
+ * @param {array} [...] Optional additional sorting conditions
+ * @returns {DataTables.Api} this
+ *//**
+ * Set the ordering for the table.
+ *
+ * @param {array} order 2D array of sorting information to be applied.
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'order()', function ( order, dir ) {
+ var ctx = this.context;
+
+ if ( order === undefined ) {
+ // get
+ return ctx.length !== 0 ?
+ ctx[0].aaSorting :
+ undefined;
+ }
+
+ // set
+ if ( typeof order === 'number' ) {
+ // Simple column / direction passed in
+ order = [ [ order, dir ] ];
+ }
+ else if ( ! $.isArray( order[0] ) ) {
+ // Arguments passed in (list of 1D arrays)
+ order = Array.prototype.slice.call( arguments );
+ }
+ // otherwise a 2D array was passed in
+
+ return this.iterator( 'table', function ( settings ) {
+ settings.aaSorting = order.slice();
+ } );
+ } );
+
+
+ /**
+ * Attach a sort listener to an element for a given column
+ *
+ * @param {node|jQuery|string} node Identifier for the element(s) to attach the
+ * listener to. This can take the form of a single DOM node, a jQuery
+ * collection of nodes or a jQuery selector which will identify the node(s).
+ * @param {integer} column the column that a click on this node will sort on
+ * @param {function} [callback] callback function when sort is run
+ * @returns {DataTables.Api} this
+ */
+ _api_register( 'order.listener()', function ( node, column, callback ) {
+ return this.iterator( 'table', function ( settings ) {
+ _fnSortAttachListener( settings, node, column, callback );
+ } );
+ } );
+
+
+ _api_register( 'order.fixed()', function ( set ) {
+ if ( ! set ) {
+ var ctx = this.context;
+ var fixed = ctx.length ?
+ ctx[0].aaSortingFixed :
+ undefined;
+
+ return $.isArray( fixed ) ?
+ { pre: fixed } :
+ fixed;
+ }
+
+ return this.iterator( 'table', function ( settings ) {
+ settings.aaSortingFixed = $.extend( true, {}, set );
+ } );
+ } );
+
+
+ // Order by the selected column(s)
+ _api_register( [
+ 'columns().order()',
+ 'column().order()'
+ ], function ( dir ) {
+ var that = this;
+
+ return this.iterator( 'table', function ( settings, i ) {
+ var sort = [];
+
+ $.each( that[i], function (j, col) {
+ sort.push( [ col, dir ] );
+ } );
+
+ settings.aaSorting = sort;
+ } );
+ } );
+
+
+
+ _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
+ var ctx = this.context;
+
+ if ( input === undefined ) {
+ // get
+ return ctx.length !== 0 ?
+ ctx[0].oPreviousSearch.sSearch :
+ undefined;
+ }
+
+ // set
+ return this.iterator( 'table', function ( settings ) {
+ if ( ! settings.oFeatures.bFilter ) {
+ return;
+ }
+
+ _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
+ "sSearch": input+"",
+ "bRegex": regex === null ? false : regex,
+ "bSmart": smart === null ? true : smart,
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ } ), 1 );
+ } );
+ } );
+
+
+ _api_registerPlural(
+ 'columns().search()',
+ 'column().search()',
+ function ( input, regex, smart, caseInsen ) {
+ return this.iterator( 'column', function ( settings, column ) {
+ var preSearch = settings.aoPreSearchCols;
+
+ if ( input === undefined ) {
+ // get
+ return preSearch[ column ].sSearch;
+ }
+
+ // set
+ if ( ! settings.oFeatures.bFilter ) {
+ return;
+ }
+
+ $.extend( preSearch[ column ], {
+ "sSearch": input+"",
+ "bRegex": regex === null ? false : regex,
+ "bSmart": smart === null ? true : smart,
+ "bCaseInsensitive": caseInsen === null ? true : caseInsen
+ } );
+
+ _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
+ } );
+ }
+ );
+
+ /*
+ * State API methods
+ */
+
+ _api_register( 'state()', function () {
+ return this.context.length ?
+ this.context[0].oSavedState :
+ null;
+ } );
+
+
+ _api_register( 'state.clear()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ // Save an empty object
+ settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
+ } );
+ } );
+
+
+ _api_register( 'state.loaded()', function () {
+ return this.context.length ?
+ this.context[0].oLoadedState :
+ null;
+ } );
+
+
+ _api_register( 'state.save()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ _fnSaveState( settings );
+ } );
+ } );
+
+
+
+ /**
+ * Provide a common method for plug-ins to check the version of DataTables being
+ * used, in order to ensure compatibility.
+ *
+ * @param {string} version Version string to check for, in the format "X.Y.Z".
+ * Note that the formats "X" and "X.Y" are also acceptable.
+ * @returns {boolean} true if this version of DataTables is greater or equal to
+ * the required version, or false if this version of DataTales is not
+ * suitable
* @static
* @dtopt API-Static
*
* @example
- * var ex = document.getElementById('example');
- * if ( ! $.fn.DataTable.fnIsDataTable( ex ) ) {
- * $(ex).dataTable();
- * }
+ * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
*/
- DataTable.fnIsDataTable = function ( nTable )
+ DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
{
- var o = DataTable.settings;
+ var aThis = DataTable.version.split('.');
+ var aThat = version.split('.');
+ var iThis, iThat;
- for ( var i=0 ; i<o.length ; i++ )
- {
- if ( o[i].nTable === nTable || o[i].nScrollHead === nTable || o[i].nScrollFoot === nTable )
- {
- return true;
+ for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
+ iThis = parseInt( aThis[i], 10 ) || 0;
+ iThat = parseInt( aThat[i], 10 ) || 0;
+
+ // Parts are the same, keep comparing
+ if (iThis === iThat) {
+ continue;
}
+
+ // Parts are different, return immediately
+ return iThis > iThat;
}
- return false;
+ return true;
};
/**
- * Get all DataTable tables that have been initialised - optionally you can select to
- * get only currently visible tables.
- * @param {boolean} [bVisible=false] Flag to indicate if you want all (default) or
- * visible tables only.
- * @returns {array} Array of TABLE nodes (not DataTable instances) which are DataTables
+ * Check if a `<table>` node is a DataTable table already or not.
+ *
+ * @param {node|jquery|string} table Table node, jQuery object or jQuery
+ * selector for the table to test. Note that if more than more than one
+ * table is passed on, only the first will be checked
+ * @returns {boolean} true the table given is a DataTable, or false otherwise
* @static
* @dtopt API-Static
*
* @example
- * var table = $.fn.dataTable.fnTables(true);
- * if ( table.length > 0 ) {
- * $(table).dataTable().fnAdjustColumnSizing();
+ * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
+ * $('#example').dataTable();
* }
*/
- DataTable.fnTables = function ( bVisible )
+ DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
{
- var out = [];
+ var t = $(table).get(0);
+ var is = false;
- jQuery.each( DataTable.settings, function (i, o) {
- if ( !bVisible || (bVisible === true && $(o.nTable).is(':visible')) )
- {
- out.push( o.nTable );
+ $.each( DataTable.settings, function (i, o) {
+ var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
+ var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
+
+ if ( o.nTable === t || head === t || foot === t ) {
+ is = true;
}
} );
- return out;
+ return is;
};
-
- /**
- * Version string for plug-ins to check compatibility. Allowed format is
- * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
- * e are optional
- * @member
- * @type string
- * @default Version number
- */
- DataTable.version = "1.9.4";
-
- /**
- * Private data store, containing all of the settings objects that are created for the
- * tables on a given page.
- *
- * Note that the <i>DataTable.settings</i> object is aliased to <i>jQuery.fn.dataTableExt</i>
- * through which it may be accessed and manipulated, or <i>jQuery.fn.dataTable.settings</i>.
- * @member
- * @type array
- * @default []
- * @private
- */
- DataTable.settings = [];
-
+
/**
- * Object models container, for the various models that DataTables has available
- * to it. These models define the objects that are used to hold the active state
- * and configuration of the table.
- * @namespace
+ * Get all DataTable tables that have been initialised - optionally you can
+ * select to get only currently visible tables.
+ *
+ * @param {boolean} [visible=false] Flag to indicate if you want all (default)
+ * or visible tables only.
+ * @returns {array} Array of `table` nodes (not DataTable instances) which are
+ * DataTables
+ * @static
+ * @dtopt API-Static
+ *
+ * @example
+ * $.each( $.fn.dataTable.tables(true), function () {
+ * $(table).DataTable().columns.adjust();
+ * } );
*/
- DataTable.models = {};
+ DataTable.tables = DataTable.fnTables = function ( visible )
+ {
+ var api = false;
+
+ if ( $.isPlainObject( visible ) ) {
+ api = visible.api;
+ visible = visible.visible;
+ }
+
+ var a = $.map( DataTable.settings, function (o) {
+ if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
+ return o.nTable;
+ }
+ } );
+
+ return api ?
+ new _Api( a ) :
+ a;
+ };
/**
- * DataTables extension options and plug-ins. This namespace acts as a collection "area"
- * for plug-ins that can be used to extend the default DataTables behaviour - indeed many
- * of the build in methods use this method to provide their own capabilities (sorting methods
- * for example).
+ * DataTables utility methods
*
- * Note that this namespace is aliased to jQuery.fn.dataTableExt so it can be readily accessed
- * and modified by plug-ins.
+ * This namespace provides helper methods that DataTables uses internally to
+ * create a DataTable, but which are not exclusively used only for DataTables.
+ * These methods can be used by extension authors to save the duplication of
+ * code.
+ *
* @namespace
*/
- DataTable.models.ext = {
+ DataTable.util = {
/**
- * Plug-in filtering functions - this method of filtering is complimentary to the default
- * type based filtering, and a lot more comprehensive as it allows you complete control
- * over the filtering logic. Each element in this array is a function (parameters
- * described below) that is called for every row in the table, and your logic decides if
- * it should be included in the filtered data set or not.
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
- * <li>{array|object} Data for the row to be processed (same as the original format
- * that was passed in as the data source, or an array from a DOM data source</li>
- * <li>{int} Row index in aoData ({@link DataTable.models.oSettings.aoData}), which can
- * be useful to retrieve the TR element if you need DOM interaction.</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{boolean} Include the row in the filtered result set (true) or not (false)</li>
- * </ul>
- * </il>
- * </ul>
- * @type array
- * @default []
+ * Throttle the calls to a function. Arguments and context are maintained
+ * for the throttled function.
*
- * @example
- * // The following example shows custom filtering being applied to the fourth column (i.e.
- * // the aData[3] index) based on two input values from the end-user, matching the data in
- * // a certain range.
- * $.fn.dataTableExt.afnFiltering.push(
- * function( oSettings, aData, iDataIndex ) {
- * var iMin = document.getElementById('min').value * 1;
- * var iMax = document.getElementById('max').value * 1;
- * var iVersion = aData[3] == "-" ? 0 : aData[3]*1;
- * if ( iMin == "" && iMax == "" ) {
- * return true;
- * }
- * else if ( iMin == "" && iVersion < iMax ) {
- * return true;
- * }
- * else if ( iMin < iVersion && "" == iMax ) {
- * return true;
- * }
- * else if ( iMin < iVersion && iVersion < iMax ) {
- * return true;
- * }
- * return false;
- * }
- * );
+ * @param {function} fn Function to be called
+ * @param {integer} freq Call frequency in mS
+ * @return {function} Wrapped function
*/
- "afnFiltering": [],
+ throttle: _fnThrottle,
/**
- * Plug-in sorting functions - this method of sorting is complimentary to the default type
- * based sorting that DataTables does automatically, allowing much greater control over the
- * the data that is being used to sort a column. This is useful if you want to do sorting
- * based on live data (for example the contents of an 'input' element) rather than just the
- * static string that DataTables knows of. The way these plug-ins work is that you create
- * an array of the values you wish to be sorted for the column in question and then return
- * that array. Which pre-sorting function is run here depends on the sSortDataType parameter
- * that is used for the column (if any). This is the corollary of <i>ofnSearch</i> for sort
- * data.
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
- * <li>{int} Target column index</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{array} Data for the column to be sorted upon</li>
- * </ul>
- * </il>
- * </ul>
- *
- * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
- * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
- * used as a function will give you a 'type' (sorting, filtering etc) that you can use to
- * prepare the data as required for the different types. As such, this method is deprecated.
- * @type array
- * @default []
- * @deprecated
+ * Escape a string such that it can be used in a regular expression
*
- * @example
- * // Updating the cached sorting information with user entered values in HTML input elements
- * jQuery.fn.dataTableExt.afnSortData['dom-text'] = function ( oSettings, iColumn )
- * {
- * var aData = [];
- * $( 'td:eq('+iColumn+') input', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
- * aData.push( this.value );
- * } );
- * return aData;
- * }
+ * @param {string} sVal string to escape
+ * @returns {string} escaped string
*/
- "afnSortData": [],
+ escapeRegex: _fnEscapeRegex
+ };
- /**
- * Feature plug-ins - This is an array of objects which describe the feature plug-ins that are
- * available to DataTables. These feature plug-ins are accessible through the sDom initialisation
- * option. As such, each feature plug-in must describe a function that is used to initialise
- * itself (fnInit), a character so the feature can be enabled by sDom (cFeature) and the name
- * of the feature (sFeature). Thus the objects attached to this method must provide:
- * <ul>
- * <li>{function} fnInit Initialisation of the plug-in
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{node|null} The element which contains your feature. Note that the return
- * may also be void if your plug-in does not require to inject any DOM elements
- * into DataTables control (sDom) - for example this might be useful when
- * developing a plug-in which allows table control via keyboard entry.</li>
- * </ul>
- * </il>
- * </ul>
- * </li>
- * <li>{character} cFeature Character that will be matched in sDom - case sensitive</li>
- * <li>{string} sFeature Feature name</li>
- * </ul>
- * @type array
- * @default []
- *
- * @example
- * // How TableTools initialises itself.
- * $.fn.dataTableExt.aoFeatures.push( {
- * "fnInit": function( oSettings ) {
- * return new TableTools( { "oDTSettings": oSettings } );
- * },
- * "cFeature": "T",
- * "sFeature": "TableTools"
- * } );
- */
- "aoFeatures": [],
+ /**
+ * Convert from camel case parameters to Hungarian notation. This is made public
+ * for the extensions to provide the same ability as DataTables core to accept
+ * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
+ * parameters.
+ *
+ * @param {object} src The model object which holds all parameters that can be
+ * mapped.
+ * @param {object} user The object to convert from camel case to Hungarian.
+ * @param {boolean} force When set to `true`, properties which already have a
+ * Hungarian value in the `user` object will be overwritten. Otherwise they
+ * won't be.
+ */
+ DataTable.camelToHungarian = _fnCamelToHungarian;
- /**
- * Type detection plug-in functions - DataTables utilises types to define how sorting and
- * filtering behave, and types can be either be defined by the developer (sType for the
- * column) or they can be automatically detected by the methods in this array. The functions
- * defined in the array are quite simple, taking a single parameter (the data to analyse)
- * and returning the type if it is a known type, or null otherwise.
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{*} Data from the column cell to be analysed</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{string|null} Data type detected, or null if unknown (and thus pass it
- * on to the other type detection functions.</li>
- * </ul>
- * </il>
- * </ul>
- * @type array
- * @default []
- *
- * @example
- * // Currency type detection plug-in:
- * jQuery.fn.dataTableExt.aTypes.push(
- * function ( sData ) {
- * var sValidChars = "0123456789.-";
- * var Char;
- *
- * // Check the numeric part
- * for ( i=1 ; i<sData.length ; i++ ) {
- * Char = sData.charAt(i);
- * if (sValidChars.indexOf(Char) == -1) {
- * return null;
- * }
- * }
- *
- * // Check prefixed by currency
- * if ( sData.charAt(0) == '$' || sData.charAt(0) == '&pound;' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- "aTypes": [],
+ /**
+ *
+ */
+ _api_register( '$()', function ( selector, opts ) {
+ var
+ rows = this.rows( opts ).nodes(), // Get all rows
+ jqRows = $(rows);
+
+ return $( [].concat(
+ jqRows.filter( selector ).toArray(),
+ jqRows.find( selector ).toArray()
+ ) );
+ } );
- /**
- * Provide a common method for plug-ins to check the version of DataTables being used,
- * in order to ensure compatibility.
- * @type function
- * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note
- * that the formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to the
- * required version, or false if this version of DataTales is not suitable
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * alert( oTable.fnVersionCheck( '1.9.0' ) );
- * } );
- */
- "fnVersionCheck": DataTable.fnVersionCheck,
+ // jQuery functions to operate on the tables
+ $.each( [ 'on', 'one', 'off' ], function (i, key) {
+ _api_register( key+'()', function ( /* event, handler */ ) {
+ var args = Array.prototype.slice.call(arguments);
- /**
- * Index for what 'this' index API functions should use
- * @type int
- * @default 0
- */
- "iApiIndex": 0,
+ // Add the `dt` namespace automatically if it isn't already present
+ if ( ! args[0].match(/\.dt\b/) ) {
+ args[0] += '.dt';
+ }
+ var inst = $( this.tables().nodes() );
+ inst[key].apply( inst, args );
+ return this;
+ } );
+ } );
- /**
- * Pre-processing of filtering data plug-ins - When you assign the sType for a column
- * (or have it automatically detected for you by DataTables or a type detection plug-in),
- * you will typically be using this for custom sorting, but it can also be used to provide
- * custom filtering by allowing you to pre-processing the data and returning the data in
- * the format that should be filtered upon. This is done by adding functions this object
- * with a parameter name which matches the sType for that target column. This is the
- * corollary of <i>afnSortData</i> for filtering data.
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{*} Data from the column cell to be prepared for filtering</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{string|null} Formatted string that will be used for the filtering.</li>
- * </ul>
- * </il>
- * </ul>
- *
- * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
- * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
- * used as a function will give you a 'type' (sorting, filtering etc) that you can use to
- * prepare the data as required for the different types. As such, this method is deprecated.
- * @type object
- * @default {}
- * @deprecated
- *
- * @example
- * $.fn.dataTableExt.ofnSearch['title-numeric'] = function ( sData ) {
- * return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
- * }
- */
- "ofnSearch": {},
+ _api_register( 'clear()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ _fnClearTable( settings );
+ } );
+ } );
- /**
- * Container for all private functions in DataTables so they can be exposed externally
- * @type object
- * @default {}
- */
- "oApi": {},
+ _api_register( 'settings()', function () {
+ return new _Api( this.context, this.context );
+ } );
- /**
- * Storage for the various classes that DataTables uses
- * @type object
- * @default {}
- */
- "oStdClasses": {},
-
- /**
- * Storage for the various classes that DataTables uses - jQuery UI suitable
- * @type object
- * @default {}
- */
- "oJUIClasses": {},
+ _api_register( 'init()', function () {
+ var ctx = this.context;
+ return ctx.length ? ctx[0].oInit : null;
+ } );
- /**
- * Pagination plug-in methods - The style and controls of the pagination can significantly
- * impact on how the end user interacts with the data in your table, and DataTables allows
- * the addition of pagination controls by extending this object, which can then be enabled
- * through the <i>sPaginationType</i> initialisation parameter. Each pagination type that
- * is added is an object (the property name of which is what <i>sPaginationType</i> refers
- * to) that has two properties, both methods that are used by DataTables to update the
- * control's state.
- * <ul>
- * <li>
- * fnInit - Initialisation of the paging controls. Called only during initialisation
- * of the table. It is expected that this function will add the required DOM elements
- * to the page for the paging controls to work. The element pointer
- * 'oSettings.aanFeatures.p' array is provided by DataTables to contain the paging
- * controls (note that this is a 2D array to allow for multiple instances of each
- * DataTables DOM element). It is suggested that you add the controls to this element
- * as children
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
- * <li>{node} Container into which the pagination controls must be inserted</li>
- * <li>{function} Draw callback function - whenever the controls cause a page
- * change, this method must be called to redraw the table.</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>No return required</li>
- * </ul>
- * </il>
- * </ul>
- * </il>
- * <li>
- * fnInit - This function is called whenever the paging status of the table changes and is
- * typically used to update classes and/or text of the paging controls to reflex the new
- * status.
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
- * <li>{function} Draw callback function - in case you need to redraw the table again
- * or attach new event listeners</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>No return required</li>
- * </ul>
- * </il>
- * </ul>
- * </il>
- * </ul>
- * @type object
- * @default {}
- *
- * @example
- * $.fn.dataTableExt.oPagination.four_button = {
- * "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) {
- * nFirst = document.createElement( 'span' );
- * nPrevious = document.createElement( 'span' );
- * nNext = document.createElement( 'span' );
- * nLast = document.createElement( 'span' );
- *
- * nFirst.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sFirst ) );
- * nPrevious.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sPrevious ) );
- * nNext.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sNext ) );
- * nLast.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sLast ) );
- *
- * nFirst.className = "paginate_button first";
- * nPrevious.className = "paginate_button previous";
- * nNext.className="paginate_button next";
- * nLast.className = "paginate_button last";
- *
- * nPaging.appendChild( nFirst );
- * nPaging.appendChild( nPrevious );
- * nPaging.appendChild( nNext );
- * nPaging.appendChild( nLast );
- *
- * $(nFirst).click( function () {
- * oSettings.oApi._fnPageChange( oSettings, "first" );
- * fnCallbackDraw( oSettings );
- * } );
- *
- * $(nPrevious).click( function() {
- * oSettings.oApi._fnPageChange( oSettings, "previous" );
- * fnCallbackDraw( oSettings );
- * } );
- *
- * $(nNext).click( function() {
- * oSettings.oApi._fnPageChange( oSettings, "next" );
- * fnCallbackDraw( oSettings );
- * } );
- *
- * $(nLast).click( function() {
- * oSettings.oApi._fnPageChange( oSettings, "last" );
- * fnCallbackDraw( oSettings );
- * } );
- *
- * $(nFirst).bind( 'selectstart', function () { return false; } );
- * $(nPrevious).bind( 'selectstart', function () { return false; } );
- * $(nNext).bind( 'selectstart', function () { return false; } );
- * $(nLast).bind( 'selectstart', function () { return false; } );
- * },
- *
- * "fnUpdate": function ( oSettings, fnCallbackDraw ) {
- * if ( !oSettings.aanFeatures.p ) {
- * return;
- * }
- *
- * // Loop over each instance of the pager
- * var an = oSettings.aanFeatures.p;
- * for ( var i=0, iLen=an.length ; i<iLen ; i++ ) {
- * var buttons = an[i].getElementsByTagName('span');
- * if ( oSettings._iDisplayStart === 0 ) {
- * buttons[0].className = "paginate_disabled_previous";
- * buttons[1].className = "paginate_disabled_previous";
- * }
- * else {
- * buttons[0].className = "paginate_enabled_previous";
- * buttons[1].className = "paginate_enabled_previous";
- * }
- *
- * if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) {
- * buttons[2].className = "paginate_disabled_next";
- * buttons[3].className = "paginate_disabled_next";
- * }
- * else {
- * buttons[2].className = "paginate_enabled_next";
- * buttons[3].className = "paginate_enabled_next";
- * }
- * }
- * }
- * };
- */
- "oPagination": {},
+ _api_register( 'data()', function () {
+ return this.iterator( 'table', function ( settings ) {
+ return _pluck( settings.aoData, '_aData' );
+ } ).flatten();
+ } );
- /**
- * Sorting plug-in methods - Sorting in DataTables is based on the detected type of the
- * data column (you can add your own type detection functions, or override automatic
- * detection using sType). With this specific type given to the column, DataTables will
- * apply the required sort from the functions in the object. Each sort type must provide
- * two mandatory methods, one each for ascending and descending sorting, and can optionally
- * provide a pre-formatting method that will help speed up sorting by allowing DataTables
- * to pre-format the sort data only once (rather than every time the actual sort functions
- * are run). The two sorting functions are typical Javascript sort methods:
- * <ul>
- * <li>
- * Function input parameters:
- * <ul>
- * <li>{*} Data to compare to the second parameter</li>
- * <li>{*} Data to compare to the first parameter</li>
- * </ul>
- * </li>
- * <li>
- * Function return:
- * <ul>
- * <li>{int} Sorting match: <0 if first parameter should be sorted lower than
- * the second parameter, ===0 if the two parameters are equal and >0 if
- * the first parameter should be sorted height than the second parameter.</li>
- * </ul>
- * </il>
- * </ul>
- * @type object
- * @default {}
- *
- * @example
- * // Case-sensitive string sorting, with no pre-formatting method
- * $.extend( $.fn.dataTableExt.oSort, {
- * "string-case-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-case-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- *
- * @example
- * // Case-insensitive string sorting, with pre-formatting
- * $.extend( $.fn.dataTableExt.oSort, {
- * "string-pre": function(x) {
- * return x.toLowerCase();
- * },
- * "string-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- */
- "oSort": {},
+ _api_register( 'destroy()', function ( remove ) {
+ remove = remove || false;
+ return this.iterator( 'table', function ( settings ) {
+ var orig = settings.nTableWrapper.parentNode;
+ var classes = settings.oClasses;
+ var table = settings.nTable;
+ var tbody = settings.nTBody;
+ var thead = settings.nTHead;
+ var tfoot = settings.nTFoot;
+ var jqTable = $(table);
+ var jqTbody = $(tbody);
+ var jqWrapper = $(settings.nTableWrapper);
+ var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
+ var i, ien;
- /**
- * Version string for plug-ins to check compatibility. Allowed format is
- * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
- * e are optional
- * @type string
- * @default Version number
- */
- "sVersion": DataTable.version,
+ // Flag to note that the table is currently being destroyed - no action
+ // should be taken
+ settings.bDestroying = true;
+ // Fire off the destroy callbacks for plug-ins etc
+ _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
- /**
- * How should DataTables report an error. Can take the value 'alert' or 'throw'
- * @type string
- * @default alert
- */
- "sErrMode": "alert",
+ // If not being removed from the document, make all columns visible
+ if ( ! remove ) {
+ new _Api( settings ).columns().visible( true );
+ }
+ // Blitz all `DT` namespaced events (these are internal events, the
+ // lowercase, `dt` events are user subscribed and they are responsible
+ // for removing them
+ jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
+ $(window).unbind('.DT-'+settings.sInstance);
- /**
- * Store information for DataTables to access globally about other instances
- * @namespace
- * @private
- */
- "_oExternConfig": {
- /* int:iNextUnique - next unique number for an instance */
- "iNextUnique": 0
+ // When scrolling we had to break the table up - restore it
+ if ( table != thead.parentNode ) {
+ jqTable.children('thead').detach();
+ jqTable.append( thead );
+ }
+
+ if ( tfoot && table != tfoot.parentNode ) {
+ jqTable.children('tfoot').detach();
+ jqTable.append( tfoot );
+ }
+
+ settings.aaSorting = [];
+ settings.aaSortingFixed = [];
+ _fnSortingClasses( settings );
+
+ $( rows ).removeClass( settings.asStripeClasses.join(' ') );
+
+ $('th, td', thead).removeClass( classes.sSortable+' '+
+ classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
+ );
+
+ if ( settings.bJUI ) {
+ $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
+ $('th, td', thead).each( function () {
+ var wrapper = $('div.'+classes.sSortJUIWrapper, this);
+ $(this).append( wrapper.contents() );
+ wrapper.detach();
+ } );
+ }
+
+ // Add the TR elements back into the table in their original order
+ jqTbody.children().detach();
+ jqTbody.append( rows );
+
+ // Remove the DataTables generated nodes, events and classes
+ var removedMethod = remove ? 'remove' : 'detach';
+ jqTable[ removedMethod ]();
+ jqWrapper[ removedMethod ]();
+
+ // If we need to reattach the table to the document
+ if ( ! remove && orig ) {
+ // insertBefore acts like appendChild if !arg[1]
+ orig.insertBefore( table, settings.nTableReinsertBefore );
+
+ // Restore the width of the original table - was read from the style property,
+ // so we can restore directly to that
+ jqTable
+ .css( 'width', settings.sDestroyWidth )
+ .removeClass( classes.sTable );
+
+ // If the were originally stripe classes - then we add them back here.
+ // Note this is not fool proof (for example if not all rows had stripe
+ // classes - but it's a good effort without getting carried away
+ ien = settings.asDestroyStripes.length;
+
+ if ( ien ) {
+ jqTbody.children().each( function (i) {
+ $(this).addClass( settings.asDestroyStripes[i % ien] );
+ } );
+ }
+ }
+
+ /* Remove the settings object from the settings array */
+ var idx = $.inArray( settings, DataTable.settings );
+ if ( idx !== -1 ) {
+ DataTable.settings.splice( idx, 1 );
+ }
+ } );
+ } );
+
+
+ // Add the `every()` method for rows, columns and cells in a compact form
+ $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
+ _api_register( type+'s().every()', function ( fn ) {
+ var opts = this.selector.opts;
+ var api = this;
+
+ return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
+ // Rows and columns:
+ // arg1 - index
+ // arg2 - table counter
+ // arg3 - loop counter
+ // arg4 - undefined
+ // Cells:
+ // arg1 - row index
+ // arg2 - column index
+ // arg3 - table counter
+ // arg4 - loop counter
+ fn.call(
+ api[ type ](
+ arg1,
+ type==='cell' ? arg2 : opts,
+ type==='cell' ? opts : undefined
+ ),
+ arg1, arg2, arg3, arg4
+ );
+ } );
+ } );
+ } );
+
+
+ // i18n method for extensions to be able to use the language object from the
+ // DataTable
+ _api_register( 'i18n()', function ( token, def, plural ) {
+ var ctx = this.context[0];
+ var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
+
+ if ( resolved === undefined ) {
+ resolved = def;
+ }
+
+ if ( plural !== undefined && $.isPlainObject( resolved ) ) {
+ resolved = resolved[ plural ] !== undefined ?
+ resolved[ plural ] :
+ resolved._;
}
- };
+ return resolved.replace( '%d', plural ); // nb: plural might be undefined,
+ } );
+
+ /**
+ * Version string for plug-ins to check compatibility. Allowed format is
+ * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
+ * only for non-release builds. See http://semver.org/ for more information.
+ * @member
+ * @type string
+ * @default Version number
+ */
+ DataTable.version = "1.10.11";
+
+ /**
+ * Private data store, containing all of the settings objects that are
+ * created for the tables on a given page.
+ *
+ * Note that the `DataTable.settings` object is aliased to
+ * `jQuery.fn.dataTableExt` through which it may be accessed and
+ * manipulated, or `jQuery.fn.dataTable.settings`.
+ * @member
+ * @type array
+ * @default []
+ * @private
+ */
+ DataTable.settings = [];
+
+ /**
+ * Object models container, for the various models that DataTables has
+ * available to it. These models define the objects that are used to hold
+ * the active state and configuration of the table.
+ * @namespace
+ */
+ DataTable.models = {};
@@ -7499,7 +9426,7 @@
/**
* Template object for the way in which DataTables holds information about
- * each individual row. This is the object format used for the settings
+ * each individual row. This is the object format used for the settings
* aoData array.
* @namespace
*/
@@ -7512,10 +9439,18 @@
"nTr": null,
/**
+ * Array of TD elements for each row. This is null until the row has been
+ * created.
+ * @type array nodes
+ * @default []
+ */
+ "anCells": null,
+
+ /**
* Data object from the original data source for the row. This is either
* an array if using the traditional form of DataTables, or an object if
* using mData options. The exact type will depend on the passed in
- * data from the data source, or will be an array if using DOM a data
+ * data from the data source, or will be an array if using DOM a data
* source.
* @type array|object
* @default []
@@ -7524,29 +9459,37 @@
/**
* Sorting data cache - this array is ostensibly the same length as the
- * number of columns (although each index is generated only as it is
+ * number of columns (although each index is generated only as it is
* needed), and holds the data that is used for sorting each column in the
* row. We do this cache generation at the start of the sort in order that
* the formatting of the sort data need be done only once for each cell
* per sort. This array should not be read from or written to by anything
* other than the master sorting methods.
* @type array
- * @default []
+ * @default null
* @private
*/
- "_aSortData": [],
+ "_aSortData": null,
/**
- * Array of TD elements that are cached for hidden rows, so they can be
- * reinserted into the table if a column is made visible again (or to act
- * as a store if a column is made hidden). Only hidden columns have a
- * reference in the array. For non-hidden columns the value is either
- * undefined or null.
- * @type array nodes
- * @default []
+ * Per cell filtering data cache. As per the sort data cache, used to
+ * increase the performance of the filtering in DataTables
+ * @type array
+ * @default null
* @private
*/
- "_anHidden": [],
+ "_aFilterData": null,
+
+ /**
+ * Filtering data cache. This is the same as the cell filtering cache, but
+ * in this case a string rather than an array. This is easily computed with
+ * a join on `_aFilterData`, but is provided as a cache so the join isn't
+ * needed on every search (memory traded for performance)
+ * @type array
+ * @default null
+ * @private
+ */
+ "_sFilterRow": null,
/**
* Cache of the class name that DataTables has applied to the row, so we
@@ -7556,17 +9499,36 @@
* @default <i>Empty string</i>
* @private
*/
- "_sRowStripe": ""
- };
+ "_sRowStripe": "",
+
+ /**
+ * Denote if the original data source was from the DOM, or the data source
+ * object. This is used for invalidating data, so DataTables can
+ * automatically read data from the original source, unless uninstructed
+ * otherwise.
+ * @type string
+ * @default null
+ * @private
+ */
+ "src": null,
+ /**
+ * Index in the aoData array. This saves an indexOf lookup when we have the
+ * object, but want to know the index
+ * @type integer
+ * @default -1
+ * @private
+ */
+ "idx": -1
+ };
/**
* Template object for the column information object in DataTables. This object
* is held in the settings aoColumns array and contains all the information that
* DataTables needs about each individual column.
- *
- * Note that this object is related to {@link DataTable.defaults.columns}
+ *
+ * Note that this object is related to {@link DataTable.defaults.column}
* but this one is the internal data store for DataTables's cache of columns.
* It should NOT be manipulated outside of DataTables. Any configuration should
* be done through the initialisation options.
@@ -7574,6 +9536,14 @@
*/
DataTable.models.oColumn = {
/**
+ * Column index. This could be worked out on-the-fly with $.inArray, but it
+ * is faster to just hold it as a variable
+ * @type integer
+ * @default null
+ */
+ "idx": null,
+
+ /**
* A list of the columns that sorting should occur on when this column
* is sorted. That this property is an array allows multi-column sorting
* to be defined for a column (for example first name / last name columns
@@ -7593,49 +9563,44 @@
* @type array
*/
"asSorting": null,
-
+
/**
* Flag to indicate if the column is searchable, and thus should be included
* in the filtering or not.
* @type boolean
*/
"bSearchable": null,
-
+
/**
* Flag to indicate if the column is sortable or not.
* @type boolean
*/
"bSortable": null,
-
- /**
- * <code>Deprecated</code> When using fnRender, you have two options for what
- * to do with the data, and this property serves as the switch. Firstly, you
- * can have the sorting and filtering use the rendered value (true - default),
- * or you can have the sorting and filtering us the original value (false).
- *
- * Please note that this option has now been deprecated and will be removed
- * in the next version of DataTables. Please use mRender / mData rather than
- * fnRender.
- * @type boolean
- * @deprecated
- */
- "bUseRendered": null,
-
+
/**
* Flag to indicate if the column is currently visible in the table or not
* @type boolean
*/
"bVisible": null,
-
+
/**
- * Flag to indicate to the type detection method if the automatic type
- * detection should be used, or if a column type (sType) has been specified
+ * Store for manual type assignment using the `column.type` option. This
+ * is held in store so we can manipulate the column's `sType` property.
+ * @type string
+ * @default null
+ * @private
+ */
+ "_sManualType": null,
+
+ /**
+ * Flag to indicate if HTML5 data attributes should be used as the data
+ * source for filtering or sorting. True is either are.
* @type boolean
- * @default true
+ * @default false
* @private
*/
- "_bAutoType": true,
-
+ "_bAttrSrc": false,
+
/**
* Developer definable function that is called whenever a cell is created (Ajax source,
* etc) or processed for input (DOM source). This can be used as a compliment to mRender
@@ -7649,64 +9614,45 @@
* @default null
*/
"fnCreatedCell": null,
-
+
/**
* Function to get data from a cell in a column. You should <b>never</b>
* access data directly through _aData internally in DataTables - always use
* the method attached to this property. It allows mData to function as
- * required. This function is automatically assigned by the column
+ * required. This function is automatically assigned by the column
* initialisation method
* @type function
- * @param {array|object} oData The data array/object for the array
+ * @param {array|object} oData The data array/object for the array
* (i.e. aoData[]._aData)
- * @param {string} sSpecific The specific data type you want to get -
+ * @param {string} sSpecific The specific data type you want to get -
* 'display', 'type' 'filter' 'sort'
* @returns {*} The data for the cell from the given row's data
* @default null
*/
"fnGetData": null,
-
- /**
- * <code>Deprecated</code> Custom display function that will be called for the
- * display of each cell in this column.
- *
- * Please note that this option has now been deprecated and will be removed
- * in the next version of DataTables. Please use mRender / mData rather than
- * fnRender.
- * @type function
- * @param {object} o Object with the following parameters:
- * @param {int} o.iDataRow The row in aoData
- * @param {int} o.iDataColumn The column in question
- * @param {array} o.aData The data for the row in question
- * @param {object} o.oSettings The settings object for this DataTables instance
- * @returns {string} The string you which to use in the display
- * @default null
- * @deprecated
- */
- "fnRender": null,
-
+
/**
- * Function to set data for a cell in the column. You should <b>never</b>
+ * Function to set data for a cell in the column. You should <b>never</b>
* set the data directly to _aData internally in DataTables - always use
* this method. It allows mData to function as required. This function
* is automatically assigned by the column initialisation method
* @type function
- * @param {array|object} oData The data array/object for the array
+ * @param {array|object} oData The data array/object for the array
* (i.e. aoData[]._aData)
* @param {*} sValue Value to set
* @default null
*/
"fnSetData": null,
-
+
/**
- * Property to read the value for the cells in the column from the data
+ * Property to read the value for the cells in the column from the data
* source array / object. If null, then the default content is used, if a
* function is given then the return from the function is used.
* @type function|int|string|null
* @default null
*/
"mData": null,
-
+
/**
* Partner property to mData which is used (only when defined) to get
* the data - i.e. it is basically the same as mData, but without the
@@ -7716,7 +9662,7 @@
* @default null
*/
"mRender": null,
-
+
/**
* Unique header TH/TD element for this column - this is what the sorting
* listener is attached to (if sorting is enabled.)
@@ -7724,28 +9670,28 @@
* @default null
*/
"nTh": null,
-
+
/**
- * Unique footer TH/TD element for this column (if there is one). Not used
- * in DataTables as such, but can be used for plug-ins to reference the
+ * Unique footer TH/TD element for this column (if there is one). Not used
+ * in DataTables as such, but can be used for plug-ins to reference the
* footer for each column.
* @type node
* @default null
*/
"nTf": null,
-
+
/**
* The class to apply to all TD elements in the table's TBODY for the column
* @type string
* @default null
*/
"sClass": null,
-
+
/**
* When DataTables calculates the column widths to assign to each column,
* it finds the longest string in each column and then constructs a
* temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
* string - thus the calculation can go wrong (doing it properly and putting
* it into an DOM object and measuring that is horribly(!) slow). Thus as
* a "work around" we provide this option. It will append its value to the
@@ -7753,7 +9699,7 @@
* @type string
*/
"sContentPadding": null,
-
+
/**
* Allows a default value to be given for a column's data, and will be used
* whenever a null data source is encountered (this can be because mData
@@ -7762,14 +9708,14 @@
* @default null
*/
"sDefaultContent": null,
-
+
/**
* Name for the column, allowing reference to the column by name as well as
* by index (needs a lookup to work by name).
* @type string
*/
"sName": null,
-
+
/**
* Custom sorting data type - defines which of the available plug-ins in
* afnSortData the custom sorting will use - if any is defined.
@@ -7777,14 +9723,14 @@
* @default std
*/
"sSortDataType": 'std',
-
+
/**
* Class to be applied to the header element when sorting on this column
* @type string
* @default null
*/
"sSortingClass": null,
-
+
/**
* Class to be applied to the header element when sorting on this column -
* when jQuery UI theming is used.
@@ -7792,27 +9738,27 @@
* @default null
*/
"sSortingClassJUI": null,
-
+
/**
* Title of the column - what is seen in the TH element (nTh).
* @type string
*/
"sTitle": null,
-
+
/**
* Column sorting and filtering type
* @type string
* @default null
*/
"sType": null,
-
+
/**
* Width of the column
* @type string
* @default null
*/
"sWidth": null,
-
+
/**
* Width of the column when it was first "encountered"
* @type string
@@ -7822,45 +9768,62 @@
};
+ /*
+ * Developer note: The properties of the object below are given in Hungarian
+ * notation, that was used as the interface for DataTables prior to v1.10, however
+ * from v1.10 onwards the primary interface is camel case. In order to avoid
+ * breaking backwards compatibility utterly with this change, the Hungarian
+ * version is still, internally the primary interface, but is is not documented
+ * - hence the @name tags in each doc comment. This allows a Javascript function
+ * to create a map from Hungarian notation to camel case (going the other direction
+ * would require each property to be listed, which would at around 3K to the size
+ * of DataTables, while this method is about a 0.5K hit.
+ *
+ * Ultimately this does pave the way for Hungarian notation to be dropped
+ * completely, but that is a massive amount of work and will break current
+ * installs (therefore is on-hold until v2).
+ */
/**
- * Initialisation options that can be given to DataTables at initialisation
+ * Initialisation options that can be given to DataTables at initialisation
* time.
* @namespace
*/
DataTable.defaults = {
/**
- * An array of data to use for the table, passed in at initialisation which
+ * An array of data to use for the table, passed in at initialisation which
* will be used in preference to any data which is already in the DOM. This is
* particularly useful for constructing tables purely in Javascript, for
* example with a custom Ajax call.
* @type array
* @default null
+ *
* @dtopt Option
- *
+ * @name DataTable.defaults.data
+ *
* @example
* // Using a 2D array data source
* $(document).ready( function () {
* $('#example').dataTable( {
- * "aaData": [
+ * "data": [
* ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
* ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
* ],
- * "aoColumns": [
- * { "sTitle": "Engine" },
- * { "sTitle": "Browser" },
- * { "sTitle": "Platform" },
- * { "sTitle": "Version" },
- * { "sTitle": "Grade" }
+ * "columns": [
+ * { "title": "Engine" },
+ * { "title": "Browser" },
+ * { "title": "Platform" },
+ * { "title": "Version" },
+ * { "title": "Grade" }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using an array of objects as a data source (mData)
+ * // Using an array of objects as a data source (`data`)
* $(document).ready( function () {
* $('#example').dataTable( {
- * "aaData": [
+ * "data": [
* {
* "engine": "Trident",
* "browser": "Internet Explorer 4.0",
@@ -7876,12 +9839,12 @@
* "grade": "C"
* }
* ],
- * "aoColumns": [
- * { "sTitle": "Engine", "mData": "engine" },
- * { "sTitle": "Browser", "mData": "browser" },
- * { "sTitle": "Platform", "mData": "platform" },
- * { "sTitle": "Version", "mData": "version" },
- * { "sTitle": "Grade", "mData": "grade" }
+ * "columns": [
+ * { "title": "Engine", "data": "engine" },
+ * { "title": "Browser", "data": "browser" },
+ * { "title": "Platform", "data": "platform" },
+ * { "title": "Version", "data": "version" },
+ * { "title": "Grade", "data": "grade" }
* ]
* } );
* } );
@@ -7890,27 +9853,29 @@
/**
- * If sorting is enabled, then DataTables will perform a first pass sort on
- * initialisation. You can define which column(s) the sort is performed upon,
- * and the sorting direction, with this variable. The aaSorting array should
- * contain an array for each column to be sorted initially containing the
- * column's index and a direction string ('asc' or 'desc').
+ * If ordering is enabled, then DataTables will perform a first pass sort on
+ * initialisation. You can define which column(s) the sort is performed
+ * upon, and the sorting direction, with this variable. The `sorting` array
+ * should contain an array for each column to be sorted initially containing
+ * the column's index and a direction string ('asc' or 'desc').
* @type array
* @default [[0,'asc']]
+ *
* @dtopt Option
- *
+ * @name DataTable.defaults.order
+ *
* @example
* // Sort by 3rd column first, and then 4th column
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aaSorting": [[2,'asc'], [3,'desc']]
+ * "order": [[2,'asc'], [3,'desc']]
* } );
* } );
- *
+ *
* // No initial sorting
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aaSorting": []
+ * "order": []
* } );
* } );
*/
@@ -7918,52 +9883,205 @@
/**
- * This parameter is basically identical to the aaSorting parameter, but
- * cannot be overridden by user interaction with the table. What this means
- * is that you could have a column (visible or hidden) which the sorting will
- * always be forced on first - any sorting after that (from the user) will
- * then be performed as required. This can be useful for grouping rows
+ * This parameter is basically identical to the `sorting` parameter, but
+ * cannot be overridden by user interaction with the table. What this means
+ * is that you could have a column (visible or hidden) which the sorting
+ * will always be forced on first - any sorting after that (from the user)
+ * will then be performed as required. This can be useful for grouping rows
* together.
* @type array
* @default null
+ *
* @dtopt Option
- *
+ * @name DataTable.defaults.orderFixed
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aaSortingFixed": [[0,'asc']]
+ * "orderFixed": [[0,'asc']]
* } );
* } )
*/
- "aaSortingFixed": null,
+ "aaSortingFixed": [],
+
+
+ /**
+ * DataTables can be instructed to load data to display in the table from a
+ * Ajax source. This option defines how that Ajax call is made and where to.
+ *
+ * The `ajax` property has three different modes of operation, depending on
+ * how it is defined. These are:
+ *
+ * * `string` - Set the URL from where the data should be loaded from.
+ * * `object` - Define properties for `jQuery.ajax`.
+ * * `function` - Custom data get function
+ *
+ * `string`
+ * --------
+ *
+ * As a string, the `ajax` property simply defines the URL from which
+ * DataTables will load data.
+ *
+ * `object`
+ * --------
+ *
+ * As an object, the parameters in the object are passed to
+ * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
+ * of the Ajax request. DataTables has a number of default parameters which
+ * you can override using this option. Please refer to the jQuery
+ * documentation for a full description of the options available, although
+ * the following parameters provide additional options in DataTables or
+ * require special consideration:
+ *
+ * * `data` - As with jQuery, `data` can be provided as an object, but it
+ * can also be used as a function to manipulate the data DataTables sends
+ * to the server. The function takes a single parameter, an object of
+ * parameters with the values that DataTables has readied for sending. An
+ * object may be returned which will be merged into the DataTables
+ * defaults, or you can add the items to the object that was passed in and
+ * not return anything from the function. This supersedes `fnServerParams`
+ * from DataTables 1.9-.
+ *
+ * * `dataSrc` - By default DataTables will look for the property `data` (or
+ * `aaData` for compatibility with DataTables 1.9-) when obtaining data
+ * from an Ajax source or for server-side processing - this parameter
+ * allows that property to be changed. You can use Javascript dotted
+ * object notation to get a data source for multiple levels of nesting, or
+ * it my be used as a function. As a function it takes a single parameter,
+ * the JSON returned from the server, which can be manipulated as
+ * required, with the returned value being that used by DataTables as the
+ * data source for the table. This supersedes `sAjaxDataProp` from
+ * DataTables 1.9-.
+ *
+ * * `success` - Should not be overridden it is used internally in
+ * DataTables. To manipulate / transform the data returned by the server
+ * use `ajax.dataSrc`, or use `ajax` as a function (see below).
+ *
+ * `function`
+ * ----------
+ *
+ * As a function, making the Ajax call is left up to yourself allowing
+ * complete control of the Ajax request. Indeed, if desired, a method other
+ * than Ajax could be used to obtain the required data, such as Web storage
+ * or an AIR database.
+ *
+ * The function is given four parameters and no return is required. The
+ * parameters are:
+ *
+ * 1. _object_ - Data to send to the server
+ * 2. _function_ - Callback function that must be executed when the required
+ * data has been obtained. That data should be passed into the callback
+ * as the only parameter
+ * 3. _object_ - DataTables settings object for the table
+ *
+ * Note that this supersedes `fnServerData` from DataTables 1.9-.
+ *
+ * @type string|object|function
+ * @default null
+ *
+ * @dtopt Option
+ * @name DataTable.defaults.ajax
+ * @since 1.10.0
+ *
+ * @example
+ * // Get JSON data from a file via Ajax.
+ * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
+ * $('#example').dataTable( {
+ * "ajax": "data.json"
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to change
+ * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": "tableData"
+ * }
+ * } );
+ *
+ * @example
+ * // Get JSON data from a file via Ajax, using `dataSrc` to read data
+ * // from a plain array rather than an array in an object
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": ""
+ * }
+ * } );
+ *
+ * @example
+ * // Manipulate the data returned from the server - add a link to data
+ * // (note this can, should, be done using `render` for the column - this
+ * // is just a simple example of how the data can be manipulated).
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "dataSrc": function ( json ) {
+ * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
+ * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
+ * }
+ * return json;
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Add data to the request
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "data": function ( d ) {
+ * return {
+ * "extra_search": $('#extra').val()
+ * };
+ * }
+ * }
+ * } );
+ *
+ * @example
+ * // Send request as POST
+ * $('#example').dataTable( {
+ * "ajax": {
+ * "url": "data.json",
+ * "type": "POST"
+ * }
+ * } );
+ *
+ * @example
+ * // Get the data from localStorage (could interface with a form for
+ * // adding, editing and removing rows).
+ * $('#example').dataTable( {
+ * "ajax": function (data, callback, settings) {
+ * callback(
+ * JSON.parse( localStorage.getItem('dataTablesData') )
+ * );
+ * }
+ * } );
+ */
+ "ajax": null,
/**
* This parameter allows you to readily specify the entries in the length drop
- * down menu that DataTables shows when pagination is enabled. It can be
- * either a 1D array of options which will be used for both the displayed
- * option and the value, or a 2D array which will use the array in the first
- * position as the value, and the array in the second position as the
+ * down menu that DataTables shows when pagination is enabled. It can be
+ * either a 1D array of options which will be used for both the displayed
+ * option and the value, or a 2D array which will use the array in the first
+ * position as the value, and the array in the second position as the
* displayed options (useful for language strings such as 'All').
+ *
+ * Note that the `pageLength` property will be automatically set to the
+ * first value given in this array, unless `pageLength` is also provided.
* @type array
* @default [ 10, 25, 50, 100 ]
+ *
* @dtopt Option
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "aLengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
- * } );
- * } );
- *
+ * @name DataTable.defaults.lengthMenu
+ *
* @example
- * // Setting the default display length as well as length menu
- * // This is likely to be wanted if you remove the '10' option which
- * // is the iDisplayLength default.
* $(document).ready( function() {
* $('#example').dataTable( {
- * "iDisplayLength": 25,
- * "aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]]
+ * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
* } );
* } );
*/
@@ -7971,25 +10089,27 @@
/**
- * The aoColumns option in the initialisation parameter allows you to define
+ * The `columns` option in the initialisation parameter allows you to define
* details about the way individual columns behave. For a full list of
- * column options that can be set, please see
- * {@link DataTable.defaults.columns}. Note that if you use aoColumns to
+ * column options that can be set, please see
+ * {@link DataTable.defaults.column}. Note that if you use `columns` to
* define your columns, you must have an entry in the array for every single
* column that you have in your table (these can be null if you don't which
* to specify any options).
* @member
+ *
+ * @name DataTable.defaults.column
*/
"aoColumns": null,
/**
- * Very similar to aoColumns, aoColumnDefs allows you to target a specific
- * column, multiple columns, or all columns, using the aTargets property of
- * each object in the array. This allows great flexibility when creating
- * tables, as the aoColumnDefs arrays can be of any length, targeting the
- * columns you specifically want. aoColumnDefs may use any of the column
- * options available: {@link DataTable.defaults.columns}, but it _must_
- * have aTargets defined in each object in the array. Values in the aTargets
+ * Very similar to `columns`, `columnDefs` allows you to target a specific
+ * column, multiple columns, or all columns, using the `targets` property of
+ * each object in the array. This allows great flexibility when creating
+ * tables, as the `columnDefs` arrays can be of any length, targeting the
+ * columns you specifically want. `columnDefs` may use any of the column
+ * options available: {@link DataTable.defaults.column}, but it _must_
+ * have `targets` defined in each object in the array. Values in the `targets`
* array may be:
* <ul>
* <li>a string - class name will be matched on the TH for the column</li>
@@ -7998,28 +10118,32 @@
* <li>the string "_all" - all columns (i.e. assign a default)</li>
* </ul>
* @member
+ *
+ * @name DataTable.defaults.columnDefs
*/
"aoColumnDefs": null,
/**
- * Basically the same as oSearch, this parameter defines the individual column
- * filtering state at initialisation time. The array must be of the same size
+ * Basically the same as `search`, this parameter defines the individual column
+ * filtering state at initialisation time. The array must be of the same size
* as the number of columns, and each element be an object with the parameters
- * "sSearch" and "bEscapeRegex" (the latter is optional). 'null' is also
+ * `search` and `escapeRegex` (the latter is optional). 'null' is also
* accepted and the default will be used.
* @type array
* @default []
+ *
* @dtopt Option
- *
+ * @name DataTable.defaults.searchCols
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoSearchCols": [
+ * "searchCols": [
* null,
- * { "sSearch": "My filter" },
+ * { "search": "My filter" },
* null,
- * { "sSearch": "^[0-9]", "bEscapeRegex": false }
+ * { "search": "^[0-9]", "escapeRegex": false }
* ]
* } );
* } )
@@ -8028,18 +10152,20 @@
/**
- * An array of CSS classes that should be applied to displayed rows. This
- * array may be of any length, and DataTables will apply each class
+ * An array of CSS classes that should be applied to displayed rows. This
+ * array may be of any length, and DataTables will apply each class
* sequentially, looping when required.
* @type array
- * @default null <i>Will take the values determined by the oClasses.sStripe*
+ * @default null <i>Will take the values determined by the `oClasses.stripe*`
* options</i>
+ *
* @dtopt Option
- *
+ * @name DataTable.defaults.stripeClasses
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "asStripeClasses": [ 'strip1', 'strip2', 'strip3' ]
+ * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
* } );
* } )
*/
@@ -8049,15 +10175,17 @@
/**
* Enable or disable automatic column width calculation. This can be disabled
* as an optimisation (it takes some time to calculate the widths) if the
- * tables widths are passed in using aoColumns.
+ * tables widths are passed in using `columns`.
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.autoWidth
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bAutoWidth": false
+ * "autoWidth": false
* } );
* } );
*/
@@ -8072,13 +10200,15 @@
* time.
* @type boolean
* @default false
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.deferRender
+ *
* @example
* $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "sAjaxSource": "sources/arrays.txt",
- * "bDeferRender": true
+ * $('#example').dataTable( {
+ * "ajax": "sources/arrays.txt",
+ * "deferRender": true
* } );
* } );
*/
@@ -8086,25 +10216,27 @@
/**
- * Replace a DataTable which matches the given selector and replace it with
+ * Replace a DataTable which matches the given selector and replace it with
* one which has the properties of the new initialisation object passed. If no
* table matches the selector, then the new DataTable will be constructed as
* per normal.
* @type boolean
* @default false
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.destroy
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false
+ * "srollY": "200px",
+ * "paginate": false
* } );
- *
+ *
* // Some time later....
* $('#example').dataTable( {
- * "bFilter": false,
- * "bDestroy": true
+ * "filter": false,
+ * "destroy": true
* } );
* } );
*/
@@ -8118,15 +10250,17 @@
* specified (this allow matching across multiple columns). Note that if you
* wish to use filtering in DataTables this must remain 'true' - to remove the
* default filtering input box and retain filtering abilities, please use
- * {@link DataTable.defaults.sDom}.
+ * {@link DataTable.defaults.dom}.
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.searching
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bFilter": false
+ * "searching": false
* } );
* } );
*/
@@ -8134,17 +10268,19 @@
/**
- * Enable or disable the table information display. This shows information
+ * Enable or disable the table information display. This shows information
* about the data that is currently visible on the page, including information
* about filtered data if that action is being performed.
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.info
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bInfo": false
+ * "info": false
* } );
* } );
*/
@@ -8157,12 +10293,14 @@
* traditionally used).
* @type boolean
* @default false
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.jQueryUI
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bJQueryUI": true
+ * "jQueryUI": true
* } );
* } );
*/
@@ -8171,15 +10309,17 @@
/**
* Allows the end user to select the size of a formatted page from a select
- * menu (sizes are 10, 25, 50 and 100). Requires pagination (bPaginate).
+ * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.lengthChange
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bLengthChange": false
+ * "lengthChange": false
* } );
* } );
*/
@@ -8190,12 +10330,14 @@
* Enable or disable pagination.
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.paging
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bPaginate": false
+ * "paging": false
* } );
* } );
*/
@@ -8209,12 +10351,14 @@
* the entries.
* @type boolean
* @default false
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.processing
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bProcessing": true
+ * "processing": true
* } );
* } );
*/
@@ -8227,56 +10371,39 @@
* to simply return the object that has already been set up - it will not take
* account of any changes you might have made to the initialisation object
* passed to DataTables (setting this parameter to true is an acknowledgement
- * that you understand this). bDestroy can be used to reinitialise a table if
+ * that you understand this). `destroy` can be used to reinitialise a table if
* you need.
* @type boolean
* @default false
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.retrieve
+ *
* @example
* $(document).ready( function() {
* initTable();
* tableActions();
* } );
- *
+ *
* function initTable ()
* {
* return $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false,
- * "bRetrieve": true
+ * "scrollY": "200px",
+ * "paginate": false,
+ * "retrieve": true
* } );
* }
- *
+ *
* function tableActions ()
* {
- * var oTable = initTable();
- * // perform API operations with oTable
+ * var table = initTable();
+ * // perform API operations with oTable
* }
*/
"bRetrieve": false,
/**
- * Indicate if DataTables should be allowed to set the padding / margin
- * etc for the scrolling header elements or not. Typically you will want
- * this.
- * @type boolean
- * @default true
- * @dtopt Options
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "bScrollAutoCss": false,
- * "sScrollY": "200px"
- * } );
- * } );
- */
- "bScrollAutoCss": true,
-
-
- /**
* When vertical (y) scrolling is enabled, DataTables will force the height of
* the table's viewport to the given height at all times (useful for layout).
* However, this can look odd when filtering data down to a small data set,
@@ -8285,13 +10412,15 @@
* the result set will fit within the given Y height.
* @type boolean
* @default false
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.scrollCollapse
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sScrollY": "200",
- * "bScrollCollapse": true
+ * "scrollY": "200",
+ * "scrollCollapse": true
* } );
* } );
*/
@@ -8299,63 +10428,64 @@
/**
- * Enable infinite scrolling for DataTables (to be used in combination with
- * sScrollY). Infinite scrolling means that DataTables will continually load
- * data as a user scrolls through a table, which is very useful for large
- * dataset. This cannot be used with pagination, which is automatically
- * disabled. Note - the Scroller extra for DataTables is recommended in
- * in preference to this option.
+ * Configure DataTables to use server-side processing. Note that the
+ * `ajax` parameter must also be given in order to give DataTables a
+ * source to obtain the required data for each draw.
* @type boolean
* @default false
+ *
* @dtopt Features
- *
+ * @dtopt Server-side
+ * @name DataTable.defaults.serverSide
+ *
* @example
- * $(document).ready( function() {
+ * $(document).ready( function () {
* $('#example').dataTable( {
- * "bScrollInfinite": true,
- * "bScrollCollapse": true,
- * "sScrollY": "200px"
+ * "serverSide": true,
+ * "ajax": "xhr.php"
* } );
* } );
*/
- "bScrollInfinite": false,
+ "bServerSide": false,
/**
- * Configure DataTables to use server-side processing. Note that the
- * sAjaxSource parameter must also be given in order to give DataTables a
- * source to obtain the required data for each draw.
+ * Enable or disable sorting of columns. Sorting of individual columns can be
+ * disabled by the `sortable` option for each column.
* @type boolean
- * @default false
+ * @default true
+ *
* @dtopt Features
- * @dtopt Server-side
- *
+ * @name DataTable.defaults.ordering
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bServerSide": true,
- * "sAjaxSource": "xhr.php"
+ * "ordering": false
* } );
* } );
*/
- "bServerSide": false,
+ "bSort": true,
/**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the "bSortable" option for each column.
+ * Enable or display DataTables' ability to sort multiple columns at the
+ * same time (activated by shift-click by the user).
* @type boolean
* @default true
- * @dtopt Features
- *
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.orderMulti
+ *
* @example
+ * // Disable multiple column sorting ability
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bSort": false
+ * "orderMulti": false
* } );
* } );
*/
- "bSort": true,
+ "bSortMulti": true,
/**
@@ -8364,12 +10494,14 @@
* This is useful when using complex headers.
* @type boolean
* @default false
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.orderCellsTop
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bSortCellsTop": true
+ * "orderCellsTop": true
* } );
* } );
*/
@@ -8377,19 +10509,21 @@
/**
- * Enable or disable the addition of the classes 'sorting_1', 'sorting_2' and
- * 'sorting_3' to the columns which are currently being sorted on. This is
+ * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
+ * `sorting\_3` to the columns which are currently being sorted on. This is
* presented as a feature switch as it can increase processing time (while
* classes are removed and added) so for large data sets you might want to
* turn this off.
* @type boolean
* @default true
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.orderClasses
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bSortClasses": false
+ * "orderClasses": false
* } );
* } );
*/
@@ -8397,18 +10531,24 @@
/**
- * Enable or disable state saving. When enabled a cookie will be used to save
- * table display information such as pagination information, display length,
- * filtering and sorting. As such when the end user reloads the page the
- * display display will match what thy had previously set up.
+ * Enable or disable state saving. When enabled HTML5 `localStorage` will be
+ * used to save table display information such as pagination information,
+ * display length, filtering and sorting. As such when the end user reloads
+ * the page the display display will match what thy had previously set up.
+ *
+ * Due to the use of `localStorage` the default state saving is not supported
+ * in IE6 or 7. If state saving is required in those browsers, use
+ * `stateSaveCallback` to provide a storage solution such as cookies.
* @type boolean
* @default false
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.stateSave
+ *
* @example
* $(document).ready( function () {
* $('#example').dataTable( {
- * "bStateSave": true
+ * "stateSave": true
* } );
* } );
*/
@@ -8416,51 +10556,25 @@
/**
- * Customise the cookie and / or the parameters being stored when using
- * DataTables with state saving enabled. This function is called whenever
- * the cookie is modified, and it expects a fully formed cookie string to be
- * returned. Note that the data object passed in is a Javascript object which
- * must be converted to a string (JSON.stringify for example).
- * @type function
- * @param {string} sName Name of the cookie defined by DataTables
- * @param {object} oData Data to be stored in the cookie
- * @param {string} sExpires Cookie expires string
- * @param {string} sPath Path of the cookie to set
- * @returns {string} Cookie formatted string (which should be encoded by
- * using encodeURIComponent())
- * @dtopt Callbacks
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "fnCookieCallback": function (sName, oData, sExpires, sPath) {
- * // Customise oData or sName or whatever else here
- * return sName + "="+JSON.stringify(oData)+"; expires=" + sExpires +"; path=" + sPath;
- * }
- * } );
- * } );
- */
- "fnCookieCallback": null,
-
-
- /**
* This function is called when a TR element is created (and all TD child
* elements have been inserted), or registered if using a DOM source, allowing
* manipulation of the TR element (adding classes etc).
* @type function
- * @param {node} nRow "TR" element for the current row
- * @param {array} aData Raw data array for this row
- * @param {int} iDataIndex The index of this row in aoData
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} dataIndex The index of this row in the internal aoData array
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.createdRow
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnCreatedRow": function( nRow, aData, iDataIndex ) {
+ * "createdRow": function( row, data, dataIndex ) {
* // Bold the grade for all 'A' grade browsers
- * if ( aData[4] == "A" )
+ * if ( data[4] == "A" )
* {
- * $('td:eq(4)', nRow).html( '<b>A</b>' );
+ * $('td:eq(4)', row).html( '<b>A</b>' );
* }
* }
* } );
@@ -8473,13 +10587,15 @@
* This function is called on every 'draw' event, and allows you to
* dynamically modify any aspect you want about the created DOM.
* @type function
- * @param {object} oSettings DataTables settings object
+ * @param {object} settings DataTables settings object
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.drawCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnDrawCallback": function( oSettings ) {
+ * "drawCallback": function( settings ) {
* alert( 'DataTables has redrawn the table' );
* }
* } );
@@ -8490,23 +10606,25 @@
/**
* Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' even.
+ * allows you to modify the table footer on every 'draw' event.
* @type function
- * @param {node} nFoot "TR" element for the footer
- * @param {array} aData Full table data (as derived from the original HTML)
- * @param {int} iStart Index for the current display starting point in the
+ * @param {node} foot "TR" element for the footer
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
* display array
- * @param {int} iEnd Index for the current display ending point in the
+ * @param {int} end Index for the current display ending point in the
* display array
- * @param {array int} aiDisplay Index array to translate the visual position
+ * @param {array int} display Index array to translate the visual position
* to the full data array
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.footerCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnFooterCallback": function( nFoot, aData, iStart, iEnd, aiDisplay ) {
- * nFoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+iStart;
+ * "footerCallback": function( tfoot, data, start, end, display ) {
+ * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
* }
* } );
* } )
@@ -8522,52 +10640,30 @@
* function will override the default method DataTables uses.
* @type function
* @member
- * @param {int} iIn number to be formatted
+ * @param {int} toFormat number to be formatted
* @returns {string} formatted string for DataTables to show the number
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.formatNumber
+ *
* @example
+ * // Format a number using a single quote for the separator (note that
+ * // this can also be done with the language.thousands option)
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnFormatNumber": function ( iIn ) {
- * if ( iIn &lt; 1000 ) {
- * return iIn;
- * } else {
- * var
- * s=(iIn+""),
- * a=s.split(""), out="",
- * iLen=s.length;
- *
- * for ( var i=0 ; i&lt;iLen ; i++ ) {
- * if ( i%3 === 0 &amp;&amp; i !== 0 ) {
- * out = "'"+out;
- * }
- * out = a[iLen-i-1]+out;
- * }
- * }
- * return out;
+ * "formatNumber": function ( toFormat ) {
+ * return toFormat.toString().replace(
+ * /\B(?=(\d{3})+(?!\d))/g, "'"
+ * );
* };
* } );
* } );
*/
- "fnFormatNumber": function ( iIn ) {
- if ( iIn < 1000 )
- {
- // A small optimisation for what is likely to be the majority of use cases
- return iIn;
- }
-
- var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
-
- for ( var i=0 ; i<iLen ; i++ )
- {
- if ( i%3 === 0 && i !== 0 )
- {
- out = this.oLanguage.sInfoThousands+out;
- }
- out = a[iLen-i-1]+out;
- }
- return out;
+ "fnFormatNumber": function ( toFormat ) {
+ return toFormat.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g,
+ this.oLanguage.sThousands
+ );
},
@@ -8576,21 +10672,23 @@
* dynamically modify the header row. This can be used to calculate and
* display useful information about the table.
* @type function
- * @param {node} nHead "TR" element for the header
- * @param {array} aData Full table data (as derived from the original HTML)
- * @param {int} iStart Index for the current display starting point in the
+ * @param {node} head "TR" element for the header
+ * @param {array} data Full table data (as derived from the original HTML)
+ * @param {int} start Index for the current display starting point in the
* display array
- * @param {int} iEnd Index for the current display ending point in the
+ * @param {int} end Index for the current display ending point in the
* display array
- * @param {array int} aiDisplay Index array to translate the visual position
+ * @param {array int} display Index array to translate the visual position
* to the full data array
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.headerCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnHeaderCallback": function( nHead, aData, iStart, iEnd, aiDisplay ) {
- * nHead.getElementsByTagName('th')[0].innerHTML = "Displaying "+(iEnd-iStart)+" records";
+ * "fheaderCallback": function( head, data, start, end, display ) {
+ * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
* }
* } );
* } )
@@ -8606,20 +10704,22 @@
* allows you to do exactly that.
* @type function
* @param {object} oSettings DataTables settings object
- * @param {int} iStart Starting position in data for the draw
- * @param {int} iEnd End position in data for the draw
- * @param {int} iMax Total number of rows in the table (regardless of
+ * @param {int} start Starting position in data for the draw
+ * @param {int} end End position in data for the draw
+ * @param {int} max Total number of rows in the table (regardless of
* filtering)
- * @param {int} iTotal Total number of rows in the data set, after filtering
- * @param {string} sPre The string that DataTables has formatted using it's
+ * @param {int} total Total number of rows in the data set, after filtering
+ * @param {string} pre The string that DataTables has formatted using it's
* own rules
* @returns {string} The string to be displayed in the information element.
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.infoCallback
+ *
* @example
* $('#example').dataTable( {
- * "fnInfoCallback": function( oSettings, iStart, iEnd, iMax, iTotal, sPre ) {
- * return iStart +" to "+ iEnd;
+ * "infoCallback": function( settings, start, end, max, total, pre ) {
+ * return start +" to "+ end;
* }
* } );
*/
@@ -8632,15 +10732,17 @@
* however, this does not hold true when using external language information
* since that is obtained using an async XHR call.
* @type function
- * @param {object} oSettings DataTables settings object
+ * @param {object} settings DataTables settings object
* @param {object} json The JSON object request from the server - only
* present if client-side Ajax sourced data is used
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.initComplete
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnInitComplete": function(oSettings, json) {
+ * "initComplete": function(settings, json) {
* alert( 'DataTables has finished its initialisation.' );
* }
* } );
@@ -8654,15 +10756,17 @@
* draw by returning false, any other return (including undefined) results in
* the full draw occurring).
* @type function
- * @param {object} oSettings DataTables settings object
+ * @param {object} settings DataTables settings object
* @returns {boolean} False will cancel the draw, anything else (including no
* return) will allow it to complete.
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.preDrawCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnPreDrawCallback": function( oSettings ) {
+ * "preDrawCallback": function( settings ) {
* if ( $('#test').val() == 1 ) {
* return false;
* }
@@ -8678,21 +10782,22 @@
* generated for each table draw, but before it is rendered on screen. This
* function might be used for setting the row class name etc.
* @type function
- * @param {node} nRow "TR" element for the current row
- * @param {array} aData Raw data array for this row
- * @param {int} iDisplayIndex The display index for the current table draw
- * @param {int} iDisplayIndexFull The index of the data in the full list of
+ * @param {node} row "TR" element for the current row
+ * @param {array} data Raw data array for this row
+ * @param {int} displayIndex The display index for the current table draw
+ * @param {int} displayIndexFull The index of the data in the full list of
* rows (after filtering)
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.rowCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
+ * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
* // Bold the grade for all 'A' grade browsers
- * if ( aData[4] == "A" )
- * {
- * $('td:eq(4)', nRow).html( '<b>A</b>' );
+ * if ( data[4] == "A" ) {
+ * $('td:eq(4)', row).html( '<b>A</b>' );
* }
* }
* } );
@@ -8702,114 +10807,77 @@
/**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
* This parameter allows you to override the default function which obtains
- * the data from the server ($.getJSON) so something more suitable for your
- * application. For example you could use POST data, or pull information from
- * a Gears or AIR database.
+ * the data from the server so something more suitable for your application.
+ * For example you could use POST data, or pull information from a Gears or
+ * AIR database.
* @type function
* @member
- * @param {string} sSource HTTP source to obtain the data from (sAjaxSource)
- * @param {array} aoData A key/value pair object containing the data to send
+ * @param {string} source HTTP source to obtain the data from (`ajax`)
+ * @param {array} data A key/value pair object containing the data to send
* to the server
- * @param {function} fnCallback to be called on completion of the data get
+ * @param {function} callback to be called on completion of the data get
* process that will draw the data on the page.
- * @param {object} oSettings DataTables settings object
+ * @param {object} settings DataTables settings object
+ *
* @dtopt Callbacks
* @dtopt Server-side
- *
- * @example
- * // POST data to server
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "bProcessing": true,
- * "bServerSide": true,
- * "sAjaxSource": "xhr.php",
- * "fnServerData": function ( sSource, aoData, fnCallback, oSettings ) {
- * oSettings.jqXHR = $.ajax( {
- * "dataType": 'json',
- * "type": "POST",
- * "url": sSource,
- * "data": aoData,
- * "success": fnCallback
- * } );
- * }
- * } );
- * } );
+ * @name DataTable.defaults.serverData
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
*/
- "fnServerData": function ( sUrl, aoData, fnCallback, oSettings ) {
- oSettings.jqXHR = $.ajax( {
- "url": sUrl,
- "data": aoData,
- "success": function (json) {
- if ( json.sError ) {
- oSettings.oApi._fnLog( oSettings, 0, json.sError );
- }
-
- $(oSettings.oInstance).trigger('xhr', [oSettings, json]);
- fnCallback( json );
- },
- "dataType": "json",
- "cache": false,
- "type": oSettings.sServerMethod,
- "error": function (xhr, error, thrown) {
- if ( error == "parsererror" ) {
- oSettings.oApi._fnLog( oSettings, 0, "DataTables warning: JSON data from "+
- "server could not be parsed. This is caused by a JSON formatting error." );
- }
- }
- } );
- },
+ "fnServerData": null,
/**
- * It is often useful to send extra data to the server when making an Ajax
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * It is often useful to send extra data to the server when making an Ajax
* request - for example custom filtering information, and this callback
* function makes it trivial to send extra information to the server. The
* passed in parameter is the data set that has been constructed by
* DataTables, and you can add to this or modify it as you require.
* @type function
- * @param {array} aoData Data array (array of objects which are name/value
+ * @param {array} data Data array (array of objects which are name/value
* pairs) that has been constructed by DataTables and will be sent to the
* server. In the case of Ajax sourced data with server-side processing
* this will be an empty array, for server-side processing there will be a
* significant number of parameters!
- * @returns {undefined} Ensure that you modify the aoData array passed in,
+ * @returns {undefined} Ensure that you modify the data array passed in,
* as this is passed by reference.
+ *
* @dtopt Callbacks
* @dtopt Server-side
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "bProcessing": true,
- * "bServerSide": true,
- * "sAjaxSource": "scripts/server_processing.php",
- * "fnServerParams": function ( aoData ) {
- * aoData.push( { "name": "more_data", "value": "my_value" } );
- * }
- * } );
- * } );
+ * @name DataTable.defaults.serverParams
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
*/
"fnServerParams": null,
/**
* Load the table state. With this function you can define from where, and how, the
- * state of a table is loaded. By default DataTables will load from its state saving
- * cookie, but you might wish to use local storage (HTML5) or a server-side database.
+ * state of a table is loaded. By default DataTables will load from `localStorage`
+ * but you might wish to use a server-side database or cookies.
* @type function
* @member
- * @param {object} oSettings DataTables settings object
+ * @param {object} settings DataTables settings object
* @return {object} The DataTables state object to be loaded
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.stateLoadCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateLoad": function (oSettings) {
+ * "stateSave": true,
+ * "stateLoadCallback": function (settings) {
* var o;
- *
+ *
* // Send an Ajax request to the server to get the data. Note that
* // this is a synchronous request.
* $.ajax( {
@@ -8820,55 +10888,53 @@
* o = json;
* }
* } );
- *
+ *
* return o;
* }
* } );
* } );
*/
- "fnStateLoad": function ( oSettings ) {
- var sData = this.oApi._fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
- var oData;
-
+ "fnStateLoadCallback": function ( settings ) {
try {
- oData = (typeof $.parseJSON === 'function') ?
- $.parseJSON(sData) : eval( '('+sData+')' );
- } catch (e) {
- oData = null;
- }
-
- return oData;
+ return JSON.parse(
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname
+ )
+ );
+ } catch (e) {}
},
/**
* Callback which allows modification of the saved state prior to loading that state.
* This callback is called when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state. Note that for
- * plug-in authors, you should use the 'stateLoadParams' event to load parameters for
+ * prior to the settings object being modified by the saved state. Note that for
+ * plug-in authors, you should use the `stateLoadParams` event to load parameters for
* a plug-in.
* @type function
- * @param {object} oSettings DataTables settings object
- * @param {object} oData The state object that is to be loaded
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that is to be loaded
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.stateLoadParams
+ *
* @example
* // Remove a saved filter, so filtering is never loaded
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateLoadParams": function (oSettings, oData) {
- * oData.oSearch.sSearch = "";
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
* }
* } );
* } );
- *
+ *
* @example
* // Disallow state loading by returning false
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateLoadParams": function (oSettings, oData) {
+ * "stateSave": true,
+ * "stateLoadParams": function (settings, data) {
* return false;
* }
* } );
@@ -8881,17 +10947,19 @@
* Callback that is called when the state has been loaded from the state saving method
* and the DataTables settings object has been modified as a result of the loaded state.
* @type function
- * @param {object} oSettings DataTables settings object
- * @param {object} oData The state object that was loaded
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object that was loaded
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.stateLoaded
+ *
* @example
* // Show an alert with the filtering value that was saved
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateLoaded": function (oSettings, oData) {
- * alert( 'Saved filter was: '+oData.oSearch.sSearch );
+ * "stateSave": true,
+ * "stateLoaded": function (settings, data) {
+ * alert( 'Saved filter was: '+data.oSearch.sSearch );
* }
* } );
* } );
@@ -8901,23 +10969,25 @@
/**
* Save the table state. This function allows you to define where and how the state
- * information for the table is stored - by default it will use a cookie, but you
- * might want to use local storage (HTML5) or a server-side database.
+ * information for the table is stored By default DataTables will use `localStorage`
+ * but you might wish to use a server-side database or cookies.
* @type function
* @member
- * @param {object} oSettings DataTables settings object
- * @param {object} oData The state object to be saved
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.stateSaveCallback
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateSave": function (oSettings, oData) {
+ * "stateSave": true,
+ * "stateSaveCallback": function (settings, data) {
* // Send an Ajax request to the server with the state object
* $.ajax( {
* "url": "/state_save",
- * "data": oData,
+ * "data": data,
* "dataType": "json",
* "method": "POST"
* "success": function () {}
@@ -8926,35 +10996,36 @@
* } );
* } );
*/
- "fnStateSave": function ( oSettings, oData ) {
- this.oApi._fnCreateCookie(
- oSettings.sCookiePrefix+oSettings.sInstance,
- this.oApi._fnJsonString(oData),
- oSettings.iCookieDuration,
- oSettings.sCookiePrefix,
- oSettings.fnCookieCallback
- );
+ "fnStateSaveCallback": function ( settings, data ) {
+ try {
+ (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
+ 'DataTables_'+settings.sInstance+'_'+location.pathname,
+ JSON.stringify( data )
+ );
+ } catch (e) {}
},
/**
- * Callback which allows modification of the state to be saved. Called when the table
+ * Callback which allows modification of the state to be saved. Called when the table
* has changed state a new state save is required. This method allows modification of
- * the state saving object prior to actually doing the save, including addition or
- * other state properties or modification. Note that for plug-in authors, you should
- * use the 'stateSaveParams' event to save parameters for a plug-in.
+ * the state saving object prior to actually doing the save, including addition or
+ * other state properties or modification. Note that for plug-in authors, you should
+ * use the `stateSaveParams` event to save parameters for a plug-in.
* @type function
- * @param {object} oSettings DataTables settings object
- * @param {object} oData The state object to be saved
+ * @param {object} settings DataTables settings object
+ * @param {object} data The state object to be saved
+ *
* @dtopt Callbacks
- *
+ * @name DataTable.defaults.stateSaveParams
+ *
* @example
* // Remove a saved filter, so filtering is never saved
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bStateSave": true,
- * "fnStateSaveParams": function (oSettings, oData) {
- * oData.oSearch.sSearch = "";
+ * "stateSave": true,
+ * "stateSaveParams": function (settings, data) {
+ * data.oSearch.sSearch = "";
* }
* } );
* } );
@@ -8963,26 +11034,29 @@
/**
- * Duration of the cookie which is used for storing session information. This
- * value is given in seconds.
+ * Duration for which the saved state information is considered valid. After this period
+ * has elapsed the state will be returned to the default.
+ * Value is given in seconds.
* @type int
* @default 7200 <i>(2 hours)</i>
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.stateDuration
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "iCookieDuration": 60*60*24; // 1 day
+ * "stateDuration": 60*60*24; // 1 day
* } );
* } )
*/
- "iCookieDuration": 7200,
+ "iStateDuration": 7200,
/**
* When enabled DataTables will not make a request to the server for the first
* page draw - rather it will use the data already on the page (no sorting etc
- * will be applied to it), thus saving on an XHR at load time. iDeferLoading
+ * will be applied to it), thus saving on an XHR at load time. `deferLoading`
* is used to indicate that deferred loading is required, but it is also used
* to tell DataTables how many records there are in the full table (allowing
* the information element and pagination to be displayed correctly). In the case
@@ -8993,27 +11067,29 @@
* to be shown correctly).
* @type int | array
* @default null
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.deferLoading
+ *
* @example
* // 57 records available in the table, no filtering applied
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bServerSide": true,
- * "sAjaxSource": "scripts/server_processing.php",
- * "iDeferLoading": 57
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": 57
* } );
* } );
- *
+ *
* @example
* // 57 records after filtering, 100 without filtering (an initial filter applied)
* $(document).ready( function() {
* $('#example').dataTable( {
- * "bServerSide": true,
- * "sAjaxSource": "scripts/server_processing.php",
- * "iDeferLoading": [ 57, 100 ],
- * "oSearch": {
- * "sSearch": "my_filter"
+ * "serverSide": true,
+ * "ajax": "scripts/server_processing.php",
+ * "deferLoading": [ 57, 100 ],
+ * "search": {
+ * "search": "my_filter"
* }
* } );
* } );
@@ -9023,16 +11099,18 @@
/**
* Number of rows to display on a single page when using pagination. If
- * feature enabled (bLengthChange) then the end user will be able to override
+ * feature enabled (`lengthChange`) then the end user will be able to override
* this to a custom setting using a pop-up menu.
* @type int
* @default 10
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.pageLength
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "iDisplayLength": 50
+ * "pageLength": 50
* } );
* } )
*/
@@ -9046,12 +11124,14 @@
* the third page, it should be "20".
* @type int
* @default 0
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.displayStart
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "iDisplayStart": 20
+ * "displayStart": 20
* } );
* } )
*/
@@ -9059,42 +11139,22 @@
/**
- * The scroll gap is the amount of scrolling that is left to go before
- * DataTables will load the next 'page' of data automatically. You typically
- * want a gap which is big enough that the scrolling will be smooth for the
- * user, while not so large that it will load more data than need.
- * @type int
- * @default 100
- * @dtopt Options
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "bScrollInfinite": true,
- * "bScrollCollapse": true,
- * "sScrollY": "200px",
- * "iScrollLoadGap": 50
- * } );
- * } );
- */
- "iScrollLoadGap": 100,
-
-
- /**
* By default DataTables allows keyboard navigation of the table (sorting, paging,
- * and filtering) by adding a tabindex attribute to the required elements. This
+ * and filtering) by adding a `tabindex` attribute to the required elements. This
* allows you to tab through the controls and press the enter key to activate them.
* The tabindex is default 0, meaning that the tab follows the flow of the document.
* You can overrule this using this parameter if you wish. Use a value of -1 to
* disable built-in keyboard navigation.
* @type int
* @default 0
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.tabIndex
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "iTabIndex": 1
+ * "tabIndex": 1
* } );
* } );
*/
@@ -9102,10 +11162,22 @@
/**
+ * Classes that DataTables assigns to the various components and features
+ * that it adds to the HTML table. This allows classes to be configured
+ * during initialisation in addition to through the static
+ * {@link DataTable.ext.oStdClasses} object).
+ * @namespace
+ * @name DataTable.defaults.classes
+ */
+ "oClasses": {},
+
+
+ /**
* All strings that DataTables uses in the user interface that it creates
* are defined in this object, allowing you to modified them individually or
* completely replace them all as required.
* @namespace
+ * @name DataTable.defaults.language
*/
"oLanguage": {
/**
@@ -9113,6 +11185,7 @@
* actually visible on the page, but will be read by screenreaders, and thus
* must be internationalised as well).
* @namespace
+ * @name DataTable.defaults.language.aria
*/
"oAria": {
/**
@@ -9121,14 +11194,16 @@
* Note that the column header is prefixed to this string.
* @type string
* @default : activate to sort column ascending
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.aria.sortAscending
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oAria": {
- * "sSortAscending": " - click/return to sort ascending"
+ * "language": {
+ * "aria": {
+ * "sortAscending": " - click/return to sort ascending"
* }
* }
* } );
@@ -9142,14 +11217,16 @@
* Note that the column header is prefixed to this string.
* @type string
* @default : activate to sort column ascending
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.aria.sortDescending
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oAria": {
- * "sSortDescending": " - click/return to sort descending"
+ * "language": {
+ * "aria": {
+ * "sortDescending": " - click/return to sort descending"
* }
* }
* } );
@@ -9159,9 +11236,10 @@
},
/**
- * Pagination string used by DataTables for the two built-in pagination
- * control types ("two_button" and "full_numbers")
+ * Pagination string used by DataTables for the built-in pagination
+ * control types.
* @namespace
+ * @name DataTable.defaults.language.paginate
*/
"oPaginate": {
/**
@@ -9169,77 +11247,85 @@
* button to take the user to the first page.
* @type string
* @default First
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.paginate.first
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oPaginate": {
- * "sFirst": "First page"
+ * "language": {
+ * "paginate": {
+ * "first": "First page"
* }
* }
* } );
* } );
*/
"sFirst": "First",
-
-
+
+
/**
* Text to use when using the 'full_numbers' type of pagination for the
* button to take the user to the last page.
* @type string
* @default Last
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.paginate.last
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oPaginate": {
- * "sLast": "Last page"
+ * "language": {
+ * "paginate": {
+ * "last": "Last page"
* }
* }
* } );
* } );
*/
"sLast": "Last",
-
-
+
+
/**
- * Text to use for the 'next' pagination button (to take the user to the
+ * Text to use for the 'next' pagination button (to take the user to the
* next page).
* @type string
* @default Next
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.paginate.next
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oPaginate": {
- * "sNext": "Next page"
+ * "language": {
+ * "paginate": {
+ * "next": "Next page"
* }
* }
* } );
* } );
*/
"sNext": "Next",
-
-
+
+
/**
- * Text to use for the 'previous' pagination button (to take the user to
+ * Text to use for the 'previous' pagination button (to take the user to
* the previous page).
* @type string
* @default Previous
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.paginate.previous
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "oPaginate": {
- * "sPrevious": "Previous page"
+ * "language": {
+ * "paginate": {
+ * "previous": "Previous page"
* }
* }
* } );
@@ -9247,130 +11333,182 @@
*/
"sPrevious": "Previous"
},
-
+
/**
- * This string is shown in preference to sZeroRecords when the table is
+ * This string is shown in preference to `zeroRecords` when the table is
* empty of data (regardless of filtering). Note that this is an optional
- * parameter - if it is not given, the value of sZeroRecords will be used
+ * parameter - if it is not given, the value of `zeroRecords` will be used
* instead (either the default or given value).
* @type string
* @default No data available in table
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.emptyTable
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sEmptyTable": "No data available in table"
+ * "language": {
+ * "emptyTable": "No data available in table"
* }
* } );
* } );
*/
"sEmptyTable": "No data available in table",
-
-
+
+
/**
- * This string gives information to the end user about the information that
- * is current on display on the page. The _START_, _END_ and _TOTAL_
- * variables are all dynamically replaced as the table display updates, and
- * can be freely moved or removed as the language requirements change.
+ * This string gives information to the end user about the information
+ * that is current on display on the page. The following tokens can be
+ * used in the string and will be dynamically replaced as the table
+ * display updates. This tokens can be placed anywhere in the string, or
+ * removed as needed by the language requires:
+ *
+ * * `\_START\_` - Display index of the first record on the current page
+ * * `\_END\_` - Display index of the last record on the current page
+ * * `\_TOTAL\_` - Number of records in the table after filtering
+ * * `\_MAX\_` - Number of records in the table without filtering
+ * * `\_PAGE\_` - Current page number
+ * * `\_PAGES\_` - Total number of pages of data in the table
+ *
* @type string
* @default Showing _START_ to _END_ of _TOTAL_ entries
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.info
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sInfo": "Got a total of _TOTAL_ entries to show (_START_ to _END_)"
+ * "language": {
+ * "info": "Showing page _PAGE_ of _PAGES_"
* }
* } );
* } );
*/
"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
-
-
+
+
/**
- * Display information string for when the table is empty. Typically the
- * format of this string should match sInfo.
+ * Display information string for when the table is empty. Typically the
+ * format of this string should match `info`.
* @type string
* @default Showing 0 to 0 of 0 entries
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.infoEmpty
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sInfoEmpty": "No entries to show"
+ * "language": {
+ * "infoEmpty": "No entries to show"
* }
* } );
* } );
*/
"sInfoEmpty": "Showing 0 to 0 of 0 entries",
-
-
+
+
/**
- * When a user filters the information in a table, this string is appended
- * to the information (sInfo) to give an idea of how strong the filtering
+ * When a user filters the information in a table, this string is appended
+ * to the information (`info`) to give an idea of how strong the filtering
* is. The variable _MAX_ is dynamically updated.
* @type string
* @default (filtered from _MAX_ total entries)
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.infoFiltered
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sInfoFiltered": " - filtering from _MAX_ records"
+ * "language": {
+ * "infoFiltered": " - filtering from _MAX_ records"
* }
* } );
* } );
*/
"sInfoFiltered": "(filtered from _MAX_ total entries)",
-
-
+
+
/**
* If can be useful to append extra information to the info string at times,
* and this variable does exactly that. This information will be appended to
- * the sInfo (sInfoEmpty and sInfoFiltered in whatever combination they are
+ * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
* being used) at all times.
* @type string
* @default <i>Empty string</i>
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.infoPostFix
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sInfoPostFix": "All records shown are derived from real information."
+ * "language": {
+ * "infoPostFix": "All records shown are derived from real information."
* }
* } );
* } );
*/
"sInfoPostFix": "",
-
-
+
+
/**
- * DataTables has a build in number formatter (fnFormatNumber) which is used
- * to format large numbers that are used in the table information. By
- * default a comma is used, but this can be trivially changed to any
+ * This decimal place operator is a little different from the other
+ * language options since DataTables doesn't output floating point
+ * numbers, so it won't ever use this for display of a number. Rather,
+ * what this parameter does is modify the sort methods of the table so
+ * that numbers which are in a format which has a character other than
+ * a period (`.`) as a decimal place will be sorted numerically.
+ *
+ * Note that numbers with different decimal places cannot be shown in
+ * the same table and still be sortable, the table must be consistent.
+ * However, multiple different tables on the page can use different
+ * decimal place characters.
+ * @type string
+ * @default
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.decimal
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "language": {
+ * "decimal": ","
+ * "thousands": "."
+ * }
+ * } );
+ * } );
+ */
+ "sDecimal": "",
+
+
+ /**
+ * DataTables has a build in number formatter (`formatNumber`) which is
+ * used to format large numbers that are used in the table information.
+ * By default a comma is used, but this can be trivially changed to any
* character you wish with this parameter.
* @type string
* @default ,
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.thousands
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sInfoThousands": "'"
+ * "language": {
+ * "thousands": "'"
* }
* } );
* } );
*/
- "sInfoThousands": ",",
-
-
+ "sThousands": ",",
+
+
/**
* Detail the action that will be taken when the drop down menu for the
* pagination length option is changed. The '_MENU_' variable is replaced
@@ -9378,24 +11516,26 @@
* with a custom select box if required.
* @type string
* @default Show _MENU_ entries
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.lengthMenu
+ *
* @example
* // Language change only
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sLengthMenu": "Display _MENU_ records"
+ * "language": {
+ * "lengthMenu": "Display _MENU_ records"
* }
* } );
* } );
- *
+ *
* @example
* // Language and options change
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sLengthMenu": 'Display <select>'+
+ * "language": {
+ * "lengthMenu": 'Display <select>'+
* '<option value="10">10</option>'+
* '<option value="20">20</option>'+
* '<option value="30">30</option>'+
@@ -9408,8 +11548,8 @@
* } );
*/
"sLengthMenu": "Show _MENU_ entries",
-
-
+
+
/**
* When using Ajax sourced data and during the first draw when DataTables is
* gathering the data, this message is shown in an empty row in the table to
@@ -9418,39 +11558,43 @@
* Ajax sourced data with client-side processing.
* @type string
* @default Loading...
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.loadingRecords
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sLoadingRecords": "Please wait - loading..."
+ * "language": {
+ * "loadingRecords": "Please wait - loading..."
* }
* } );
* } );
*/
"sLoadingRecords": "Loading...",
-
-
+
+
/**
* Text which is displayed when the table is processing a user action
* (usually a sort command or similar).
* @type string
* @default Processing...
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.processing
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sProcessing": "DataTables is currently busy"
+ * "language": {
+ * "processing": "DataTables is currently busy"
* }
* } );
* } );
*/
"sProcessing": "Processing...",
-
-
+
+
/**
* Details the actions that will be taken when the user types into the
* filtering input text box. The variable "_INPUT_", if used in the string,
@@ -9459,31 +11603,44 @@
* then the input box is appended to the string automatically.
* @type string
* @default Search:
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.search
+ *
* @example
* // Input text box will be appended at the end automatically
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sSearch": "Filter records:"
+ * "language": {
+ * "search": "Filter records:"
* }
* } );
* } );
- *
+ *
* @example
* // Specify where the filter should appear
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sSearch": "Apply filter _INPUT_ to table"
+ * "language": {
+ * "search": "Apply filter _INPUT_ to table"
* }
* } );
* } );
*/
"sSearch": "Search:",
-
-
+
+
+ /**
+ * Assign a `placeholder` attribute to the search `input` element
+ * @type string
+ * @default
+ *
+ * @dtopt Language
+ * @name DataTable.defaults.language.searchPlaceholder
+ */
+ "sSearchPlaceholder": "",
+
+
/**
* All of the language information can be stored in a file on the
* server-side, which DataTables will look up if this parameter is passed.
@@ -9493,33 +11650,37 @@
* the example language files to see how this works in action.
* @type string
* @default <i>Empty string - i.e. disabled</i>
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.url
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sUrl": "http://www.sprymedia.co.uk/dataTables/lang.txt"
+ * "language": {
+ * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
* }
* } );
* } );
*/
"sUrl": "",
-
-
+
+
/**
* Text shown inside the table records when the is no information to be
- * displayed after filtering. sEmptyTable is shown when there is simply no
+ * displayed after filtering. `emptyTable` is shown when there is simply no
* information in the table at all (regardless of filtering).
* @type string
* @default No matching records found
+ *
* @dtopt Language
- *
+ * @name DataTable.defaults.language.zeroRecords
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oLanguage": {
- * "sZeroRecords": "No records to display"
+ * "language": {
+ * "zeroRecords": "No records to display"
* }
* } );
* } );
@@ -9530,20 +11691,22 @@
/**
* This parameter allows you to have define the global filtering state at
- * initialisation time. As an object the "sSearch" parameter must be
- * defined, but all other parameters are optional. When "bRegex" is true,
+ * initialisation time. As an object the `search` parameter must be
+ * defined, but all other parameters are optional. When `regex` is true,
* the search string will be treated as a regular expression, when false
- * (default) it will be treated as a straight string. When "bSmart"
+ * (default) it will be treated as a straight string. When `smart`
* DataTables will use it's smart filtering methods (to word match at
* any point in the data), when false this will not be done.
* @namespace
* @extends DataTable.models.oSearch
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.search
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "oSearch": {"sSearch": "Initial search"}
+ * "search": {"search": "Initial search"}
* } );
* } )
*/
@@ -9551,83 +11714,55 @@
/**
- * By default DataTables will look for the property 'aaData' when obtaining
- * data from an Ajax source or for server-side processing - this parameter
- * allows that property to be changed. You can use Javascript dotted object
- * notation to get a data source for multiple levels of nesting.
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * By default DataTables will look for the property `data` (or `aaData` for
+ * compatibility with DataTables 1.9-) when obtaining data from an Ajax
+ * source or for server-side processing - this parameter allows that
+ * property to be changed. You can use Javascript dotted object notation to
+ * get a data source for multiple levels of nesting.
* @type string
- * @default aaData
+ * @default data
+ *
* @dtopt Options
* @dtopt Server-side
- *
- * @example
- * // Get data from { "data": [...] }
- * $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "sAjaxSource": "sources/data.txt",
- * "sAjaxDataProp": "data"
- * } );
- * } );
- *
- * @example
- * // Get data from { "data": { "inner": [...] } }
- * $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "sAjaxSource": "sources/data.txt",
- * "sAjaxDataProp": "data.inner"
- * } );
- * } );
+ * @name DataTable.defaults.ajaxDataProp
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
*/
- "sAjaxDataProp": "aaData",
+ "sAjaxDataProp": "data",
/**
- * You can instruct DataTables to load data from an external source using this
- * parameter (use aData if you want to pass data in you already have). Simply
- * provide a url a JSON object can be obtained from. This object must include
- * the parameter 'aaData' which is the data source for the table.
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
+ * You can instruct DataTables to load data from an external
+ * source using this parameter (use aData if you want to pass data in you
+ * already have). Simply provide a url a JSON object can be obtained from.
* @type string
* @default null
+ *
* @dtopt Options
* @dtopt Server-side
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "sAjaxSource": "http://www.sprymedia.co.uk/dataTables/json.php"
- * } );
- * } )
+ * @name DataTable.defaults.ajaxSource
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
*/
"sAjaxSource": null,
/**
- * This parameter can be used to override the default prefix that DataTables
- * assigns to a cookie when state saving is enabled.
- * @type string
- * @default SpryMedia_DataTables_
- * @dtopt Options
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "sCookiePrefix": "my_datatable_",
- * } );
- * } );
- */
- "sCookiePrefix": "SpryMedia_DataTables_",
-
-
- /**
* This initialisation variable allows you to specify exactly where in the
* DOM you want DataTables to inject the various controls it adds to the page
* (for example you might want the pagination controls at the top of the
* table). DIV elements (with or without a custom class) can also be added to
* aid styling. The follow syntax is used:
* <ul>
- * <li>The following options are allowed:
+ * <li>The following options are allowed:
* <ul>
- * <li>'l' - Length changing</li
+ * <li>'l' - Length changing</li>
* <li>'f' - Filtering input</li>
* <li>'t' - The table!</li>
* <li>'i' - Information</li>
@@ -9656,14 +11791,16 @@
* </li>
* </ul>
* @type string
- * @default lfrtip <i>(when bJQueryUI is false)</i> <b>or</b>
- * <"H"lfr>t<"F"ip> <i>(when bJQueryUI is true)</i>
+ * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
+ * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.dom
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sDom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
+ * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
* } );
* } );
*/
@@ -9671,38 +11808,71 @@
/**
- * DataTables features two different built-in pagination interaction methods
- * ('two_button' or 'full_numbers') which present different page controls to
- * the end user. Further methods can be added using the API (see below).
- * @type string
- * @default two_button
+ * Search delay option. This will throttle full table searches that use the
+ * DataTables provided search input element (it does not effect calls to
+ * `dt-api search()`, providing a delay before the search is made.
+ * @type integer
+ * @default 0
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.searchDelay
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sPaginationType": "full_numbers"
+ * "searchDelay": 200
* } );
* } )
*/
- "sPaginationType": "two_button",
+ "searchDelay": null,
/**
- * Enable horizontal scrolling. When a table is too wide to fit into a certain
- * layout, or you have a large number of columns in the table, you can enable
- * x-scrolling to show the table in a viewport, which can be scrolled. This
- * property can be any CSS unit, or a number (in which case it will be treated
- * as a pixel measurement).
+ * DataTables features four different built-in options for the buttons to
+ * display for pagination control:
+ *
+ * * `simple` - 'Previous' and 'Next' buttons only
+ * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
+ * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
+ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
+ * page numbers
+ *
+ * Further methods can be added using {@link DataTable.ext.oPagination}.
* @type string
+ * @default simple_numbers
+ *
+ * @dtopt Options
+ * @name DataTable.defaults.pagingType
+ *
+ * @example
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "pagingType": "full_numbers"
+ * } );
+ * } )
+ */
+ "sPaginationType": "simple_numbers",
+
+
+ /**
+ * Enable horizontal scrolling. When a table is too wide to fit into a
+ * certain layout, or you have a large number of columns in the table, you
+ * can enable x-scrolling to show the table in a viewport, which can be
+ * scrolled. This property can be `true` which will allow the table to
+ * scroll horizontally when needed, or any CSS unit, or a number (in which
+ * case it will be treated as a pixel measurement). Setting as simply `true`
+ * is recommended.
+ * @type boolean|string
* @default <i>blank string - i.e. disabled</i>
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.scrollX
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sScrollX": "100%",
- * "bScrollCollapse": true
+ * "scrollX": true,
+ * "scrollCollapse": true
* } );
* } );
*/
@@ -9718,13 +11888,15 @@
* measurement).
* @type string
* @default <i>blank string - i.e. disabled</i>
+ *
* @dtopt Options
- *
+ * @name DataTable.defaults.scrollXInner
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sScrollX": "100%",
- * "sScrollXInner": "110%"
+ * "scrollX": "100%",
+ * "scrollXInner": "110%"
* } );
* } );
*/
@@ -9740,13 +11912,15 @@
* (in which case it will be treated as a pixel measurement).
* @type string
* @default <i>blank string - i.e. disabled</i>
+ *
* @dtopt Features
- *
+ * @name DataTable.defaults.scrollY
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false
+ * "scrollY": "200px",
+ * "paginate": false
* } );
* } );
*/
@@ -9754,60 +11928,98 @@
/**
+ * __Deprecated__ The functionality provided by this parameter has now been
+ * superseded by that provided through `ajax`, which should be used instead.
+ *
* Set the HTTP method that is used to make the Ajax call for server-side
* processing or Ajax sourced data.
* @type string
* @default GET
+ *
* @dtopt Options
* @dtopt Server-side
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "bServerSide": true,
- * "sAjaxSource": "scripts/post.php",
- * "sServerMethod": "POST"
- * } );
- * } );
+ * @name DataTable.defaults.serverMethod
+ *
+ * @deprecated 1.10. Please use `ajax` for this functionality now.
*/
- "sServerMethod": "GET"
+ "sServerMethod": "GET",
+
+
+ /**
+ * DataTables makes use of renderers when displaying HTML elements for
+ * a table. These renderers can be added or modified by plug-ins to
+ * generate suitable mark-up for a site. For example the Bootstrap
+ * integration plug-in for DataTables uses a paging button renderer to
+ * display pagination buttons in the mark-up required by Bootstrap.
+ *
+ * For further information about the renderers available see
+ * DataTable.ext.renderer
+ * @type string|object
+ * @default null
+ *
+ * @name DataTable.defaults.renderer
+ *
+ */
+ "renderer": null,
+
+
+ /**
+ * Set the data property name that DataTables should use to get a row's id
+ * to set as the `id` property in the node.
+ * @type string
+ * @default DT_RowId
+ *
+ * @name DataTable.defaults.rowId
+ */
+ "rowId": "DT_RowId"
};
+ _fnHungarianMap( DataTable.defaults );
+
+
+ /*
+ * Developer note - See note in model.defaults.js about the use of Hungarian
+ * notation and camel case.
+ */
/**
* Column options that can be given to DataTables at initialisation time.
* @namespace
*/
- DataTable.defaults.columns = {
- /**
- * Allows a column's sorting to take multiple columns into account when
- * doing a sort. For example first name / last name columns make sense to
- * do a multi-column sort over the two columns.
- * @type array
+ DataTable.defaults.column = {
+ /**
+ * Define which column(s) an order will occur on for this column. This
+ * allows a column's ordering to take multiple columns into account when
+ * doing a sort or use the data from a different column. For example first
+ * name / last name columns make sense to do a multi-column sort over the
+ * two columns.
+ * @type array|int
* @default null <i>Takes the value of the column index automatically</i>
+ *
+ * @name DataTable.defaults.column.orderData
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "aDataSort": [ 0, 1 ], "aTargets": [ 0 ] },
- * { "aDataSort": [ 1, 0 ], "aTargets": [ 1 ] },
- * { "aDataSort": [ 2, 3, 4 ], "aTargets": [ 2 ] }
+ * "columnDefs": [
+ * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
+ * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
+ * { "orderData": 2, "targets": [ 2 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "aDataSort": [ 0, 1 ] },
- * { "aDataSort": [ 1, 0 ] },
- * { "aDataSort": [ 2, 3, 4 ] },
+ * "columns": [
+ * { "orderData": [ 0, 1 ] },
+ * { "orderData": [ 1, 0 ] },
+ * { "orderData": 2 },
* null,
* null
* ]
@@ -9815,37 +12027,40 @@
* } );
*/
"aDataSort": null,
+ "iDataSort": -1,
/**
- * You can control the default sorting direction, and even alter the behaviour
- * of the sort handler (i.e. only allow ascending sorting etc) using this
- * parameter.
+ * You can control the default ordering direction, and even alter the
+ * behaviour of the sort handler (i.e. only allow ascending ordering etc)
+ * using this parameter.
* @type array
* @default [ 'asc', 'desc' ]
+ *
+ * @name DataTable.defaults.column.orderSequence
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "asSorting": [ "asc" ], "aTargets": [ 1 ] },
- * { "asSorting": [ "desc", "asc", "asc" ], "aTargets": [ 2 ] },
- * { "asSorting": [ "desc" ], "aTargets": [ 3 ] }
+ * "columnDefs": [
+ * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
+ * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
+ * "columns": [
* null,
- * { "asSorting": [ "asc" ] },
- * { "asSorting": [ "desc", "asc", "asc" ] },
- * { "asSorting": [ "desc" ] },
+ * { "orderSequence": [ "asc" ] },
+ * { "orderSequence": [ "desc", "asc", "asc" ] },
+ * { "orderSequence": [ "desc" ] },
* null
* ]
* } );
@@ -9858,23 +12073,25 @@
* Enable or disable filtering on the data in this column.
* @type boolean
* @default true
+ *
+ * @name DataTable.defaults.column.searchable
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "bSearchable": false, "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "searchable": false, "targets": [ 0 ] }
* ] } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "bSearchable": false },
+ * "columns": [
+ * { "searchable": false },
* null,
* null,
* null,
@@ -9886,26 +12103,28 @@
/**
- * Enable or disable sorting on this column.
+ * Enable or disable ordering on this column.
* @type boolean
* @default true
+ *
+ * @name DataTable.defaults.column.orderable
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "bSortable": false, "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "orderable": false, "targets": [ 0 ] }
* ] } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "bSortable": false },
+ * "columns": [
+ * { "orderable": false },
* null,
* null,
* null,
@@ -9917,43 +12136,28 @@
/**
- * <code>Deprecated</code> When using fnRender() for a column, you may wish
- * to use the original data (before rendering) for sorting and filtering
- * (the default is to used the rendered data that the user can see). This
- * may be useful for dates etc.
- *
- * Please note that this option has now been deprecated and will be removed
- * in the next version of DataTables. Please use mRender / mData rather than
- * fnRender.
- * @type boolean
- * @default true
- * @dtopt Columns
- * @deprecated
- */
- "bUseRendered": true,
-
-
- /**
* Enable or disable the display of this column.
* @type boolean
* @default true
+ *
+ * @name DataTable.defaults.column.visible
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "bVisible": false, "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "visible": false, "targets": [ 0 ] }
* ] } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "bVisible": false },
+ * "columns": [
+ * { "visible": false },
* null,
* null,
* null,
@@ -9962,29 +12166,31 @@
* } );
*/
"bVisible": true,
-
-
+
+
/**
* Developer definable function that is called whenever a cell is created (Ajax source,
* etc) or processed for input (DOM source). This can be used as a compliment to mRender
* allowing you to modify the DOM element (add background colour for example) when the
* element is available.
* @type function
- * @param {element} nTd The TD node that has been created
- * @param {*} sData The Data for the cell
- * @param {array|object} oData The data for the whole row
- * @param {int} iRow The row index for the aoData data store
- * @param {int} iCol The column index for aoColumns
+ * @param {element} td The TD node that has been created
+ * @param {*} cellData The Data for the cell
+ * @param {array|object} rowData The data for the whole row
+ * @param {int} row The row index for the aoData data store
+ * @param {int} col The column index for aoColumns
+ *
+ * @name DataTable.defaults.column.createdCell
* @dtopt Columns
- *
+ *
* @example
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [ {
- * "aTargets": [3],
- * "fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
- * if ( sData == "1.7" ) {
- * $(nTd).css('color', 'blue')
+ * "columnDefs": [ {
+ * "targets": [3],
+ * "createdCell": function (td, cellData, rowData, row, col) {
+ * if ( cellData == "1.7" ) {
+ * $(td).css('color', 'blue')
* }
* }
* } ]
@@ -9995,132 +12201,139 @@
/**
- * <code>Deprecated</code> Custom display function that will be called for the
- * display of each cell in this column.
- *
- * Please note that this option has now been deprecated and will be removed
- * in the next version of DataTables. Please use mRender / mData rather than
- * fnRender.
- * @type function
- * @param {object} o Object with the following parameters:
- * @param {int} o.iDataRow The row in aoData
- * @param {int} o.iDataColumn The column in question
- * @param {array} o.aData The data for the row in question
- * @param {object} o.oSettings The settings object for this DataTables instance
- * @param {object} o.mDataProp The data property used for this column
- * @param {*} val The current cell value
- * @returns {string} The string you which to use in the display
- * @dtopt Columns
- * @deprecated
+ * This parameter has been replaced by `data` in DataTables to ensure naming
+ * consistency. `dataProp` can still be used, as there is backwards
+ * compatibility in DataTables for this option, but it is strongly
+ * recommended that you use `data` in preference to `dataProp`.
+ * @name DataTable.defaults.column.dataProp
*/
- "fnRender": null,
/**
- * The column index (starting from 0!) that you wish a sort to be performed
- * upon when this column is selected for sorting. This can be used for sorting
- * on hidden columns for example.
- * @type int
- * @default -1 <i>Use automatically calculated column index</i>
+ * This property can be used to read data from any data source property,
+ * including deeply nested objects / properties. `data` can be given in a
+ * number of different ways which effect its behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object. Note that
+ * function notation is recommended for use in `render` rather than
+ * `data` as it is much simpler to use as a renderer.
+ * * `null` - use the original data source for the row rather than plucking
+ * data directly from it. This action has effects on two other
+ * initialisation options:
+ * * `defaultContent` - When null is given as the `data` option and
+ * `defaultContent` is specified for the column, the value defined by
+ * `defaultContent` will be used for the cell.
+ * * `render` - When null is used for the `data` option and the `render`
+ * option is specified for the column, the whole data source for the
+ * row is used for the renderer.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * `{array|object}` The data source for the row
+ * * `{string}` The type call data requested - this will be 'set' when
+ * setting data or 'filter', 'display', 'type', 'sort' or undefined
+ * when gathering data. Note that when `undefined` is given for the
+ * type DataTables expects to get the raw data for the object back<
+ * * `{*}` Data to set when the second parameter is 'set'.
+ * * Return:
+ * * The return value from the function is not required when 'set' is
+ * the type of call, but otherwise the return is what will be used
+ * for the data requested.
+ *
+ * Note that `data` is a getter and setter option. If you just require
+ * formatting of data for output, you will likely want to use `render` which
+ * is simply a getter and thus simpler to use.
+ *
+ * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
+ * name change reflects the flexibility of this property and is consistent
+ * with the naming of mRender. If 'mDataProp' is given, then it will still
+ * be used by DataTables, as it automatically maps the old name to the new
+ * if required.
+ *
+ * @type string|int|function|null
+ * @default null <i>Use automatically calculated column index</i>
+ *
+ * @name DataTable.defaults.column.data
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Read table data from objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {value},
+ * // "version": {value},
+ * // "grade": {value}
+ * // }
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "iDataSort": 1, "aTargets": [ 0 ] }
+ * "ajaxSource": "sources/objects.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform" },
+ * { "data": "version" },
+ * { "data": "grade" }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Read information from deeply nested objects
+ * // JSON structure for each row:
+ * // {
+ * // "engine": {value},
+ * // "browser": {value},
+ * // "platform": {
+ * // "inner": {value}
+ * // },
+ * // "details": [
+ * // {value}, {value}
+ * // ]
+ * // }
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "iDataSort": 1 },
- * null,
- * null,
- * null,
- * null
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
+ * { "data": "platform.inner" },
+ * { "data": "platform.details.0" },
+ * { "data": "platform.details.1" }
* ]
* } );
* } );
- */
- "iDataSort": -1,
-
-
- /**
- * This parameter has been replaced by mData in DataTables to ensure naming
- * consistency. mDataProp can still be used, as there is backwards compatibility
- * in DataTables for this option, but it is strongly recommended that you use
- * mData in preference to mDataProp.
- * @name DataTable.defaults.columns.mDataProp
- */
-
-
- /**
- * This property can be used to read data from any JSON data source property,
- * including deeply nested objects / properties. mData can be given in a
- * number of different ways which effect its behaviour:
- * <ul>
- * <li>integer - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).</li>
- * <li>string - read an object property from the data source. Note that you can
- * use Javascript dotted notation to read deep properties / arrays from the
- * data source.</li>
- * <li>null - the sDefaultContent option will be used for the cell (null
- * by default, so you will need to specify the default content you want -
- * typically an empty string). This can be useful on generated columns such
- * as edit / delete action columns.</li>
- * <li>function - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * <ul>
- * <li>{array|object} The data source for the row</li>
- * <li>{string} The type call data requested - this will be 'set' when
- * setting data or 'filter', 'display', 'type', 'sort' or undefined when
- * gathering data. Note that when <i>undefined</i> is given for the type
- * DataTables expects to get the raw data for the object back</li>
- * <li>{*} Data to set when the second parameter is 'set'.</li>
- * </ul>
- * The return value from the function is not required when 'set' is the type
- * of call, but otherwise the return is what will be used for the data
- * requested.</li>
- * </ul>
*
- * Note that prior to DataTables 1.9.2 mData was called mDataProp. The name change
- * reflects the flexibility of this property and is consistent with the naming of
- * mRender. If 'mDataProp' is given, then it will still be used by DataTables, as
- * it automatically maps the old name to the new if required.
- * @type string|int|function|null
- * @default null <i>Use automatically calculated column index</i>
- * @dtopt Columns
- *
* @example
- * // Read table data from objects
- * $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "sAjaxSource": "sources/deep.txt",
- * "aoColumns": [
- * { "mData": "engine" },
- * { "mData": "browser" },
- * { "mData": "platform.inner" },
- * { "mData": "platform.details.0" },
- * { "mData": "platform.details.1" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using mData as a function to provide different information for
+ * // Using `data` as a function to provide different information for
* // sorting, filtering and display. In this case, currency (price)
* $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "aoColumnDefs": [ {
- * "aTargets": [ 0 ],
- * "mData": function ( source, type, val ) {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": function ( source, type, val ) {
* if (type === 'set') {
* source.price = val;
* // Store the computed dislay and filter values for efficiency
@@ -10140,73 +12353,150 @@
* } ]
* } );
* } );
+ *
+ * @example
+ * // Using default content
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null,
+ * "defaultContent": "Click to edit"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // Using array notation - outputting a list from an array
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "name[, ]"
+ * } ]
+ * } );
+ * } );
+ *
*/
"mData": null,
/**
- * This property is the rendering partner to mData and it is suggested that
- * when you want to manipulate data for display (including filtering, sorting etc)
- * but not altering the underlying data for the table, use this property. mData
- * can actually do everything this property can and more, but this parameter is
- * easier to use since there is no 'set' option. Like mData is can be given
- * in a number of different ways to effect its behaviour, with the addition of
- * supporting array syntax for easy outputting of arrays (including arrays of
- * objects):
- * <ul>
- * <li>integer - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).</li>
- * <li>string - read an object property from the data source. Note that you can
- * use Javascript dotted notation to read deep properties / arrays from the
- * data source and also array brackets to indicate that the data reader should
- * loop over the data source array. When characters are given between the array
- * brackets, these characters are used to join the data source array together.
- * For example: "accounts[, ].name" would result in a comma separated list with
- * the 'name' value from the 'accounts' array of objects.</li>
- * <li>function - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * <ul>
- * <li>{array|object} The data source for the row (based on mData)</li>
- * <li>{string} The type call data requested - this will be 'filter', 'display',
- * 'type' or 'sort'.</li>
- * <li>{array|object} The full data source for the row (not based on mData)</li>
- * </ul>
- * The return value from the function is what will be used for the data
- * requested.</li>
- * </ul>
- * @type string|int|function|null
- * @default null <i>Use mData</i>
+ * This property is the rendering partner to `data` and it is suggested that
+ * when you want to manipulate data for display (including filtering,
+ * sorting etc) without altering the underlying data for the table, use this
+ * property. `render` can be considered to be the the read only companion to
+ * `data` which is read / write (then as such more complex). Like `data`
+ * this option can be given in a number of different ways to effect its
+ * behaviour:
+ *
+ * * `integer` - treated as an array index for the data source. This is the
+ * default that DataTables uses (incrementally increased for each column).
+ * * `string` - read an object property from the data source. There are
+ * three 'special' options that can be used in the string to alter how
+ * DataTables reads the data from the source object:
+ * * `.` - Dotted Javascript notation. Just as you use a `.` in
+ * Javascript to read from nested objects, so to can the options
+ * specified in `data`. For example: `browser.version` or
+ * `browser.name`. If your object parameter name contains a period, use
+ * `\\` to escape it - i.e. `first\\.name`.
+ * * `[]` - Array notation. DataTables can automatically combine data
+ * from and array source, joining the data with the characters provided
+ * between the two brackets. For example: `name[, ]` would provide a
+ * comma-space separated list from the source array. If no characters
+ * are provided between the brackets, the original array source is
+ * returned.
+ * * `()` - Function notation. Adding `()` to the end of a parameter will
+ * execute a function of the name given. For example: `browser()` for a
+ * simple function on the data source, `browser.version()` for a
+ * function in a nested property or even `browser().version` to get an
+ * object property if the function called returns an object.
+ * * `object` - use different data for the different data types requested by
+ * DataTables ('filter', 'display', 'type' or 'sort'). The property names
+ * of the object is the data type the property refers to and the value can
+ * defined using an integer, string or function using the same rules as
+ * `render` normally does. Note that an `_` option _must_ be specified.
+ * This is the default value to use if you haven't specified a value for
+ * the data type requested by DataTables.
+ * * `function` - the function given will be executed whenever DataTables
+ * needs to set or get the data for a cell in the column. The function
+ * takes three parameters:
+ * * Parameters:
+ * * {array|object} The data source for the row (based on `data`)
+ * * {string} The type call data requested - this will be 'filter',
+ * 'display', 'type' or 'sort'.
+ * * {array|object} The full data source for the row (not based on
+ * `data`)
+ * * Return:
+ * * The return value from the function is what will be used for the
+ * data requested.
+ *
+ * @type string|int|function|object|null
+ * @default null Use the data source value.
+ *
+ * @name DataTable.defaults.column.render
* @dtopt Columns
- *
+ *
* @example
* // Create a comma separated list from an array of objects
* $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "sAjaxSource": "sources/deep.txt",
- * "aoColumns": [
- * { "mData": "engine" },
- * { "mData": "browser" },
+ * $('#example').dataTable( {
+ * "ajaxSource": "sources/deep.txt",
+ * "columns": [
+ * { "data": "engine" },
+ * { "data": "browser" },
* {
- * "mData": "platform",
- * "mRender": "[, ].name"
+ * "data": "platform",
+ * "render": "[, ].name"
* }
* ]
* } );
* } );
- *
+ *
+ * @example
+ * // Execute a function to obtain data
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": "browserName()"
+ * } ]
+ * } );
+ * } );
+ *
+ * @example
+ * // As an object, extracting different data for the different types
+ * // This would be used with a data source such as:
+ * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
+ * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
+ * // (which has both forms) is used for filtering for if a user inputs either format, while
+ * // the formatted phone number is the one that is shown in the table.
+ * $(document).ready( function() {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": null, // Use the full data source object for the renderer's source
+ * "render": {
+ * "_": "phone",
+ * "filter": "phone_filter",
+ * "display": "phone_display"
+ * }
+ * } ]
+ * } );
+ * } );
+ *
* @example
* // Use as a function to create a link from the data source
* $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "aoColumnDefs": [
- * {
- * "aTargets": [ 0 ],
- * "mData": "download_link",
- * "mRender": function ( data, type, full ) {
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "data": "download_link",
+ * "render": function ( data, type, full ) {
* return '<a href="'+data+'">Download</a>';
* }
- * ]
+ * } ]
* } );
* } );
*/
@@ -10219,15 +12509,17 @@
* to act as a header for a row (you may wish to add scope='row' to the TH elements).
* @type string
* @default td
+ *
+ * @name DataTable.defaults.column.cellType
* @dtopt Columns
- *
+ *
* @example
* // Make the first column use TH cells
* $(document).ready( function() {
- * var oTable = $('#example').dataTable( {
- * "aoColumnDefs": [ {
- * "aTargets": [ 0 ],
- * "sCellType": "th"
+ * $('#example').dataTable( {
+ * "columnDefs": [ {
+ * "targets": [ 0 ],
+ * "cellType": "th"
* } ]
* } );
* } );
@@ -10239,24 +12531,26 @@
* Class to give to each cell in this column.
* @type string
* @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.class
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sClass": "my_class", "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "class": "my_class", "targets": [ 0 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "sClass": "my_class" },
+ * "columns": [
+ * { "class": "my_class" },
* null,
* null,
* null,
@@ -10266,32 +12560,33 @@
* } );
*/
"sClass": "",
-
+
/**
* When DataTables calculates the column widths to assign to each column,
* it finds the longest string in each column and then constructs a
* temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
+ * is that "mmm" is much wider then "iiii", but the latter is a longer
* string - thus the calculation can go wrong (doing it properly and putting
* it into an DOM object and measuring that is horribly(!) slow). Thus as
* a "work around" we provide this option. It will append its value to the
* text that is found to be the longest string for the column - i.e. padding.
- * Generally you shouldn't need this, and it is not documented on the
- * general DataTables.net documentation
+ * Generally you shouldn't need this!
* @type string
* @default <i>Empty string<i>
+ *
+ * @name DataTable.defaults.column.contentPadding
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
+ * "columns": [
* null,
* null,
* null,
* {
- * "sContentPadding": "mmm"
+ * "contentPadding": "mmm"
* }
* ]
* } );
@@ -10302,37 +12597,39 @@
/**
* Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because mData
+ * whenever a null data source is encountered (this can be because `data`
* is set to null, or because the data source itself is null).
* @type string
* @default null
+ *
+ * @name DataTable.defaults.column.defaultContent
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
+ * "columnDefs": [
* {
- * "mData": null,
- * "sDefaultContent": "Edit",
- * "aTargets": [ -1 ]
+ * "data": null,
+ * "defaultContent": "Edit",
+ * "targets": [ -1 ]
* }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
+ * "columns": [
* null,
* null,
* null,
* {
- * "mData": null,
- * "sDefaultContent": "Edit"
+ * "data": null,
+ * "defaultContent": "Edit"
* }
* ]
* } );
@@ -10350,32 +12647,34 @@
* client-side, your server-side code does not also need updating).
* @type string
* @default <i>Empty string</i>
+ *
+ * @name DataTable.defaults.column.name
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sName": "engine", "aTargets": [ 0 ] },
- * { "sName": "browser", "aTargets": [ 1 ] },
- * { "sName": "platform", "aTargets": [ 2 ] },
- * { "sName": "version", "aTargets": [ 3 ] },
- * { "sName": "grade", "aTargets": [ 4 ] }
+ * "columnDefs": [
+ * { "name": "engine", "targets": [ 0 ] },
+ * { "name": "browser", "targets": [ 1 ] },
+ * { "name": "platform", "targets": [ 2 ] },
+ * { "name": "version", "targets": [ 3 ] },
+ * { "name": "grade", "targets": [ 4 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "sName": "engine" },
- * { "sName": "browser" },
- * { "sName": "platform" },
- * { "sName": "version" },
- * { "sName": "grade" }
+ * "columns": [
+ * { "name": "engine" },
+ * { "name": "browser" },
+ * { "name": "platform" },
+ * { "name": "version" },
+ * { "name": "grade" }
* ]
* } );
* } );
@@ -10384,38 +12683,40 @@
/**
- * Defines a data source type for the sorting which can be used to read
+ * Defines a data source type for the ordering which can be used to read
* real-time information from the table (updating the internally cached
- * version) prior to sorting. This allows sorting to occur on user editable
- * elements such as form inputs.
+ * version) prior to ordering. This allows ordering to occur on user
+ * editable elements such as form inputs.
* @type string
* @default std
+ *
+ * @name DataTable.defaults.column.orderDataType
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sSortDataType": "dom-text", "aTargets": [ 2, 3 ] },
- * { "sType": "numeric", "aTargets": [ 3 ] },
- * { "sSortDataType": "dom-select", "aTargets": [ 4 ] },
- * { "sSortDataType": "dom-checkbox", "aTargets": [ 5 ] }
+ * "columnDefs": [
+ * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
+ * { "type": "numeric", "targets": [ 3 ] },
+ * { "orderDataType": "dom-select", "targets": [ 4 ] },
+ * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
+ * "columns": [
* null,
* null,
- * { "sSortDataType": "dom-text" },
- * { "sSortDataType": "dom-text", "sType": "numeric" },
- * { "sSortDataType": "dom-select" },
- * { "sSortDataType": "dom-checkbox" }
+ * { "orderDataType": "dom-text" },
+ * { "orderDataType": "dom-text", "type": "numeric" },
+ * { "orderDataType": "dom-select" },
+ * { "orderDataType": "dom-checkbox" }
* ]
* } );
* } );
@@ -10426,26 +12727,28 @@
/**
* The title of this column.
* @type string
- * @default null <i>Derived from the 'TH' value for this column in the
+ * @default null <i>Derived from the 'TH' value for this column in the
* original HTML table.</i>
+ *
+ * @name DataTable.defaults.column.title
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sTitle": "My column title", "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "title": "My column title", "targets": [ 0 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "sTitle": "My column title" },
+ * "columns": [
+ * { "title": "My column title" },
* null,
* null,
* null,
@@ -10458,33 +12761,35 @@
/**
- * The type allows you to specify how the data for this column will be sorted.
- * Four types (string, numeric, date and html (which will strip HTML tags
- * before sorting)) are currently available. Note that only date formats
- * understood by Javascript's Date() object will be accepted as type date. For
- * example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 'numeric',
- * 'date' or 'html' (by default). Further types can be adding through
- * plug-ins.
+ * The type allows you to specify how the data for this column will be
+ * ordered. Four types (string, numeric, date and html (which will strip
+ * HTML tags before ordering)) are currently available. Note that only date
+ * formats understood by Javascript's Date() object will be accepted as type
+ * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
+ * 'numeric', 'date' or 'html' (by default). Further types can be adding
+ * through plug-ins.
* @type string
* @default null <i>Auto-detected from raw data</i>
+ *
+ * @name DataTable.defaults.column.type
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sType": "html", "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "type": "html", "targets": [ 0 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "sType": "html" },
+ * "columns": [
+ * { "type": "html" },
* null,
* null,
* null,
@@ -10498,29 +12803,31 @@
/**
* Defining the width of the column, this parameter may take any CSS value
- * (3em, 20px etc). DataTables apples 'smart' widths to columns which have not
+ * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
* been given a specific width through this interface ensuring that the table
* remains readable.
* @type string
* @default null <i>Automatic</i>
+ *
+ * @name DataTable.defaults.column.width
* @dtopt Columns
- *
+ *
* @example
- * // Using aoColumnDefs
+ * // Using `columnDefs`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumnDefs": [
- * { "sWidth": "20%", "aTargets": [ 0 ] }
+ * "columnDefs": [
+ * { "width": "20%", "targets": [ 0 ] }
* ]
* } );
* } );
- *
+ *
* @example
- * // Using aoColumns
+ * // Using `columns`
* $(document).ready( function() {
* $('#example').dataTable( {
- * "aoColumns": [
- * { "sWidth": "20%" },
+ * "columns": [
+ * { "width": "20%" },
* null,
* null,
* null,
@@ -10532,6 +12839,8 @@
"sWidth": null
};
+ _fnHungarianMap( DataTable.defaults.column );
+
/**
@@ -10539,11 +12848,11 @@
* given table, including configuration, data and current application of the
* table options. DataTables does not have a single instance for each DataTable
* with the settings attached to that instance, but rather instances of the
- * DataTable "class" are created on-the-fly as needed (typically by a
+ * DataTable "class" are created on-the-fly as needed (typically by a
* $().dataTable() call) and the settings object is then applied to that
* instance.
- *
- * Note that this object is related to {@link DataTable.defaults} but this
+ *
+ * Note that this object is related to {@link DataTable.defaults} but this
* one is the internal data store for DataTables's cache of columns. It should
* NOT be manipulated outside of DataTables. Any configuration should be done
* through the initialisation options.
@@ -10551,7 +12860,7 @@
* @todo Really should attach the settings object to individual instances so we
* don't need to create new instances on each $().dataTable() call (if the
* table already exists). It would also save passing oSettings around and
- * into every single function. However, this is a very significant
+ * into every single function. However, this is a very significant
* architecture change for DataTables and will almost certainly break
* backwards compatibility with older installations. This is something that
* will be done in 2.0.
@@ -10562,7 +12871,7 @@
* @namespace
*/
"oFeatures": {
-
+
/**
* Flag to say if DataTables should automatically try to calculate the
* optimum table and columns widths (true) or not (false).
@@ -10582,7 +12891,7 @@
* @type boolean
*/
"bDeferRender": null,
-
+
/**
* Enable filtering on the table or not. Note that if this is disabled
* then there is no filtering at all on the table, including fnFilter.
@@ -10592,7 +12901,7 @@
* @type boolean
*/
"bFilter": null,
-
+
/**
* Table information element (the 'Showing x of y records' div) enable
* flag.
@@ -10601,7 +12910,7 @@
* @type boolean
*/
"bInfo": null,
-
+
/**
* Present a user control allowing the end user to change the page size
* when pagination is enabled.
@@ -10619,7 +12928,7 @@
* @type boolean
*/
"bPaginate": null,
-
+
/**
* Processing indicator enable flag whenever DataTables is enacting a
* user request - typically an Ajax request for server-side processing.
@@ -10628,7 +12937,7 @@
* @type boolean
*/
"bProcessing": null,
-
+
/**
* Server-side processing enabled flag - when enabled DataTables will
* get all data from the server for every draw - there is no filtering,
@@ -10638,7 +12947,7 @@
* @type boolean
*/
"bServerSide": null,
-
+
/**
* Sorting enablement flag.
* Note that this parameter will be set by the initialisation routine. To
@@ -10646,7 +12955,15 @@
* @type boolean
*/
"bSort": null,
-
+
+ /**
+ * Multi-column sorting
+ * Note that this parameter will be set by the initialisation routine. To
+ * set a default use {@link DataTable.defaults}.
+ * @type boolean
+ */
+ "bSortMulti": null,
+
/**
* Apply a class to the columns which are being sorted to provide a
* visual highlight or not. This can slow things down when enabled since
@@ -10656,7 +12973,7 @@
* @type boolean
*/
"bSortClasses": null,
-
+
/**
* State saving enablement flag.
* Note that this parameter will be set by the initialisation routine. To
@@ -10665,7 +12982,7 @@
*/
"bStateSave": null
},
-
+
/**
* Scrolling settings for a table.
@@ -10673,16 +12990,6 @@
*/
"oScroll": {
/**
- * Indicate if DataTables should be allowed to set the padding / margin
- * etc for the scrolling header elements or not. Typically you will want
- * this.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bAutoCss": null,
-
- /**
* When the table is shorter in height than sScrollY, collapse the
* table container down to the height of the table (when true).
* Note that this parameter will be set by the initialisation routine. To
@@ -10690,16 +12997,7 @@
* @type boolean
*/
"bCollapse": null,
-
- /**
- * Infinite scrolling enablement flag. Now deprecated in favour of
- * using the Scroller plug-in.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bInfinite": null,
-
+
/**
* Width of the scrollbar for the web-browser's platform. Calculated
* during table initialisation.
@@ -10707,26 +13005,16 @@
* @default 0
*/
"iBarWidth": 0,
-
- /**
- * Space (in pixels) between the bottom of the scrolling container and
- * the bottom of the scrolling viewport before the next page is loaded
- * when using infinite scrolling.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type int
- */
- "iLoadGap": null,
-
+
/**
- * Viewport width for horizontal scrolling. Horizontal scrolling is
+ * Viewport width for horizontal scrolling. Horizontal scrolling is
* disabled if an empty string.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type string
*/
"sX": null,
-
+
/**
* Width to expand the table to when using x-scrolling. Typically you
* should not need to use this.
@@ -10736,7 +13024,7 @@
* @deprecated
*/
"sXInner": null,
-
+
/**
* Viewport height for vertical scrolling. Vertical scrolling is disabled
* if an empty string.
@@ -10746,7 +13034,7 @@
*/
"sY": null
},
-
+
/**
* Language information for the table.
* @namespace
@@ -10754,14 +13042,14 @@
*/
"oLanguage": {
/**
- * Information callback function. See
+ * Information callback function. See
* {@link DataTable.defaults.fnInfoCallback}
* @type function
* @default null
*/
"fnInfoCallback": null
},
-
+
/**
* Browser support parameters
* @namespace
@@ -10773,11 +13061,38 @@
* @type boolean
* @default false
*/
- "bScrollOversize": false
+ "bScrollOversize": false,
+
+ /**
+ * Determine if the vertical scrollbar is on the right or left of the
+ * scrolling container - needed for rtl language layout, although not
+ * all browsers move the scrollbar (Safari).
+ * @type boolean
+ * @default false
+ */
+ "bScrollbarLeft": false,
+
+ /**
+ * Flag for if `getBoundingClientRect` is fully supported or not
+ * @type boolean
+ * @default false
+ */
+ "bBounding": false,
+
+ /**
+ * Browser scrollbar width
+ * @type integer
+ * @default 0
+ */
+ "barWidth": 0
},
-
+
+
+ "ajax": null,
+
+
/**
- * Array referencing the nodes which are used for the features. The
+ * Array referencing the nodes which are used for the features. The
* parameters of this object match what is allowed by sDom - i.e.
* <ul>
* <li>'l' - Length changing</li>
@@ -10791,7 +13106,7 @@
* @default []
*/
"aanFeatures": [],
-
+
/**
* Store data information - see {@link DataTable.models.oRow} for detailed
* information.
@@ -10799,51 +13114,51 @@
* @default []
*/
"aoData": [],
-
+
/**
* Array of indexes which are in the current display (after filtering etc)
* @type array
* @default []
*/
"aiDisplay": [],
-
+
/**
* Array of indexes for display - no filtering
* @type array
* @default []
*/
"aiDisplayMaster": [],
-
+
+ /**
+ * Map of row ids to data indexes
+ * @type object
+ * @default {}
+ */
+ "aIds": {},
+
/**
* Store information about each column that is in use
* @type array
* @default []
*/
"aoColumns": [],
-
+
/**
* Store information about the table's header
* @type array
* @default []
*/
"aoHeader": [],
-
+
/**
* Store information about the table's footer
* @type array
* @default []
*/
"aoFooter": [],
-
- /**
- * Search data array for regular expression searching
- * @type array
- * @default []
- */
- "asDataSearch": [],
-
+
/**
- * Store the applied global search information in case we want to force a
+ * Store the applied global search information in case we want to force a
* research or compare the old search to a new one.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
@@ -10851,23 +13166,22 @@
* @extends DataTable.models.oSearch
*/
"oPreviousSearch": {},
-
+
/**
- * Store the applied search for each column - see
+ * Store the applied search for each column - see
* {@link DataTable.models.oSearch} for the format that is used for the
* filtering information for each column.
* @type array
* @default []
*/
"aoPreSearchCols": [],
-
+
/**
* Sorting that is applied to the table. Note that the inner arrays are
* used in the following manner:
* <ul>
* <li>Index 0 - column number</li>
* <li>Index 1 - current sorting direction</li>
- * <li>Index 2 - index of asSorting for this column</li>
* </ul>
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
@@ -10875,17 +13189,17 @@
* @todo These inner arrays should really be objects
*/
"aaSorting": null,
-
+
/**
* Sorting that is always applied to the table (i.e. prefixed in front of
* aaSorting).
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type array|null
- * @default null
+ * @type array
+ * @default []
*/
- "aaSortingFixed": null,
-
+ "aaSortingFixed": [],
+
/**
* Classes to use for the striping of a table.
* Note that this parameter will be set by the initialisation routine. To
@@ -10894,64 +13208,64 @@
* @default []
*/
"asStripeClasses": null,
-
+
/**
* If restoring a table - we should restore its striping classes as well
* @type array
* @default []
*/
"asDestroyStripes": [],
-
+
/**
- * If restoring a table - we should restore its width
+ * If restoring a table - we should restore its width
* @type int
* @default 0
*/
"sDestroyWidth": 0,
-
+
/**
* Callback functions array for every time a row is inserted (i.e. on a draw).
* @type array
* @default []
*/
"aoRowCallback": [],
-
+
/**
* Callback functions for the header on each draw.
* @type array
* @default []
*/
"aoHeaderCallback": [],
-
+
/**
* Callback function for the footer on each draw.
* @type array
* @default []
*/
"aoFooterCallback": [],
-
+
/**
* Array of callback functions for draw callback functions
* @type array
* @default []
*/
"aoDrawCallback": [],
-
+
/**
* Array of callback functions for row created function
* @type array
* @default []
*/
"aoRowCreatedCallback": [],
-
+
/**
- * Callback functions for just before the table is redrawn. A return of
+ * Callback functions for just before the table is redrawn. A return of
* false will be used to cancel the draw.
* @type array
* @default []
*/
"aoPreDrawCallback": [],
-
+
/**
* Callback functions for when the table has been initialised.
* @type array
@@ -10959,7 +13273,7 @@
*/
"aoInitComplete": [],
-
+
/**
* Callbacks for modifying the settings to be stored for state saving, prior to
* saving state.
@@ -10967,7 +13281,7 @@
* @default []
*/
"aoStateSaveParams": [],
-
+
/**
* Callbacks for modifying the settings that have been stored for state saving
* prior to using the stored values to restore the state.
@@ -10975,7 +13289,7 @@
* @default []
*/
"aoStateLoadParams": [],
-
+
/**
* Callbacks for operating on the settings object once the saved state has been
* loaded
@@ -10983,51 +13297,51 @@
* @default []
*/
"aoStateLoaded": [],
-
+
/**
* Cache the table ID for quick access
* @type string
* @default <i>Empty string</i>
*/
"sTableId": "",
-
+
/**
* The TABLE node for the main table
* @type node
* @default null
*/
"nTable": null,
-
+
/**
* Permanent ref to the thead element
* @type node
* @default null
*/
"nTHead": null,
-
+
/**
* Permanent ref to the tfoot element - if it exists
* @type node
* @default null
*/
"nTFoot": null,
-
+
/**
* Permanent ref to the tbody element
* @type node
* @default null
*/
"nTBody": null,
-
+
/**
* Cache the wrapper node (contains all DataTables controlled elements)
* @type node
* @default null
*/
"nTableWrapper": null,
-
+
/**
- * Indicate if when using server-side processing the loading of data
+ * Indicate if when using server-side processing the loading of data
* should be deferred until the second draw.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
@@ -11035,14 +13349,14 @@
* @default false
*/
"bDeferLoading": false,
-
+
/**
* Indicate if all required information has been read in
* @type boolean
* @default false
*/
"bInitialised": false,
-
+
/**
* Information about open rows. Each object in the array has the parameters
* 'nTr' and 'nParent'
@@ -11050,7 +13364,7 @@
* @default []
*/
"aoOpenRows": [],
-
+
/**
* Dictate the positioning of DataTables' control elements - see
* {@link DataTable.model.oInit.sDom}.
@@ -11060,50 +13374,39 @@
* @default null
*/
"sDom": null,
-
+
+ /**
+ * Search delay (in mS)
+ * @type integer
+ * @default null
+ */
+ "searchDelay": null,
+
/**
* Which type of pagination should be used.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
- * @type string
+ * @type string
* @default two_button
*/
"sPaginationType": "two_button",
-
+
/**
- * The cookie duration (for bStateSave) in seconds.
+ * The state duration (for `stateSave`) in seconds.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type int
* @default 0
*/
- "iCookieDuration": 0,
-
- /**
- * The cookie name prefix.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default <i>Empty string</i>
- */
- "sCookiePrefix": "",
-
- /**
- * Callback function for cookie creation.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type function
- * @default null
- */
- "fnCookieCallback": null,
-
+ "iStateDuration": 0,
+
/**
- * Array of callback functions for state saving. Each array element is an
+ * Array of callback functions for state saving. Each array element is an
* object with the following parameters:
* <ul>
* <li>function:fn - function to call. Takes two parameters, oSettings
* and the JSON string to save that has been thus far created. Returns
- * a JSON string to be inserted into a json object
+ * a JSON string to be inserted into a json object
* (i.e. '"param": [ 0, 1, 2]')</li>
* <li>string:sName - name of callback</li>
* </ul>
@@ -11111,12 +13414,12 @@
* @default []
*/
"aoStateSave": [],
-
+
/**
- * Array of callback functions for state loading. Each array element is an
+ * Array of callback functions for state loading. Each array element is an
* object with the following parameters:
* <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
+ * <li>function:fn - function to call. Takes two parameters, oSettings
* and the object stored. May return false to cancel state loading</li>
* <li>string:sName - name of callback</li>
* </ul>
@@ -11124,14 +13427,21 @@
* @default []
*/
"aoStateLoad": [],
-
+
+ /**
+ * State that was saved. Useful for back reference
+ * @type object
+ * @default null
+ */
+ "oSavedState": null,
+
/**
- * State that was loaded from the cookie. Useful for back reference
+ * State that was loaded. Useful for back reference
* @type object
* @default null
*/
"oLoadedState": null,
-
+
/**
* Source url for AJAX data for the table.
* Note that this parameter will be set by the initialisation routine. To
@@ -11140,33 +13450,47 @@
* @default null
*/
"sAjaxSource": null,
-
+
/**
* Property from a given object from which to read the table data from. This
- * can be an empty string (when not server-side processing), in which case
+ * can be an empty string (when not server-side processing), in which case
* it is assumed an an array is given directly.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type string
*/
"sAjaxDataProp": null,
-
+
/**
* Note if draw should be blocked while getting data
* @type boolean
* @default true
*/
"bAjaxDataGet": true,
-
+
/**
- * The last jQuery XHR object that was used for server-side data gathering.
- * This can be used for working with the XHR information in one of the
+ * The last jQuery XHR object that was used for server-side data gathering.
+ * This can be used for working with the XHR information in one of the
* callbacks
* @type object
* @default null
*/
"jqXHR": null,
-
+
+ /**
+ * JSON returned from the server in the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "json": undefined,
+
+ /**
+ * Data submitted as part of the last Ajax request
+ * @type object
+ * @default undefined
+ */
+ "oAjaxData": undefined,
+
/**
* Function to get the server-side data.
* Note that this parameter will be set by the initialisation routine. To
@@ -11174,24 +13498,24 @@
* @type function
*/
"fnServerData": null,
-
+
/**
- * Functions which are called prior to sending an Ajax request so extra
+ * Functions which are called prior to sending an Ajax request so extra
* parameters can easily be sent to the server
* @type array
* @default []
*/
"aoServerParams": [],
-
+
/**
- * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
+ * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
* required).
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type string
*/
"sServerMethod": null,
-
+
/**
* Format numbers for display.
* Note that this parameter will be set by the initialisation routine. To
@@ -11199,7 +13523,7 @@
* @type function
*/
"fnFormatNumber": null,
-
+
/**
* List of options that can be used for the user selectable length menu.
* Note that this parameter will be set by the initialisation routine. To
@@ -11208,7 +13532,7 @@
* @default []
*/
"aLengthMenu": null,
-
+
/**
* Counter for the draws that the table does. Also used as a tracker for
* server-side processing
@@ -11216,21 +13540,21 @@
* @default 0
*/
"iDraw": 0,
-
+
/**
* Indicate if a redraw is being done - useful for Ajax
* @type boolean
* @default false
*/
"bDrawing": false,
-
+
/**
* Draw index (iDraw) of the last error when parsing the returned data
* @type int
* @default -1
*/
"iDrawError": -1,
-
+
/**
* Paging display length
* @type int
@@ -11246,15 +13570,6 @@
"_iDisplayStart": 0,
/**
- * Paging end point - aiDisplay index. Use fnDisplayEnd rather than
- * this property to get the end point
- * @type int
- * @default 10
- * @private
- */
- "_iDisplayEnd": 10,
-
- /**
* Server-side processing - number of records in the result set
* (i.e. before filtering), Use fnRecordsTotal rather than
* this property to get the value of the number of records, regardless of
@@ -11275,7 +13590,7 @@
* @private
*/
"_iRecordsDisplay": 0,
-
+
/**
* Flag to indicate if jQuery UI marking and classes should be used.
* Note that this parameter will be set by the initialisation routine. To
@@ -11283,16 +13598,16 @@
* @type boolean
*/
"bJUI": null,
-
+
/**
* The classes to use for the table
* @type object
* @default {}
*/
"oClasses": {},
-
+
/**
- * Flag attached to the settings object so you can check in the draw
+ * Flag attached to the settings object so you can check in the draw
* callback if filtering has been done in the draw. Deprecated in favour of
* events.
* @type boolean
@@ -11300,9 +13615,9 @@
* @deprecated
*/
"bFiltered": false,
-
+
/**
- * Flag attached to the settings object so you can check in the draw
+ * Flag attached to the settings object so you can check in the draw
* callback if sorting has been done in the draw. Deprecated in favour of
* events.
* @type boolean
@@ -11310,24 +13625,24 @@
* @deprecated
*/
"bSorted": false,
-
+
/**
- * Indicate that if multiple rows are in the header and there is more than
- * one unique cell per column, if the top one (true) or bottom one (false)
+ * Indicate that if multiple rows are in the header and there is more than
+ * one unique cell per column, if the top one (true) or bottom one (false)
* should be used for sorting / title by DataTables.
* Note that this parameter will be set by the initialisation routine. To
* set a default use {@link DataTable.defaults}.
* @type boolean
*/
"bSortCellsTop": null,
-
+
/**
* Initialisation object that is used for the table
* @type object
* @default null
*/
"oInit": null,
-
+
/**
* Destroy callback functions - for plug-ins to attach themselves to the
* destroy so they can clean up markup and events.
@@ -11336,59 +13651,62 @@
*/
"aoDestroyCallback": [],
-
+
/**
* Get the number of records in the current record set, before filtering
* @type function
*/
"fnRecordsTotal": function ()
{
- if ( this.oFeatures.bServerSide ) {
- return parseInt(this._iRecordsTotal, 10);
- } else {
- return this.aiDisplayMaster.length;
- }
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsTotal * 1 :
+ this.aiDisplayMaster.length;
},
-
+
/**
* Get the number of records in the current record set, after filtering
* @type function
*/
"fnRecordsDisplay": function ()
{
- if ( this.oFeatures.bServerSide ) {
- return parseInt(this._iRecordsDisplay, 10);
- } else {
- return this.aiDisplay.length;
- }
+ return _fnDataSource( this ) == 'ssp' ?
+ this._iRecordsDisplay * 1 :
+ this.aiDisplay.length;
},
-
+
/**
- * Set the display end point - aiDisplay index
+ * Get the display end point - aiDisplay index
* @type function
- * @todo Should do away with _iDisplayEnd and calculate it on-the-fly here
*/
"fnDisplayEnd": function ()
{
- if ( this.oFeatures.bServerSide ) {
- if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
- return this._iDisplayStart+this.aiDisplay.length;
- } else {
- return Math.min( this._iDisplayStart+this._iDisplayLength,
- this._iRecordsDisplay );
- }
- } else {
- return this._iDisplayEnd;
+ var
+ len = this._iDisplayLength,
+ start = this._iDisplayStart,
+ calc = start + len,
+ records = this.aiDisplay.length,
+ features = this.oFeatures,
+ paginate = features.bPaginate;
+
+ if ( features.bServerSide ) {
+ return paginate === false || len === -1 ?
+ start + records :
+ Math.min( start+len, this._iRecordsDisplay );
+ }
+ else {
+ return ! paginate || calc>records || len===-1 ?
+ records :
+ calc;
}
},
-
+
/**
* The DataTables object for this table
* @type object
* @default null
*/
"oInstance": null,
-
+
/**
* Unique identifier for each instance of the DataTables object. If there
* is an ID on the table node, then it takes that value, otherwise an
@@ -11412,47 +13730,604 @@
/**
* DIV container for the footer scrolling table if scrolling
*/
- "nScrollFoot": null
+ "nScrollFoot": null,
+
+ /**
+ * Last applied sort
+ * @type array
+ * @default []
+ */
+ "aLastSort": [],
+
+ /**
+ * Stored plug-in instances
+ * @type object
+ * @default {}
+ */
+ "oPlugins": {},
+
+ /**
+ * Function used to get a row's id from the row's data
+ * @type function
+ * @default null
+ */
+ "rowIdFn": null,
+
+ /**
+ * Data location where to store a row's id
+ * @type string
+ * @default null
+ */
+ "rowId": null
};
/**
- * Extension object for DataTables that is used to provide all extension options.
- *
- * Note that the <i>DataTable.ext</i> object is available through
- * <i>jQuery.fn.dataTable.ext</i> where it may be accessed and manipulated. It is
- * also aliased to <i>jQuery.fn.dataTableExt</i> for historic reasons.
+ * Extension object for DataTables that is used to provide all extension
+ * options.
+ *
+ * Note that the `DataTable.ext` object is available through
+ * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
+ * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
* @namespace
* @extends DataTable.models.ext
*/
- DataTable.ext = $.extend( true, {}, DataTable.models.ext );
- $.extend( DataTable.ext.oStdClasses, {
+
+ /**
+ * DataTables extensions
+ *
+ * This namespace acts as a collection area for plug-ins that can be used to
+ * extend DataTables capabilities. Indeed many of the build in methods
+ * use this method to provide their own capabilities (sorting methods for
+ * example).
+ *
+ * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
+ * reasons
+ *
+ * @namespace
+ */
+ DataTable.ext = _ext = {
+ /**
+ * Buttons. For use with the Buttons extension for DataTables. This is
+ * defined here so other extensions can define buttons regardless of load
+ * order. It is _not_ used by DataTables core.
+ *
+ * @type object
+ * @default {}
+ */
+ buttons: {},
+
+
+ /**
+ * Element class names
+ *
+ * @type object
+ * @default {}
+ */
+ classes: {},
+
+
+ /**
+ * DataTables build type (expanded by the download builder)
+ *
+ * @type string
+ */
+ builder: "-source-",
+
+
+ /**
+ * Error reporting.
+ *
+ * How should DataTables report an error. Can take the value 'alert',
+ * 'throw', 'none' or a function.
+ *
+ * @type string|function
+ * @default alert
+ */
+ errMode: "alert",
+
+
+ /**
+ * Feature plug-ins.
+ *
+ * This is an array of objects which describe the feature plug-ins that are
+ * available to DataTables. These feature plug-ins are then available for
+ * use through the `dom` initialisation option.
+ *
+ * Each feature plug-in is described by an object which must have the
+ * following properties:
+ *
+ * * `fnInit` - function that is used to initialise the plug-in,
+ * * `cFeature` - a character so the feature can be enabled by the `dom`
+ * instillation option. This is case sensitive.
+ *
+ * The `fnInit` function has the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ *
+ * And the following return is expected:
+ *
+ * * {node|null} The element which contains your feature. Note that the
+ * return may also be void if your plug-in does not require to inject any
+ * DOM elements into DataTables control (`dom`) - for example this might
+ * be useful when developing a plug-in which allows table control via
+ * keyboard entry
+ *
+ * @type array
+ *
+ * @example
+ * $.fn.dataTable.ext.features.push( {
+ * "fnInit": function( oSettings ) {
+ * return new TableTools( { "oDTSettings": oSettings } );
+ * },
+ * "cFeature": "T"
+ * } );
+ */
+ feature: [],
+
+
+ /**
+ * Row searching.
+ *
+ * This method of searching is complimentary to the default type based
+ * searching, and a lot more comprehensive as it allows you complete control
+ * over the searching logic. Each element in this array is a function
+ * (parameters described below) that is called for every row in the table,
+ * and your logic decides if it should be included in the searching data set
+ * or not.
+ *
+ * Searching functions have the following input parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{array|object}` Data for the row to be processed (same as the
+ * original format that was passed in as the data source, or an array
+ * from a DOM data source
+ * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
+ * can be useful to retrieve the `TR` element if you need DOM interaction.
+ *
+ * And the following return is expected:
+ *
+ * * {boolean} Include the row in the searched result set (true) or not
+ * (false)
+ *
+ * Note that as with the main search ability in DataTables, technically this
+ * is "filtering", since it is subtractive. However, for consistency in
+ * naming we call it searching here.
+ *
+ * @type array
+ * @default []
+ *
+ * @example
+ * // The following example shows custom search being applied to the
+ * // fourth column (i.e. the data[3] index) based on two input values
+ * // from the end-user, matching the data in a certain range.
+ * $.fn.dataTable.ext.search.push(
+ * function( settings, data, dataIndex ) {
+ * var min = document.getElementById('min').value * 1;
+ * var max = document.getElementById('max').value * 1;
+ * var version = data[3] == "-" ? 0 : data[3]*1;
+ *
+ * if ( min == "" && max == "" ) {
+ * return true;
+ * }
+ * else if ( min == "" && version < max ) {
+ * return true;
+ * }
+ * else if ( min < version && "" == max ) {
+ * return true;
+ * }
+ * else if ( min < version && version < max ) {
+ * return true;
+ * }
+ * return false;
+ * }
+ * );
+ */
+ search: [],
+
+
+ /**
+ * Selector extensions
+ *
+ * The `selector` option can be used to extend the options available for the
+ * selector modifier options (`selector-modifier` object data type) that
+ * each of the three built in selector types offer (row, column and cell +
+ * their plural counterparts). For example the Select extension uses this
+ * mechanism to provide an option to select only rows, columns and cells
+ * that have been marked as selected by the end user (`{selected: true}`),
+ * which can be used in conjunction with the existing built in selector
+ * options.
+ *
+ * Each property is an array to which functions can be pushed. The functions
+ * take three attributes:
+ *
+ * * Settings object for the host table
+ * * Options object (`selector-modifier` object type)
+ * * Array of selected item indexes
+ *
+ * The return is an array of the resulting item indexes after the custom
+ * selector has been applied.
+ *
+ * @type object
+ */
+ selector: {
+ cell: [],
+ column: [],
+ row: []
+ },
+
+
+ /**
+ * Internal functions, exposed for used in plug-ins.
+ *
+ * Please note that you should not need to use the internal methods for
+ * anything other than a plug-in (and even then, try to avoid if possible).
+ * The internal function may change between releases.
+ *
+ * @type object
+ * @default {}
+ */
+ internal: {},
+
+
+ /**
+ * Legacy configuration options. Enable and disable legacy options that
+ * are available in DataTables.
+ *
+ * @type object
+ */
+ legacy: {
+ /**
+ * Enable / disable DataTables 1.9 compatible server-side processing
+ * requests
+ *
+ * @type boolean
+ * @default null
+ */
+ ajax: null
+ },
+
+
+ /**
+ * Pagination plug-in methods.
+ *
+ * Each entry in this object is a function and defines which buttons should
+ * be shown by the pagination rendering method that is used for the table:
+ * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
+ * buttons are displayed in the document, while the functions here tell it
+ * what buttons to display. This is done by returning an array of button
+ * descriptions (what each button will do).
+ *
+ * Pagination types (the four built in options and any additional plug-in
+ * options defined here) can be used through the `paginationType`
+ * initialisation parameter.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{int} page` The current page index
+ * 2. `{int} pages` The number of pages in the table
+ *
+ * Each function is expected to return an array where each element of the
+ * array can be one of:
+ *
+ * * `first` - Jump to first page when activated
+ * * `last` - Jump to last page when activated
+ * * `previous` - Show previous page when activated
+ * * `next` - Show next page when activated
+ * * `{int}` - Show page of the index given
+ * * `{array}` - A nested array containing the above elements to add a
+ * containing 'DIV' element (might be useful for styling).
+ *
+ * Note that DataTables v1.9- used this object slightly differently whereby
+ * an object with two functions would be defined for each plug-in. That
+ * ability is still supported by DataTables 1.10+ to provide backwards
+ * compatibility, but this option of use is now decremented and no longer
+ * documented in DataTables 1.10+.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * // Show previous, next and current page buttons only
+ * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
+ * return [ 'previous', page, 'next' ];
+ * };
+ */
+ pager: {},
+
+
+ renderer: {
+ pageButton: {},
+ header: {}
+ },
+
+
+ /**
+ * Ordering plug-ins - custom data source
+ *
+ * The extension options for ordering of data available here is complimentary
+ * to the default type based ordering that DataTables typically uses. It
+ * allows much greater control over the the data that is being used to
+ * order a column, but is necessarily therefore more complex.
+ *
+ * This type of ordering is useful if you want to do ordering based on data
+ * live from the DOM (for example the contents of an 'input' element) rather
+ * than just the static string that DataTables knows of.
+ *
+ * The way these plug-ins work is that you create an array of the values you
+ * wish to be ordering for the column in question and then return that
+ * array. The data in the array much be in the index order of the rows in
+ * the table (not the currently ordering order!). Which order data gathering
+ * function is run here depends on the `dt-init columns.orderDataType`
+ * parameter that is used for the column (if any).
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{object}` DataTables settings object: see
+ * {@link DataTable.models.oSettings}
+ * 2. `{int}` Target column index
+ *
+ * Each function is expected to return an array:
+ *
+ * * `{array}` Data for the column to be ordering upon
+ *
+ * @type array
+ *
+ * @example
+ * // Ordering using `input` node values
+ * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
+ * {
+ * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
+ * return $('input', td).val();
+ * } );
+ * }
+ */
+ order: {},
+
+
+ /**
+ * Type based plug-ins.
+ *
+ * Each column in DataTables has a type assigned to it, either by automatic
+ * detection or by direct assignment using the `type` option for the column.
+ * The type of a column will effect how it is ordering and search (plug-ins
+ * can also make use of the column type if required).
+ *
+ * @namespace
+ */
+ type: {
+ /**
+ * Type detection functions.
+ *
+ * The functions defined in this object are used to automatically detect
+ * a column's type, making initialisation of DataTables super easy, even
+ * when complex data is in the table.
+ *
+ * The functions defined take two parameters:
+ *
+ * 1. `{*}` Data from the column cell to be analysed
+ * 2. `{settings}` DataTables settings object. This can be used to
+ * perform context specific type detection - for example detection
+ * based on language settings such as using a comma for a decimal
+ * place. Generally speaking the options from the settings will not
+ * be required
+ *
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Data type detected, or null if unknown (and thus
+ * pass it on to the other type detection functions.
+ *
+ * @type array
+ *
+ * @example
+ * // Currency type detection plug-in:
+ * $.fn.dataTable.ext.type.detect.push(
+ * function ( data, settings ) {
+ * // Check the numeric part
+ * if ( ! $.isNumeric( data.substring(1) ) ) {
+ * return null;
+ * }
+ *
+ * // Check prefixed by currency
+ * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
+ * return 'currency';
+ * }
+ * return null;
+ * }
+ * );
+ */
+ detect: [],
+
+
+ /**
+ * Type based search formatting.
+ *
+ * The type based searching functions can be used to pre-format the
+ * data to be search on. For example, it can be used to strip HTML
+ * tags or to de-format telephone numbers for numeric only searching.
+ *
+ * Note that is a search is not defined for a column of a given type,
+ * no search formatting will be performed.
+ *
+ * Pre-processing of searching data plug-ins - When you assign the sType
+ * for a column (or have it automatically detected for you by DataTables
+ * or a type detection plug-in), you will typically be using this for
+ * custom sorting, but it can also be used to provide custom searching
+ * by allowing you to pre-processing the data and returning the data in
+ * the format that should be searched upon. This is done by adding
+ * functions this object with a parameter name which matches the sType
+ * for that target column. This is the corollary of <i>afnSortData</i>
+ * for searching data.
+ *
+ * The functions defined take a single parameter:
+ *
+ * 1. `{*}` Data from the column cell to be prepared for searching
+ *
+ * Each function is expected to return:
+ *
+ * * `{string|null}` Formatted string that will be used for the searching.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
+ * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
+ * }
+ */
+ search: {},
+
+
+ /**
+ * Type based ordering.
+ *
+ * The column type tells DataTables what ordering to apply to the table
+ * when a column is sorted upon. The order for each type that is defined,
+ * is defined by the functions available in this object.
+ *
+ * Each ordering option can be described by three properties added to
+ * this object:
+ *
+ * * `{type}-pre` - Pre-formatting function
+ * * `{type}-asc` - Ascending order function
+ * * `{type}-desc` - Descending order function
+ *
+ * All three can be used together, only `{type}-pre` or only
+ * `{type}-asc` and `{type}-desc` together. It is generally recommended
+ * that only `{type}-pre` is used, as this provides the optimal
+ * implementation in terms of speed, although the others are provided
+ * for compatibility with existing Javascript sort functions.
+ *
+ * `{type}-pre`: Functions defined take a single parameter:
+ *
+ * 1. `{*}` Data from the column cell to be prepared for ordering
+ *
+ * And return:
+ *
+ * * `{*}` Data to be sorted upon
+ *
+ * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
+ * functions, taking two parameters:
+ *
+ * 1. `{*}` Data to compare to the second parameter
+ * 2. `{*}` Data to compare to the first parameter
+ *
+ * And returning:
+ *
+ * * `{*}` Ordering match: <0 if first parameter should be sorted lower
+ * than the second parameter, ===0 if the two parameters are equal and
+ * >0 if the first parameter should be sorted height than the second
+ * parameter.
+ *
+ * @type object
+ * @default {}
+ *
+ * @example
+ * // Numeric ordering of formatted numbers with a pre-formatter
+ * $.extend( $.fn.dataTable.ext.type.order, {
+ * "string-pre": function(x) {
+ * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
+ * return parseFloat( a );
+ * }
+ * } );
+ *
+ * @example
+ * // Case-sensitive string ordering, with no pre-formatting method
+ * $.extend( $.fn.dataTable.ext.order, {
+ * "string-case-asc": function(x,y) {
+ * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ * },
+ * "string-case-desc": function(x,y) {
+ * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ * }
+ * } );
+ */
+ order: {}
+ },
+
+ /**
+ * Unique DataTables instance counter
+ *
+ * @type int
+ * @private
+ */
+ _unique: 0,
+
+
+ //
+ // Depreciated
+ // The following properties are retained for backwards compatiblity only.
+ // The should not be used in new projects and will be removed in a future
+ // version
+ //
+
+ /**
+ * Version check function.
+ * @type function
+ * @depreciated Since 1.10
+ */
+ fnVersionCheck: DataTable.fnVersionCheck,
+
+
+ /**
+ * Index for what 'this' index API functions should use
+ * @type int
+ * @deprecated Since v1.10
+ */
+ iApiIndex: 0,
+
+
+ /**
+ * jQuery UI class container
+ * @type object
+ * @deprecated Since v1.10
+ */
+ oJUIClasses: {},
+
+
+ /**
+ * Software version
+ * @type string
+ * @deprecated Since v1.10
+ */
+ sVersion: DataTable.version
+ };
+
+
+ //
+ // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
+ //
+ $.extend( _ext, {
+ afnFiltering: _ext.search,
+ aTypes: _ext.type.detect,
+ ofnSearch: _ext.type.search,
+ oSort: _ext.type.order,
+ afnSortData: _ext.order,
+ aoFeatures: _ext.feature,
+ oApi: _ext.internal,
+ oStdClasses: _ext.classes,
+ oPagination: _ext.pager
+ } );
+
+
+ $.extend( DataTable.ext.classes, {
"sTable": "dataTable",
+ "sNoFooter": "no-footer",
- /* Two buttons buttons */
- "sPagePrevEnabled": "paginate_enabled_previous",
- "sPagePrevDisabled": "paginate_disabled_previous",
- "sPageNextEnabled": "paginate_enabled_next",
- "sPageNextDisabled": "paginate_disabled_next",
- "sPageJUINext": "",
- "sPageJUIPrev": "",
-
- /* Full numbers paging buttons */
+ /* Paging buttons */
"sPageButton": "paginate_button",
- "sPageButtonActive": "paginate_active",
- "sPageButtonStaticDisabled": "paginate_button paginate_button_disabled",
- "sPageFirst": "first",
- "sPagePrevious": "previous",
- "sPageNext": "next",
- "sPageLast": "last",
-
+ "sPageButtonActive": "current",
+ "sPageButtonDisabled": "disabled",
+
/* Striping classes */
"sStripeOdd": "odd",
"sStripeEven": "even",
-
+
/* Empty row */
"sRowEmpty": "dataTables_empty",
-
+
/* Features */
"sWrapper": "dataTables_wrapper",
"sFilter": "dataTables_filter",
@@ -11460,7 +14335,7 @@
"sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
"sLength": "dataTables_length",
"sProcessing": "dataTables_processing",
-
+
/* Sorting */
"sSortAsc": "sorting_asc",
"sSortDesc": "sorting_desc",
@@ -11469,14 +14344,13 @@
"sSortableDesc": "sorting_desc_disabled",
"sSortableNone": "sorting_disabled",
"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
- "sSortJUIAsc": "",
- "sSortJUIDesc": "",
- "sSortJUI": "",
- "sSortJUIAscAllowed": "",
- "sSortJUIDescAllowed": "",
- "sSortJUIWrapper": "",
- "sSortIcon": "",
-
+
+ /* Filtering */
+ "sFilterInput": "",
+
+ /* Page length */
+ "sLengthSelect": "",
+
/* Scrolling */
"sScrollWrapper": "dataTables_scroll",
"sScrollHead": "dataTables_scrollHead",
@@ -11484,518 +14358,765 @@
"sScrollBody": "dataTables_scrollBody",
"sScrollFoot": "dataTables_scrollFoot",
"sScrollFootInner": "dataTables_scrollFootInner",
-
+
/* Misc */
+ "sHeaderTH": "",
"sFooterTH": "",
+
+ // Deprecated
+ "sSortJUIAsc": "",
+ "sSortJUIDesc": "",
+ "sSortJUI": "",
+ "sSortJUIAscAllowed": "",
+ "sSortJUIDescAllowed": "",
+ "sSortJUIWrapper": "",
+ "sSortIcon": "",
"sJUIHeader": "",
"sJUIFooter": ""
} );
- $.extend( DataTable.ext.oJUIClasses, DataTable.ext.oStdClasses, {
- /* Two buttons buttons */
- "sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
- "sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
- "sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
- "sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
- "sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
- "sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
-
+ (function() {
+
+ // Reused strings for better compression. Closure compiler appears to have a
+ // weird edge case where it is trying to expand strings rather than use the
+ // variable version. This results in about 200 bytes being added, for very
+ // little preference benefit since it this run on script load only.
+ var _empty = '';
+ _empty = '';
+
+ var _stateDefault = _empty + 'ui-state-default';
+ var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
+ var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
+
+ $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
/* Full numbers paging buttons */
- "sPageButton": "fg-button ui-button ui-state-default",
- "sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
- "sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
- "sPageFirst": "first ui-corner-tl ui-corner-bl",
- "sPageLast": "last ui-corner-tr ui-corner-br",
-
+ "sPageButton": "fg-button ui-button "+_stateDefault,
+ "sPageButtonActive": "ui-state-disabled",
+ "sPageButtonDisabled": "ui-state-disabled",
+
/* Features */
"sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
"ui-buttonset-multi paging_", /* Note that the type is postfixed */
-
+
/* Sorting */
- "sSortAsc": "ui-state-default",
- "sSortDesc": "ui-state-default",
- "sSortable": "ui-state-default",
- "sSortableAsc": "ui-state-default",
- "sSortableDesc": "ui-state-default",
- "sSortableNone": "ui-state-default",
- "sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
- "sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
- "sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
- "sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
- "sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
- "sSortJUIWrapper": "DataTables_sort_wrapper",
- "sSortIcon": "DataTables_sort_icon",
-
+ "sSortAsc": _stateDefault+" sorting_asc",
+ "sSortDesc": _stateDefault+" sorting_desc",
+ "sSortable": _stateDefault+" sorting",
+ "sSortableAsc": _stateDefault+" sorting_asc_disabled",
+ "sSortableDesc": _stateDefault+" sorting_desc_disabled",
+ "sSortableNone": _stateDefault+" sorting_disabled",
+ "sSortJUIAsc": _sortIcon+"triangle-1-n",
+ "sSortJUIDesc": _sortIcon+"triangle-1-s",
+ "sSortJUI": _sortIcon+"carat-2-n-s",
+ "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
+ "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
+ "sSortJUIWrapper": "DataTables_sort_wrapper",
+ "sSortIcon": "DataTables_sort_icon",
+
/* Scrolling */
- "sScrollHead": "dataTables_scrollHead ui-state-default",
- "sScrollFoot": "dataTables_scrollFoot ui-state-default",
-
+ "sScrollHead": "dataTables_scrollHead "+_stateDefault,
+ "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
+
/* Misc */
- "sFooterTH": "ui-state-default",
- "sJUIHeader": "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",
- "sJUIFooter": "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"
+ "sHeaderTH": _stateDefault,
+ "sFooterTH": _stateDefault,
+ "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
+ "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
} );
- /*
- * Variable: oPagination
- * Purpose:
- * Scope: jQuery.fn.dataTableExt
- */
- $.extend( DataTable.ext.oPagination, {
- /*
- * Variable: two_button
- * Purpose: Standard two button (forward/back) pagination
- * Scope: jQuery.fn.dataTableExt.oPagination
- */
- "two_button": {
- /*
- * Function: oPagination.two_button.fnInit
- * Purpose: Initialise dom elements required for pagination with forward/back buttons only
- * Returns: -
- * Inputs: object:oSettings - dataTables settings object
- * node:nPaging - the DIV which contains this pagination control
- * function:fnCallbackDraw - draw function which must be called on update
- */
- "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
- {
- var oLang = oSettings.oLanguage.oPaginate;
- var oClasses = oSettings.oClasses;
- var fnClickHandler = function ( e ) {
- if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
- {
- fnCallbackDraw( oSettings );
- }
- };
+ }());
- var sAppend = (!oSettings.bJUI) ?
- '<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sPrevious+'</a>'+
- '<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sNext+'</a>'
- :
- '<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUIPrev+'"></span></a>'+
- '<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUINext+'"></span></a>';
- $(nPaging).append( sAppend );
-
- var els = $('a', nPaging);
- var nPrevious = els[0],
- nNext = els[1];
-
- oSettings.oApi._fnBindAction( nPrevious, {action: "previous"}, fnClickHandler );
- oSettings.oApi._fnBindAction( nNext, {action: "next"}, fnClickHandler );
-
- /* ID the first elements only */
- if ( !oSettings.aanFeatures.p )
- {
- nPaging.id = oSettings.sTableId+'_paginate';
- nPrevious.id = oSettings.sTableId+'_previous';
- nNext.id = oSettings.sTableId+'_next';
- nPrevious.setAttribute('aria-controls', oSettings.sTableId);
- nNext.setAttribute('aria-controls', oSettings.sTableId);
- }
- },
-
- /*
- * Function: oPagination.two_button.fnUpdate
- * Purpose: Update the two button pagination at the end of the draw
- * Returns: -
- * Inputs: object:oSettings - dataTables settings object
- * function:fnCallbackDraw - draw function to call on page change
- */
- "fnUpdate": function ( oSettings, fnCallbackDraw )
- {
- if ( !oSettings.aanFeatures.p )
- {
- return;
- }
-
- var oClasses = oSettings.oClasses;
- var an = oSettings.aanFeatures.p;
- var nNode;
- /* Loop over each instance of the pager */
- for ( var i=0, iLen=an.length ; i<iLen ; i++ )
- {
- nNode = an[i].firstChild;
- if ( nNode )
- {
- /* Previous page */
- nNode.className = ( oSettings._iDisplayStart === 0 ) ?
- oClasses.sPagePrevDisabled : oClasses.sPagePrevEnabled;
-
- /* Next page */
- nNode = nNode.nextSibling;
- nNode.className = ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
- oClasses.sPageNextDisabled : oClasses.sPageNextEnabled;
- }
- }
- }
+ var extPagination = DataTable.ext.pager;
+
+ function _numbers ( page, pages ) {
+ var
+ numbers = [],
+ buttons = extPagination.numbers_length,
+ half = Math.floor( buttons / 2 ),
+ i = 1;
+
+ if ( pages <= buttons ) {
+ numbers = _range( 0, pages );
+ }
+ else if ( page <= half ) {
+ numbers = _range( 0, buttons-2 );
+ numbers.push( 'ellipsis' );
+ numbers.push( pages-1 );
+ }
+ else if ( page >= pages - 1 - half ) {
+ numbers = _range( pages-(buttons-2), pages );
+ numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
+ numbers.splice( 0, 0, 0 );
+ }
+ else {
+ numbers = _range( page-half+2, page+half-1 );
+ numbers.push( 'ellipsis' );
+ numbers.push( pages-1 );
+ numbers.splice( 0, 0, 'ellipsis' );
+ numbers.splice( 0, 0, 0 );
+ }
+
+ numbers.DT_el = 'span';
+ return numbers;
+ }
+
+
+ $.extend( extPagination, {
+ simple: function ( page, pages ) {
+ return [ 'previous', 'next' ];
},
-
-
- /*
- * Variable: iFullNumbersShowPages
- * Purpose: Change the number of pages which can be seen
- * Scope: jQuery.fn.dataTableExt.oPagination
- */
- "iFullNumbersShowPages": 5,
-
- /*
- * Variable: full_numbers
- * Purpose: Full numbers pagination
- * Scope: jQuery.fn.dataTableExt.oPagination
- */
- "full_numbers": {
- /*
- * Function: oPagination.full_numbers.fnInit
- * Purpose: Initialise dom elements required for pagination with a list of the pages
- * Returns: -
- * Inputs: object:oSettings - dataTables settings object
- * node:nPaging - the DIV which contains this pagination control
- * function:fnCallbackDraw - draw function which must be called on update
- */
- "fnInit": function ( oSettings, nPaging, fnCallbackDraw )
- {
- var oLang = oSettings.oLanguage.oPaginate;
- var oClasses = oSettings.oClasses;
- var fnClickHandler = function ( e ) {
- if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
- {
- fnCallbackDraw( oSettings );
+
+ full: function ( page, pages ) {
+ return [ 'first', 'previous', 'next', 'last' ];
+ },
+
+ numbers: function ( page, pages ) {
+ return [ _numbers(page, pages) ];
+ },
+
+ simple_numbers: function ( page, pages ) {
+ return [ 'previous', _numbers(page, pages), 'next' ];
+ },
+
+ full_numbers: function ( page, pages ) {
+ return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
+ },
+
+ // For testing and plug-ins to use
+ _numbers: _numbers,
+
+ // Number of number buttons (including ellipsis) to show. _Must be odd!_
+ numbers_length: 7
+ } );
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ pageButton: {
+ _: function ( settings, host, idx, buttons, page, pages ) {
+ var classes = settings.oClasses;
+ var lang = settings.oLanguage.oPaginate;
+ var aria = settings.oLanguage.oAria.paginate || {};
+ var btnDisplay, btnClass, counter=0;
+
+ var attach = function( container, buttons ) {
+ var i, ien, node, button;
+ var clickHandler = function ( e ) {
+ _fnPageChange( settings, e.data.action, true );
+ };
+
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
+ button = buttons[i];
+
+ if ( $.isArray( button ) ) {
+ var inner = $( '<'+(button.DT_el || 'div')+'/>' )
+ .appendTo( container );
+ attach( inner, button );
+ }
+ else {
+ btnDisplay = null;
+ btnClass = '';
+
+ switch ( button ) {
+ case 'ellipsis':
+ container.append('<span class="ellipsis">&#x2026;</span>');
+ break;
+
+ case 'first':
+ btnDisplay = lang.sFirst;
+ btnClass = button + (page > 0 ?
+ '' : ' '+classes.sPageButtonDisabled);
+ break;
+
+ case 'previous':
+ btnDisplay = lang.sPrevious;
+ btnClass = button + (page > 0 ?
+ '' : ' '+classes.sPageButtonDisabled);
+ break;
+
+ case 'next':
+ btnDisplay = lang.sNext;
+ btnClass = button + (page < pages-1 ?
+ '' : ' '+classes.sPageButtonDisabled);
+ break;
+
+ case 'last':
+ btnDisplay = lang.sLast;
+ btnClass = button + (page < pages-1 ?
+ '' : ' '+classes.sPageButtonDisabled);
+ break;
+
+ default:
+ btnDisplay = button + 1;
+ btnClass = page === button ?
+ classes.sPageButtonActive : '';
+ break;
+ }
+
+ if ( btnDisplay !== null ) {
+ node = $('<a>', {
+ 'class': classes.sPageButton+' '+btnClass,
+ 'aria-controls': settings.sTableId,
+ 'aria-label': aria[ button ],
+ 'data-dt-idx': counter,
+ 'tabindex': settings.iTabIndex,
+ 'id': idx === 0 && typeof button === 'string' ?
+ settings.sTableId +'_'+ button :
+ null
+ } )
+ .html( btnDisplay )
+ .appendTo( container );
+
+ _fnBindAction(
+ node, {action: button}, clickHandler
+ );
+
+ counter++;
+ }
+ }
}
};
- $(nPaging).append(
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageFirst+'">'+oLang.sFirst+'</a>'+
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPagePrevious+'">'+oLang.sPrevious+'</a>'+
- '<span></span>'+
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageNext+'">'+oLang.sNext+'</a>'+
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageLast+'">'+oLang.sLast+'</a>'
- );
- var els = $('a', nPaging);
- var nFirst = els[0],
- nPrev = els[1],
- nNext = els[2],
- nLast = els[3];
-
- oSettings.oApi._fnBindAction( nFirst, {action: "first"}, fnClickHandler );
- oSettings.oApi._fnBindAction( nPrev, {action: "previous"}, fnClickHandler );
- oSettings.oApi._fnBindAction( nNext, {action: "next"}, fnClickHandler );
- oSettings.oApi._fnBindAction( nLast, {action: "last"}, fnClickHandler );
-
- /* ID the first elements only */
- if ( !oSettings.aanFeatures.p )
- {
- nPaging.id = oSettings.sTableId+'_paginate';
- nFirst.id =oSettings.sTableId+'_first';
- nPrev.id =oSettings.sTableId+'_previous';
- nNext.id =oSettings.sTableId+'_next';
- nLast.id =oSettings.sTableId+'_last';
- }
- },
-
- /*
- * Function: oPagination.full_numbers.fnUpdate
- * Purpose: Update the list of page buttons shows
- * Returns: -
- * Inputs: object:oSettings - dataTables settings object
- * function:fnCallbackDraw - draw function to call on page change
- */
- "fnUpdate": function ( oSettings, fnCallbackDraw )
- {
- if ( !oSettings.aanFeatures.p )
- {
- return;
- }
-
- var iPageCount = DataTable.ext.oPagination.iFullNumbersShowPages;
- var iPageCountHalf = Math.floor(iPageCount / 2);
- var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
- var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
- var sList = "";
- var iStartButton, iEndButton, i, iLen;
- var oClasses = oSettings.oClasses;
- var anButtons, anStatic, nPaginateList, nNode;
- var an = oSettings.aanFeatures.p;
- var fnBind = function (j) {
- oSettings.oApi._fnBindAction( this, {"page": j+iStartButton-1}, function(e) {
- /* Use the information in the element to jump to the required page */
- oSettings.oApi._fnPageChange( oSettings, e.data.page );
- fnCallbackDraw( oSettings );
- e.preventDefault();
- } );
- };
-
- /* Pages calculation */
- if ( oSettings._iDisplayLength === -1 )
- {
- iStartButton = 1;
- iEndButton = 1;
- iCurrentPage = 1;
- }
- else if (iPages < iPageCount)
- {
- iStartButton = 1;
- iEndButton = iPages;
- }
- else if (iCurrentPage <= iPageCountHalf)
- {
- iStartButton = 1;
- iEndButton = iPageCount;
- }
- else if (iCurrentPage >= (iPages - iPageCountHalf))
- {
- iStartButton = iPages - iPageCount + 1;
- iEndButton = iPages;
- }
- else
- {
- iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
- iEndButton = iStartButton + iPageCount - 1;
- }
+ // IE9 throws an 'unknown error' if document.activeElement is used
+ // inside an iframe or frame. Try / catch the error. Not good for
+ // accessibility, but neither are frames.
+ var activeEl;
-
- /* Build the dynamic list */
- for ( i=iStartButton ; i<=iEndButton ; i++ )
- {
- sList += (iCurrentPage !== i) ?
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+'">'+oSettings.fnFormatNumber(i)+'</a>' :
- '<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButtonActive+'">'+oSettings.fnFormatNumber(i)+'</a>';
+ try {
+ // Because this approach is destroying and recreating the paging
+ // elements, focus is lost on the select button which is bad for
+ // accessibility. So we want to restore focus once the draw has
+ // completed
+ activeEl = $(host).find(document.activeElement).data('dt-idx');
}
-
- /* Loop over each instance of the pager */
- for ( i=0, iLen=an.length ; i<iLen ; i++ )
- {
- nNode = an[i];
- if ( !nNode.hasChildNodes() )
- {
- continue;
- }
-
- /* Build up the dynamic list first - html and listeners */
- $('span:eq(0)', nNode)
- .html( sList )
- .children('a').each( fnBind );
-
- /* Update the permanent button's classes */
- anButtons = nNode.getElementsByTagName('a');
- anStatic = [
- anButtons[0], anButtons[1],
- anButtons[anButtons.length-2], anButtons[anButtons.length-1]
- ];
-
- $(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
- $([anStatic[0], anStatic[1]]).addClass(
- (iCurrentPage==1) ?
- oClasses.sPageButtonStaticDisabled :
- oClasses.sPageButton
- );
- $([anStatic[2], anStatic[3]]).addClass(
- (iPages===0 || iCurrentPage===iPages || oSettings._iDisplayLength===-1) ?
- oClasses.sPageButtonStaticDisabled :
- oClasses.sPageButton
- );
+ catch (e) {}
+
+ attach( $(host).empty(), buttons );
+
+ if ( activeEl ) {
+ $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
}
}
}
} );
- $.extend( DataTable.ext.oSort, {
- /*
- * text sorting
- */
- "string-pre": function ( a )
- {
- if ( typeof a != 'string' ) {
- a = (a !== null && a.toString) ? a.toString() : '';
- }
- return a.toLowerCase();
- },
- "string-asc": function ( x, y )
- {
- return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- },
-
- "string-desc": function ( x, y )
- {
- return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- },
-
-
- /*
- * html sorting (ignore html tags)
- */
- "html-pre": function ( a )
- {
- return a.replace( /<.*?>/g, "" ).toLowerCase();
- },
-
- "html-asc": function ( x, y )
- {
- return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- },
-
- "html-desc": function ( x, y )
+
+ // Built in type detection. See model.ext.aTypes for information about
+ // what is required from this methods.
+ $.extend( DataTable.ext.type.detect, [
+ // Plain numbers - first since V8 detects some plain numbers as dates
+ // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
+ function ( d, settings )
{
- return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal ) ? 'num'+decimal : null;
},
-
-
- /*
- * date sorting
- */
- "date-pre": function ( a )
+
+ // Dates (only those recognised by the browser's Date.parse)
+ function ( d, settings )
{
- var x = Date.parse( a );
-
- if ( isNaN(x) || x==="" )
- {
- x = Date.parse( "01/01/1970 00:00:00" );
+ // V8 will remove any unknown characters at the start and end of the
+ // expression, leading to false matches such as `$245.12` or `10%` being
+ // a valid date. See forum thread 18941 for detail.
+ if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
+ return null;
}
- return x;
+ var parsed = Date.parse(d);
+ return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
},
- "date-asc": function ( x, y )
+ // Formatted numbers
+ function ( d, settings )
{
- return x - y;
+ var decimal = settings.oLanguage.sDecimal;
+ return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
},
-
- "date-desc": function ( x, y )
+
+ // HTML numeric
+ function ( d, settings )
{
- return y - x;
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
},
-
-
- /*
- * numerical sorting
- */
- "numeric-pre": function ( a )
+
+ // HTML numeric, formatted
+ function ( d, settings )
{
- return (a=="-" || a==="") ? 0 : a*1;
+ var decimal = settings.oLanguage.sDecimal;
+ return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
},
- "numeric-asc": function ( x, y )
+ // HTML (this is strict checking - there must be html)
+ function ( d, settings )
{
- return x - y;
+ return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
+ 'html' : null;
+ }
+ ] );
+
+
+
+ // Filter formatting functions. See model.ext.ofnSearch for information about
+ // what is required from these methods.
+ //
+ // Note that additional search methods are added for the html numbers and
+ // html formatted numbers by `_addNumericSort()` when we know what the decimal
+ // place is
+
+
+ $.extend( DataTable.ext.type.search, {
+ html: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data
+ .replace( _re_new_lines, " " )
+ .replace( _re_html, "" ) :
+ '';
},
-
- "numeric-desc": function ( x, y )
- {
- return y - x;
+
+ string: function ( data ) {
+ return _empty(data) ?
+ data :
+ typeof data === 'string' ?
+ data.replace( _re_new_lines, " " ) :
+ data;
}
} );
- $.extend( DataTable.ext.aTypes, [
- /*
- * Function: -
- * Purpose: Check to see if a string is numeric
- * Returns: string:'numeric' or null
- * Inputs: mixed:sText - string to check
- */
- function ( sData )
- {
- /* Allow zero length strings as a number */
- if ( typeof sData === 'number' )
- {
- return 'numeric';
- }
- else if ( typeof sData !== 'string' )
- {
- return null;
+
+ var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
+ if ( d !== 0 && (!d || d === '-') ) {
+ return -Infinity;
+ }
+
+ // If a decimal place other than `.` is used, it needs to be given to the
+ // function so we can detect it and replace with a `.` which is the only
+ // decimal place Javascript recognises - it is not locale aware.
+ if ( decimalPlace ) {
+ d = _numToDecimal( d, decimalPlace );
+ }
+
+ if ( d.replace ) {
+ if ( re1 ) {
+ d = d.replace( re1, '' );
}
-
- var sValidFirstChars = "0123456789-";
- var sValidChars = "0123456789.";
- var Char;
- var bDecimal = false;
-
- /* Check for a valid first char (no period and allow negatives) */
- Char = sData.charAt(0);
- if (sValidFirstChars.indexOf(Char) == -1)
- {
- return null;
+
+ if ( re2 ) {
+ d = d.replace( re2, '' );
}
-
- /* Check all the other characters are valid */
- for ( var i=1 ; i<sData.length ; i++ )
+ }
+
+ return d * 1;
+ };
+
+
+ // Add the numeric 'deformatting' functions for sorting and search. This is done
+ // in a function to provide an easy ability for the language options to add
+ // additional methods if a non-period decimal place is used.
+ function _addNumericSort ( decimalPlace ) {
+ $.each(
{
- Char = sData.charAt(i);
- if (sValidChars.indexOf(Char) == -1)
- {
- return null;
+ // Plain numbers
+ "num": function ( d ) {
+ return __numericReplace( d, decimalPlace );
+ },
+
+ // Formatted numbers
+ "num-fmt": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_formatted_numeric );
+ },
+
+ // HTML numeric
+ "html-num": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_html );
+ },
+
+ // HTML numeric, formatted
+ "html-num-fmt": function ( d ) {
+ return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
}
-
- /* Only allowed one decimal place... */
- if ( Char == "." )
- {
- if ( bDecimal )
- {
- return null;
- }
- bDecimal = true;
+ },
+ function ( key, fn ) {
+ // Add the ordering method
+ _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
+
+ // For HTML types add a search formatter that will strip the HTML
+ if ( key.match(/^html\-/) ) {
+ _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
}
}
-
- return 'numeric';
+ );
+ }
+
+
+ // Default sort methods
+ $.extend( _ext.type.order, {
+ // Dates
+ "date-pre": function ( d ) {
+ return Date.parse( d ) || 0;
},
-
- /*
- * Function: -
- * Purpose: Check to see if a string is actually a formatted date
- * Returns: string:'date' or null
- * Inputs: string:sText - string to check
- */
- function ( sData )
- {
- var iParse = Date.parse(sData);
- if ( (iParse !== null && !isNaN(iParse)) || (typeof sData === 'string' && sData.length === 0) )
- {
- return 'date';
- }
- return null;
+
+ // html
+ "html-pre": function ( a ) {
+ return _empty(a) ?
+ '' :
+ a.replace ?
+ a.replace( /<.*?>/g, "" ).toLowerCase() :
+ a+'';
},
-
- /*
- * Function: -
- * Purpose: Check to see if a string should be treated as an HTML string
- * Returns: string:'html' or null
- * Inputs: string:sText - string to check
- */
- function ( sData )
- {
- if ( typeof sData === 'string' && sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
- {
- return 'html';
+
+ // string
+ "string-pre": function ( a ) {
+ // This is a little complex, but faster than always calling toString,
+ // http://jsperf.com/tostring-v-check
+ return _empty(a) ?
+ '' :
+ typeof a === 'string' ?
+ a.toLowerCase() :
+ ! a.toString ?
+ '' :
+ a.toString();
+ },
+
+ // string-asc and -desc are retained only for compatibility with the old
+ // sort methods
+ "string-asc": function ( x, y ) {
+ return ((x < y) ? -1 : ((x > y) ? 1 : 0));
+ },
+
+ "string-desc": function ( x, y ) {
+ return ((x < y) ? 1 : ((x > y) ? -1 : 0));
+ }
+ } );
+
+
+ // Numeric sorting types - order doesn't matter here
+ _addNumericSort( '' );
+
+
+ $.extend( true, DataTable.ext.renderer, {
+ header: {
+ _: function ( settings, cell, column, classes ) {
+ // No additional mark-up required
+ // Attach a sort listener to update on sort - note that using the
+ // `DT` namespace will allow the event to be removed automatically
+ // on destroy, while the `dt` namespaced event is the one we are
+ // listening for
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) { // need to check this this is the host
+ return; // table, not a nested one
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass(
+ column.sSortingClass +' '+
+ classes.sSortAsc +' '+
+ classes.sSortDesc
+ )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+ } );
+ },
+
+ jqueryui: function ( settings, cell, column, classes ) {
+ $('<div/>')
+ .addClass( classes.sSortJUIWrapper )
+ .append( cell.contents() )
+ .append( $('<span/>')
+ .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
+ )
+ .appendTo( cell );
+
+ // Attach a sort listener to update on sort
+ $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
+ if ( settings !== ctx ) {
+ return;
+ }
+
+ var colIdx = column.idx;
+
+ cell
+ .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortDesc :
+ column.sSortingClass
+ );
+
+ cell
+ .find( 'span.'+classes.sSortIcon )
+ .removeClass(
+ classes.sSortJUIAsc +" "+
+ classes.sSortJUIDesc +" "+
+ classes.sSortJUI +" "+
+ classes.sSortJUIAscAllowed +" "+
+ classes.sSortJUIDescAllowed
+ )
+ .addClass( columns[ colIdx ] == 'asc' ?
+ classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
+ classes.sSortJUIDesc :
+ column.sSortingClassJUI
+ );
+ } );
}
- return null;
}
- ] );
+ } );
+
+ /*
+ * Public helper functions. These aren't used internally by DataTables, or
+ * called by any of the options passed into DataTables, but they can be used
+ * externally by developers working with DataTables. They are helper functions
+ * to make working with DataTables a little bit easier.
+ */
+
+ var __htmlEscapeEntities = function ( d ) {
+ return typeof d === 'string' ?
+ d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
+ d;
+ };
+
+ /**
+ * Helpers for `columns.render`.
+ *
+ * The options defined here can be used with the `columns.render` initialisation
+ * option to provide a display renderer. The following functions are defined:
+ *
+ * * `number` - Will format numeric data (defined by `columns.data`) for
+ * display, retaining the original unformatted data for sorting and filtering.
+ * It takes 5 parameters:
+ * * `string` - Thousands grouping separator
+ * * `string` - Decimal point indicator
+ * * `integer` - Number of decimal points to show
+ * * `string` (optional) - Prefix.
+ * * `string` (optional) - Postfix (/suffix).
+ * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
+ * parameters.
+ *
+ * @example
+ * // Column definition using the number renderer
+ * {
+ * data: "salary",
+ * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
+ * }
+ *
+ * @namespace
+ */
+ DataTable.render = {
+ number: function ( thousands, decimal, precision, prefix, postfix ) {
+ return {
+ display: function ( d ) {
+ if ( typeof d !== 'number' && typeof d !== 'string' ) {
+ return d;
+ }
+
+ var negative = d < 0 ? '-' : '';
+ var flo = parseFloat( d );
+
+ // If NaN then there isn't much formatting that we can do - just
+ // return immediately, escaping any HTML (this was supposed to
+ // be a number after all)
+ if ( isNaN( flo ) ) {
+ return __htmlEscapeEntities( d );
+ }
+
+ d = Math.abs( flo );
+
+ var intPart = parseInt( d, 10 );
+ var floatPart = precision ?
+ decimal+(d - intPart).toFixed( precision ).substring( 2 ):
+ '';
+
+ return negative + (prefix||'') +
+ intPart.toString().replace(
+ /\B(?=(\d{3})+(?!\d))/g, thousands
+ ) +
+ floatPart +
+ (postfix||'');
+ }
+ };
+ },
+
+ text: function () {
+ return {
+ display: __htmlEscapeEntities
+ };
+ }
+ };
+
+
+ /*
+ * This is really a good bit rubbish this method of exposing the internal methods
+ * publicly... - To be fixed in 2.0 using methods on the prototype
+ */
+
+
+ /**
+ * Create a wrapper function for exporting an internal functions to an external API.
+ * @param {string} fn API function name
+ * @returns {function} wrapped function
+ * @memberof DataTable#internal
+ */
+ function _fnExternApiFunc (fn)
+ {
+ return function() {
+ var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
+ Array.prototype.slice.call(arguments)
+ );
+ return DataTable.ext.internal[fn].apply( this, args );
+ };
+ }
+
+
+ /**
+ * Reference to internal functions for use by plug-in developers. Note that
+ * these methods are references to internal functions and are considered to be
+ * private. If you use these methods, be aware that they are liable to change
+ * between versions.
+ * @namespace
+ */
+ $.extend( DataTable.ext.internal, {
+ _fnExternApiFunc: _fnExternApiFunc,
+ _fnBuildAjax: _fnBuildAjax,
+ _fnAjaxUpdate: _fnAjaxUpdate,
+ _fnAjaxParameters: _fnAjaxParameters,
+ _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
+ _fnAjaxDataSrc: _fnAjaxDataSrc,
+ _fnAddColumn: _fnAddColumn,
+ _fnColumnOptions: _fnColumnOptions,
+ _fnAdjustColumnSizing: _fnAdjustColumnSizing,
+ _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
+ _fnColumnIndexToVisible: _fnColumnIndexToVisible,
+ _fnVisbleColumns: _fnVisbleColumns,
+ _fnGetColumns: _fnGetColumns,
+ _fnColumnTypes: _fnColumnTypes,
+ _fnApplyColumnDefs: _fnApplyColumnDefs,
+ _fnHungarianMap: _fnHungarianMap,
+ _fnCamelToHungarian: _fnCamelToHungarian,
+ _fnLanguageCompat: _fnLanguageCompat,
+ _fnBrowserDetect: _fnBrowserDetect,
+ _fnAddData: _fnAddData,
+ _fnAddTr: _fnAddTr,
+ _fnNodeToDataIndex: _fnNodeToDataIndex,
+ _fnNodeToColumnIndex: _fnNodeToColumnIndex,
+ _fnGetCellData: _fnGetCellData,
+ _fnSetCellData: _fnSetCellData,
+ _fnSplitObjNotation: _fnSplitObjNotation,
+ _fnGetObjectDataFn: _fnGetObjectDataFn,
+ _fnSetObjectDataFn: _fnSetObjectDataFn,
+ _fnGetDataMaster: _fnGetDataMaster,
+ _fnClearTable: _fnClearTable,
+ _fnDeleteIndex: _fnDeleteIndex,
+ _fnInvalidate: _fnInvalidate,
+ _fnGetRowElements: _fnGetRowElements,
+ _fnCreateTr: _fnCreateTr,
+ _fnBuildHead: _fnBuildHead,
+ _fnDrawHead: _fnDrawHead,
+ _fnDraw: _fnDraw,
+ _fnReDraw: _fnReDraw,
+ _fnAddOptionsHtml: _fnAddOptionsHtml,
+ _fnDetectHeader: _fnDetectHeader,
+ _fnGetUniqueThs: _fnGetUniqueThs,
+ _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
+ _fnFilterComplete: _fnFilterComplete,
+ _fnFilterCustom: _fnFilterCustom,
+ _fnFilterColumn: _fnFilterColumn,
+ _fnFilter: _fnFilter,
+ _fnFilterCreateSearch: _fnFilterCreateSearch,
+ _fnEscapeRegex: _fnEscapeRegex,
+ _fnFilterData: _fnFilterData,
+ _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
+ _fnUpdateInfo: _fnUpdateInfo,
+ _fnInfoMacros: _fnInfoMacros,
+ _fnInitialise: _fnInitialise,
+ _fnInitComplete: _fnInitComplete,
+ _fnLengthChange: _fnLengthChange,
+ _fnFeatureHtmlLength: _fnFeatureHtmlLength,
+ _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
+ _fnPageChange: _fnPageChange,
+ _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
+ _fnProcessingDisplay: _fnProcessingDisplay,
+ _fnFeatureHtmlTable: _fnFeatureHtmlTable,
+ _fnScrollDraw: _fnScrollDraw,
+ _fnApplyToChildren: _fnApplyToChildren,
+ _fnCalculateColumnWidths: _fnCalculateColumnWidths,
+ _fnThrottle: _fnThrottle,
+ _fnConvertToWidth: _fnConvertToWidth,
+ _fnGetWidestNode: _fnGetWidestNode,
+ _fnGetMaxLenString: _fnGetMaxLenString,
+ _fnStringToCss: _fnStringToCss,
+ _fnSortFlatten: _fnSortFlatten,
+ _fnSort: _fnSort,
+ _fnSortAria: _fnSortAria,
+ _fnSortListener: _fnSortListener,
+ _fnSortAttachListener: _fnSortAttachListener,
+ _fnSortingClasses: _fnSortingClasses,
+ _fnSortData: _fnSortData,
+ _fnSaveState: _fnSaveState,
+ _fnLoadState: _fnLoadState,
+ _fnSettingsFromNode: _fnSettingsFromNode,
+ _fnLog: _fnLog,
+ _fnMap: _fnMap,
+ _fnBindAction: _fnBindAction,
+ _fnCallbackReg: _fnCallbackReg,
+ _fnCallbackFire: _fnCallbackFire,
+ _fnLengthOverflow: _fnLengthOverflow,
+ _fnRenderer: _fnRenderer,
+ _fnDataSource: _fnDataSource,
+ _fnRowAttributes: _fnRowAttributes,
+ _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
+ // in 1.10, so this dead-end function is
+ // added to prevent errors
+ } );
- // jQuery aliases
- $.fn.DataTable = DataTable;
+ // jQuery access
$.fn.dataTable = DataTable;
+
+ // Provide access to the host jQuery object (circular reference)
+ DataTable.$ = $;
+
+ // Legacy aliases
$.fn.dataTableSettings = DataTable.settings;
$.fn.dataTableExt = DataTable.ext;
+ // With a capital `D` we return a DataTables API instance rather than a
+ // jQuery object
+ $.fn.DataTable = function ( opts ) {
+ return $(this).dataTable( opts ).api();
+ };
+
+ // All properties that are available to $.fn.dataTable should also be
+ // available on $.fn.DataTable
+ $.each( DataTable, function ( prop, val ) {
+ $.fn.DataTable[ prop ] = val;
+ } );
+
// Information about events fired by DataTables - for documentation.
/**
- * Draw event, fired whenever the table is redrawn on the page, at the same point as
- * fnDrawCallback. This may be useful for binding events or performing calculations when
- * the table is altered at all.
- * @name DataTable#draw
+ * Draw event, fired whenever the table is redrawn on the page, at the same
+ * point as fnDrawCallback. This may be useful for binding events or
+ * performing calculations when the table is altered at all.
+ * @name DataTable#draw.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
*/
/**
- * Filter event, fired when the filtering applied to the table (using the build in global
- * global filter, or column filters) is altered.
- * @name DataTable#filter
+ * Search event, fired when the searching applied to the table (using the
+ * built-in global search, or column filters) is altered.
+ * @name DataTable#search.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
@@ -12003,24 +15124,24 @@
/**
* Page change event, fired when the paging of the table is altered.
- * @name DataTable#page
+ * @name DataTable#page.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
*/
/**
- * Sort event, fired when the sorting applied to the table is altered.
- * @name DataTable#sort
+ * Order event, fired when the ordering applied to the table is altered.
+ * @name DataTable#order.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
*/
/**
- * DataTables initialisation complete event, fired when the table is fully drawn,
- * including Ajax data loaded, if Ajax data is required.
- * @name DataTable#init
+ * DataTables initialisation complete event, fired when the table is fully
+ * drawn, including Ajax data loaded, if Ajax data is required.
+ * @name DataTable#init.dt
* @event
* @param {event} e jQuery event object
* @param {object} oSettings DataTables settings object
@@ -12029,11 +15150,11 @@
*/
/**
- * State save event, fired when the table has changed state a new state save is required.
- * This method allows modification of the state saving object prior to actually doing the
- * save, including addition or other state properties (for plug-ins) or modification
- * of a DataTables core property.
- * @name DataTable#stateSaveParams
+ * State save event, fired when the table has changed state a new state save
+ * is required. This event allows modification of the state saving object
+ * prior to actually doing the save, including addition or other state
+ * properties (for plug-ins) or modification of a DataTables core property.
+ * @name DataTable#stateSaveParams.dt
* @event
* @param {event} e jQuery event object
* @param {object} oSettings DataTables settings object
@@ -12041,10 +15162,11 @@
*/
/**
- * State load event, fired when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state - allowing modification
- * of the saved state is required or loading of state for a plug-in.
- * @name DataTable#stateLoadParams
+ * State load event, fired when the table is loading state from the stored
+ * data, but prior to the settings object being modified by the saved state
+ * - allowing modification of the saved state is required or loading of
+ * state for a plug-in.
+ * @name DataTable#stateLoadParams.dt
* @event
* @param {event} e jQuery event object
* @param {object} oSettings DataTables settings object
@@ -12052,9 +15174,9 @@
*/
/**
- * State loaded event, fired when state has been loaded from stored data and the settings
- * object has been modified by the loaded data.
- * @name DataTable#stateLoaded
+ * State loaded event, fired when state has been loaded from stored data and
+ * the settings object has been modified by the loaded data.
+ * @name DataTable#stateLoaded.dt
* @event
* @param {event} e jQuery event object
* @param {object} oSettings DataTables settings object
@@ -12062,10 +15184,11 @@
*/
/**
- * Processing event, fired when DataTables is doing some kind of processing (be it,
- * sort, filter or anything else). Can be used to indicate to the end user that
- * there is something happening, or that something has finished.
- * @name DataTable#processing
+ * Processing event, fired when DataTables is doing some kind of processing
+ * (be it, order, searcg or anything else). It can be used to indicate to
+ * the end user that there is something happening, or that something has
+ * finished.
+ * @name DataTable#processing.dt
* @event
* @param {event} e jQuery event object
* @param {object} oSettings DataTables settings object
@@ -12073,27 +15196,73 @@
*/
/**
- * Ajax (XHR) event, fired whenever an Ajax request is completed from a request to
- * made to the server for new data (note that this trigger is called in fnServerData,
- * if you override fnServerData and which to use this event, you need to trigger it in
- * you success function).
- * @name DataTable#xhr
+ * Ajax (XHR) event, fired whenever an Ajax request is completed from a
+ * request to made to the server for new data. This event is called before
+ * DataTables processed the returned data, so it can also be used to pre-
+ * process the data returned from the server, if needed.
+ *
+ * Note that this trigger is called in `fnServerData`, if you override
+ * `fnServerData` and which to use this event, you need to trigger it in you
+ * success function.
+ * @name DataTable#xhr.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
* @param {object} json JSON returned from the server
+ *
+ * @example
+ * // Use a custom property returned from the server in another DOM element
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
+ * $('#status').html( json.status );
+ * } );
+ *
+ * @example
+ * // Pre-process the data returned from the server
+ * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
+ * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
+ * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
+ * }
+ * // Note no return - manipulate the data directly in the JSON object.
+ * } );
*/
/**
- * Destroy event, fired when the DataTable is destroyed by calling fnDestroy or passing
- * the bDestroy:true parameter in the initialisation object. This can be used to remove
- * bound events, added DOM nodes, etc.
- * @name DataTable#destroy
+ * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
+ * or passing the bDestroy:true parameter in the initialisation object. This
+ * can be used to remove bound events, added DOM nodes, etc.
+ * @name DataTable#destroy.dt
* @event
* @param {event} e jQuery event object
* @param {object} o DataTables settings object {@link DataTable.models.oSettings}
*/
-}));
-}(window, document));
+ /**
+ * Page length change event, fired when number of records to show on each
+ * page (the length) is changed.
+ * @name DataTable#length.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ * @param {integer} len New length
+ */
+ /**
+ * Column sizing has changed.
+ * @name DataTable#column-sizing.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ */
+
+ /**
+ * Column visibility has changed.
+ * @name DataTable#column-visibility.dt
+ * @event
+ * @param {event} e jQuery event object
+ * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
+ * @param {int} column Column index
+ * @param {bool} vis `false` if column now hidden, or `true` if visible
+ */
+
+ return $.fn.dataTable;
+}));
diff --git a/themes/elegant/theme.css b/themes/elegant/theme.css
index 83f130deb..3fb589222 100644
--- a/themes/elegant/theme.css
+++ b/themes/elegant/theme.css
@@ -264,6 +264,23 @@ a:hover { border-bottom: none;}
#pictureComments INPUT[type=submit] { margin: 0;}
.commentElement .description { padding: 5px;}
+/* tag page */
+
+FIELDSET.tagLetter, LEGEND.tagLetterLegend, TR.tagLine {
+ border-color: #333;
+}
+
+FIELDSET.tagLetter {
+ width:250px;
+}
+
+TABLE.tagLetterContent {
+ font-size:inherit;
+}
+
+TD.nbEntries {
+ white-space: nowrap;
+}
/* calendar */
diff --git a/upgrade.php b/upgrade.php
index 11b745b6d..6d7c12638 100644
--- a/upgrade.php
+++ b/upgrade.php
@@ -466,22 +466,12 @@ REPLACE INTO '.PLUGINS_TABLE.'
if (file_exists(PHPWG_PLUGINS_PATH .'TakeATour/tours/'.$version_.'/config.inc.php'))
{
- load_language(
- 'plugin.lang',
- PHPWG_PLUGINS_PATH.'TakeATour/',
- array(
- 'language' => $language,
- 'force_fallback'=>'en_UK',
- )
- );
-
// we need the secret key for get_pwg_token()
load_conf_from_db();
$template->assign(
array(
- // TODO find a better way to do that, with a core string in English
- 'button_label' => str_replace('2.7', get_branch_from_version(PHPWG_VERSION), l10n('2_7_0_descrp')),
+ 'button_label' => l10n('Discover what\'s new in Piwigo %s', get_branch_from_version(PHPWG_VERSION)),
'button_link' => 'admin.php?submited_tour_path=tours/'.$version_.'&amp;pwg_token='.get_pwg_token(),
)
);