diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index f0b72099d..acbba568d 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -11,6 +11,7 @@ class LibraryController extends Zend_Controller_Action $ajaxContext = $this->_helper->getHelper('AjaxContext'); $ajaxContext->addActionContext('contents', 'json') ->addActionContext('delete', 'json') + ->addActionContext('delete-group', 'json') ->addActionContext('context-menu', 'json') ->addActionContext('get-file-meta-data', 'html') ->addActionContext('upload-file-soundcloud', 'json') @@ -31,12 +32,16 @@ class LibraryController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.pluginAPI.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.fnSetFilteringDelay.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js','text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js','text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/advancedsearch.js','text/javascript'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css'); $this->view->headLink()->appendStylesheet($baseUrl.'/css/contextmenu.css'); - + $this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColVis.css'); + $this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColReorder.css'); $this->_helper->layout->setLayout('library'); $this->_helper->viewRenderer->setResponseSegment('library'); @@ -178,10 +183,50 @@ class LibraryController extends Zend_Controller_Action $this->view->id = $id; } } + + public function deleteGroupAction() + { + $ids = $this->_getParam('ids'); + $userInfo = Zend_Auth::getInstance()->getStorage()->read(); + $user = new Application_Model_User($userInfo->id); + + if ($user->isAdmin()) { + + if (!is_null($ids)) { + foreach ($ids as $key => $id) { + $file = Application_Model_StoredFile::Recall($id); + + if (PEAR::isError($file)) { + $this->view->message = $file->getMessage(); + return; + } + else if(is_null($file)) { + $this->view->message = "file doesn't exist"; + return; + } + + $res = $file->delete(); + + if (PEAR::isError($res)) { + $this->view->message = $res->getMessage(); + return; + } + else { + $res = settype($res, "integer"); + $data = array("filepath" => $file->getFilePath(), "delete" => $res); + Application_Model_RabbitMq::SendMessageToMediaMonitor("file_delete", $data); + } + } + + $this->view->ids = $ids; + } + } + } public function contentsAction() { $post = $this->getRequest()->getPost(); + Logging::log(print_r($post, true)); $datatables = Application_Model_StoredFile::searchFilesForPlaylistBuilder($post); //format clip lengh to 1 decimal diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php index 9c3bc11ec..7618d94c9 100644 --- a/airtime_mvc/application/controllers/PlaylistController.php +++ b/airtime_mvc/application/controllers/PlaylistController.php @@ -8,18 +8,20 @@ class PlaylistController extends Zend_Controller_Action { $ajaxContext = $this->_helper->getHelper('AjaxContext'); $ajaxContext->addActionContext('add-item', 'json') - ->addActionContext('delete-item', 'json') - ->addActionContext('set-fade', 'json') - ->addActionContext('set-cue', 'json') - ->addActionContext('move-item', 'json') - ->addActionContext('close', 'json') - ->addActionContext('new', 'json') - ->addActionContext('edit', 'json') - ->addActionContext('delete-active', 'json') - ->addActionContext('delete', 'json') + ->addActionContext('delete-item', 'json') + ->addActionContext('add-group', 'json') + ->addActionContext('delete-group', 'json') + ->addActionContext('set-fade', 'json') + ->addActionContext('set-cue', 'json') + ->addActionContext('move-item', 'json') + ->addActionContext('close', 'json') + ->addActionContext('new', 'json') + ->addActionContext('edit', 'json') + ->addActionContext('delete-active', 'json') + ->addActionContext('delete', 'json') ->addActionContext('set-playlist-fades', 'json') ->addActionContext('set-playlist-name', 'json') - ->addActionContext('set-playlist-description', 'json') + ->addActionContext('set-playlist-description', 'json') ->initContext(); $this->pl_sess = new Zend_Session_Namespace(UI_PLAYLIST_SESSNAME); @@ -209,6 +211,59 @@ class PlaylistController extends Zend_Controller_Action unset($this->view->pl); } + public function addGroupAction() + { + $ids = $this->_getParam('ids'); + $pos = $this->_getParam('pos', null); + + if (!is_null($ids)) { + $pl = $this->getPlaylist(); + if ($pl === false) { + $this->view->playlist_error = true; + return false; + } + + foreach ($ids as $key => $value) { + $res = $pl->addAudioClip($value); + if (PEAR::isError($res)) { + $this->view->message = $res->getMessage(); + break; + } + } + + $this->view->pl = $pl; + $this->view->html = $this->view->render('playlist/update.phtml'); + $this->view->name = $pl->getName(); + $this->view->length = $pl->getLength(); + $this->view->description = $pl->getDescription(); + return; + } + $this->view->message = "a file is not chosen"; + } + + public function deleteGroupAction() + { + $ids = $this->_getParam('ids', null); + + foreach ($ids as $key => $id) { + $pl = Application_Model_Playlist::Recall($id); + + if ($pl !== FALSE) { + Application_Model_Playlist::Delete($id); + $pl_sess = $this->pl_sess; + if($pl_sess->id === $id){ + unset($pl_sess->id); + } + } else { + $this->view->playlist_error = true; + return false; + } + } + + $this->view->ids = $ids; + $this->view->html = $this->view->render('playlist/index.phtml'); + } + public function setCueAction() { $pos = $this->_getParam('pos'); diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index d48c3c420..3e98b0d7e 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -488,6 +488,7 @@ class Application_Model_StoredFile { { $file = new CcFiles(); $file->setDbGunid(md5(uniqid("", true))); + $file->setDbMtime(new DateTime("now"), new DateTimeZone("UTC")); $storedFile = new Application_Model_StoredFile(); $storedFile->_file = $file; @@ -628,50 +629,52 @@ class Application_Model_StoredFile { return $res; } - public static function searchFilesForPlaylistBuilder($datatables) { - global $CC_CONFIG; + public static function searchFilesForPlaylistBuilder($datatables) + { + global $CC_CONFIG; - $displayData = array("track_title", "artist_name", "album_title", "genre", "length", "ftype"); + $displayData = array("track_title", "artist_name", "album_title", "genre", "length", "year", "upload_time", "ftype"); - $plSelect = "SELECT "; + $plSelect = "SELECT "; $fileSelect = "SELECT "; - foreach ($displayData as $key){ + foreach ($displayData as $key) { - if($key === "track_title"){ + if ($key === "track_title") { $plSelect .= "name AS ".$key.", "; $fileSelect .= $key.", "; - } - else if ($key === "ftype"){ + } else if ($key === "ftype") { $plSelect .= "'playlist' AS ".$key.", "; $fileSelect .= $key.", "; - } - else if ($key === "artist_name"){ + } else if ($key === "artist_name") { $plSelect .= "creator AS ".$key.", "; $fileSelect .= $key.", "; - } - else if ($key === "length"){ + } else if ($key === "length") { $plSelect .= $key.", "; $fileSelect .= $key.", "; - } - else { + } else if ($key === "year") { + $plSelect .= "CAST(mtime AS varchar) AS ".$key.", "; + $fileSelect .= $key.", "; + } else if ($key === "upload_time") { + $plSelect .= "mtime AS ".$key.", "; + $fileSelect .= "mtime AS ".$key.", "; + } else { $plSelect .= "NULL AS ".$key.", "; $fileSelect .= $key.", "; } } - $fromTable = " ((".$plSelect."PL.id - FROM ".$CC_CONFIG["playListTable"]." AS PL - LEFT JOIN ".$CC_CONFIG['playListTimeView']." AS PLT USING(id)) + $fromTable = " ((".$plSelect."PL.id + FROM ".$CC_CONFIG["playListTable"]." AS PL + LEFT JOIN ".$CC_CONFIG['playListTimeView']." AS PLT USING(id)) - UNION + UNION - (".$fileSelect."id FROM ".$CC_CONFIG["filesTable"]." AS FILES)) AS RESULTS"; + (".$fileSelect."id FROM ".$CC_CONFIG["filesTable"]." AS FILES)) AS RESULTS"; + + return Application_Model_StoredFile::searchFiles($fromTable, $datatables); + } - return Application_Model_StoredFile::searchFiles($fromTable, $datatables); - - } - - public static function searchPlaylistsForSchedule($datatables) + public static function searchPlaylistsForSchedule($datatables) { $fromTable = "cc_playlist AS pl LEFT JOIN cc_playlisttimes AS plt USING(id) LEFT JOIN cc_subjs AS sub ON pl.editedby = sub.id"; //$datatables["optWhere"][] = "INTERVAL '{$time_remaining}' > INTERVAL '00:00:00'"; @@ -690,7 +693,12 @@ class Application_Model_StoredFile { $searchTerms = explode(" ", $data["sSearch"]); $selectorCount = "SELECT COUNT(*)"; - $selectorRows = "SELECT ". join("," , $columnsDisplayed); + foreach( $columnsDisplayed as $key=>$col){ + if($col == ''){ + unset($columnsDisplayed[$key]); + } + } + $selectorRows = "SELECT " . join(',', $columnsDisplayed ); $sql = $selectorCount." FROM ".$fromTable; $totalRows = $CC_DBC->getOne($sql); @@ -733,7 +741,7 @@ class Application_Model_StoredFile { // End Order By clause //ordered by integer as expected by datatables. - $CC_DBC->setFetchMode(DB_FETCHMODE_ORDERED); + //$CC_DBC->setFetchMode(DB_FETCHMODE_ORDERED); if(isset($where)) { $where = join(" AND ", $where); @@ -746,6 +754,18 @@ class Application_Model_StoredFile { } $results = $CC_DBC->getAll($sql); + foreach($results as &$row){ + // add checkbox row + $row['checkbox'] = ""; + + // a full timestamp is being returned for playlists' year column; + // split it and grab only the year info + $yearSplit = explode('-', $row['year']); + $row['year'] = $yearSplit[0]; + } + //$results['checkbox'] + //$results = $CC_DBC->getAssoc($sql); + Logging::log(print_r($results, true)); //echo $results; //echo $sql; diff --git a/airtime_mvc/public/css/datatables/css/ColReorder.css b/airtime_mvc/public/css/datatables/css/ColReorder.css new file mode 100644 index 000000000..6bb55e22c --- /dev/null +++ b/airtime_mvc/public/css/datatables/css/ColReorder.css @@ -0,0 +1,14 @@ +/* + * Namespace DTCR - "DataTables ColReorder" plug-in + */ + +table.DTCR_clonedTable { + background-color: white; + z-index: 998; +} + +div.DTCR_pointer { + width: 1px; + background-color: #5B5B5B; + z-index: 997; +} \ No newline at end of file diff --git a/airtime_mvc/public/css/datatables/css/ColVis.css b/airtime_mvc/public/css/datatables/css/ColVis.css new file mode 100644 index 000000000..95987b876 --- /dev/null +++ b/airtime_mvc/public/css/datatables/css/ColVis.css @@ -0,0 +1,75 @@ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * ColVis styles + */ +.ColVis { + float: right; + margin-bottom: 1em; +} + +.ColVis_Button { + position: relative; + float: left; + margin-right: 3px; + padding: 3px 5px; + height: 30px; + background-color: #fff; + border: 1px solid #d0d0d0; + cursor: pointer; +} + +button.ColVis_Button::-moz-focus-inner { + border: none !important; + padding: 0; +} + +.ColVis_text_hover { + border: 1px solid #999; + background-color: #f0f0f0; +} + +div.ColVis_collectionBackground { + background-color: black; + z-index: 996; +} + +div.ColVis_collection { + position: relative; + width: 130px; + background-color: #999; + padding: 3px; + border: 1px solid #ccc; + z-index: 998; +} + +div.ColVis_collection button.ColVis_Button { + background-color: white; + width: 100%; + float: none; + margin-bottom: 2px; +} + +div.ColVis_catcher { + position: absolute; + z-index: 997; +} + +.disabled { + color: #999; +} + + + +button.ColVis_Button { + text-align: left; +} + +div.ColVis_collection button.ColVis_Button:hover { + border: 1px solid #999; + background-color: #f0f0f0; +} + +span.ColVis_radio { + display: inline-block; + width: 20px; +} diff --git a/airtime_mvc/public/css/media_library.css b/airtime_mvc/public/css/media_library.css index dbef3052a..925baa36a 100644 --- a/airtime_mvc/public/css/media_library.css +++ b/airtime_mvc/public/css/media_library.css @@ -66,4 +66,12 @@ -moz-user-select: none; -o-user-select: none; user-select: none; -} \ No newline at end of file +} + +.datatable_checkbox { + text-align: center; +} + +.datatable_checkbox .DataTables_sort_wrapper { + text-align: center; +} diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 7b95307f7..e2acc5daa 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -607,10 +607,10 @@ dl.inline-list dd { } .dataTables_filter input { background: url("images/search_auto_bg.png") no-repeat scroll 0 0 #DDDDDD; - width: 60%; - border: 1px solid #5B5B5B; - margin: 0; - padding: 4px 3px 4px 25px; + width: 60%; + border: 1px solid #5B5B5B; + margin-left: -8px; + padding: 4px 3px 4px 25px; } .dataTables_length select { background-color: #DDDDDD; @@ -622,6 +622,30 @@ dl.inline-list dd { padding: 2px 2px 2px 0; vertical-align: top; } +#library_display thead th, #library_display tbody td { + cursor: pointer; +} +#library_display thead th.library_checkbox, +#library_display thead th.library_id, +#library_display thead th.library_title { + cursor: default; +} + +.ColVis.TableTools .ui-button { + height: 21px; +} +button.ColVis_Button.ColVis_ShowAll { + text-align: center; + margin-top: 10px; +} +.library_toolbar .ui-button, .ColVis.TableTools .ui-button { + float: right; + text-align:center; + font-size:12px; + font-weight:normal; + padding: 0.2em 1em; + margin: 0.5em 0.2em -0.5em 0.2em; +} /*----END Data Table----*/ diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index eefa73ccf..0bd77535a 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -1,13 +1,17 @@ +var dTable; +var checkedCount = 0; +var checkedPLCount = 0; + //used by jjmenu function getId() { - var tr_id = $(this.triggerElement).attr("id"); + var tr_id = $(this.triggerElement).parent().attr("id"); tr_id = tr_id.split("_"); return tr_id[1]; } function getType() { - var tr_id = $(this.triggerElement).attr("id"); + var tr_id = $(this.triggerElement).parent().attr("id"); tr_id = tr_id.split("_"); return tr_id[0]; @@ -30,8 +34,20 @@ function deleteAudioClip(json) { return; } - deleteItem("au", json.id); + if (json.ids != undefined) { + for (var i = json.ids.length - 1; i >= 0; i--) { + deleteItem("au", json.ids[i]); + } + } else if (json.id != undefined) { + deleteItem("au", json.id); + } location.reload(true); +} + +function confirmDeleteGroup() { + if(confirm('Are you sure you want to delete the selected items?')){ + groupDelete(); + } } //callbacks called by jjmenu @@ -73,11 +89,17 @@ function checkImportStatus(){ function deletePlaylist(json) { if(json.message) { - alert(json.message); - return; + alert(json.message); + return; } - - deleteItem("pl", json.id); + + if (json.ids != undefined) { + for (var i = json.ids.length - 1; i >= 0; i--) { + deleteItem("pl", json.ids[i]); + } + } else if (json.id != undefined) { + deleteItem("pl", json.id); + } window.location.reload(); } //end callbacks called by jjmenu @@ -90,7 +112,7 @@ function addLibraryItemEvents() { cursor: 'pointer' }); - $('#library_display tbody tr') + $('#library_display tbody tr td').not('[class=library_checkbox]') .jjmenu("click", [{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}], {id: getId, type: getType}, @@ -101,36 +123,36 @@ function addLibraryItemEvents() { function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { var id, type, once; - type = aData[6].substring(0,2); - id = aData[0]; - + type = aData["ftype"].substring(0,2); + id = aData["id"]; + if(type == "au") { - $('td:eq(5)', nRow).html( '' ); - } - else if(type == "pl") { - $('td:eq(5)', nRow).html( '' ); + $('td.library_type', nRow).html( '' ); + } else if(type == "pl") { + $('td.library_type', nRow).html( '' ); } - $(nRow).attr("id", type+'_'+id); + $(nRow).attr("id", type+'_'+id); - // insert id on lenth field - $('td:eq(4)', nRow).attr("id", "length"); + // insert id on lenth field + $('td.library_length', nRow).attr("id", "length"); - return nRow; + return nRow; } function dtDrawCallback() { - addLibraryItemEvents(); - addMetadataQtip(); - saveNumEntriesSetting(); + addLibraryItemEvents(); + addMetadataQtip(); + saveNumEntriesSetting(); + setupGroupActions(); } function addProgressIcon(id) { - if($("#au_"+id).find("td:eq(0)").find("span").length > 0){ - $("#au_"+id).find("td:eq(0)").find("span").removeClass(); + if($("#au_"+id).find("td.library_title").find("span").length > 0){ + $("#au_"+id).find("td.library_title").find("span").removeClass(); $("span[id="+id+"]").addClass("small-icon progress"); }else{ - $("#au_"+id).find("td:eq(0)").append('') + $("#au_"+id).find("td.library_title").append('') } } @@ -229,7 +251,7 @@ function addQtipToSCIcons(){ function addMetadataQtip(){ var tableRow = $('#library_display tbody tr'); tableRow.each(function(){ - var title = $(this).find('td:eq(0)').html() + var title = $(this).find('td.library_title').html() var info = $(this).attr("id") info = info.split("_"); var id = info[1]; @@ -297,31 +319,215 @@ function getNumEntriesPreference(data) { return parseInt(data.libraryInit.numEntries); } +function groupAdd() { + if (checkedPLCount > 0) { + alert("Can't add playlist to another playlist"); + return; + } + disableGroupBtn('library_group_add'); + + var ids = new Array(); + var addGroupUrl = '/Playlist/add-group'; + var newSPLUrl = '/Playlist/new/format/json'; + var dirty = true; + $('#library_display tbody tr').each(function() { + var idSplit = $(this).attr('id').split("_"); + var id = idSplit.pop(); + var type = idSplit.pop(); + if (dirty && $(this).find(":checkbox").attr("checked")) { + if (type == "au") { + ids.push(id); + } else if (type == "pl") { + alert("Can't add playlist to another playlist"); + dirty = false; + } + } + }); + + if (dirty && ids.length > 0) { + stopAudioPreview(); + + if ($('#spl_sortable').length == 0) { + $.post(newSPLUrl, function(json) { + openDiffSPL(json); + redrawDataTablePage(); + + $.post(addGroupUrl, {format: "json", ids: ids}, setSPLContent); + }); + } else { + $.post(addGroupUrl, {format: "json", ids: ids}, setSPLContent); + } + } +} + +function groupDelete() { + disableGroupBtn('library_group_delete'); + + var auIds = new Array(); + var plIds = new Array(); + var auUrl = '/Library/delete-group'; + var plUrl = '/Playlist/delete-group'; + var dirty = true; + $('#library_display tbody tr').each(function() { + var idSplit = $(this).attr('id').split("_"); + var id = idSplit.pop(); + var type = idSplit.pop(); + if (dirty && $(this).find(":checkbox").attr("checked")) { + if (type == "au") { + auIds.push(id); + } else if (type == "pl") { + plIds.push(id); + } + } + }); + + if (dirty && (auIds.length > 0 || plIds.length > 0)) { + stopAudioPreview(); + + if (auIds.length > 0) { + $.post(auUrl, {format: "json", ids: auIds}, deleteAudioClip); + } + if (plIds.length > 0) { + $.post(plUrl, {format: "json", ids: plIds}, deletePlaylist); + } + } +} + +function toggleAll() { + var checked = $(this).attr("checked"); + $('#library_display tr').each(function() { + var idSplit = $(this).attr('id').split("_"); + var type = idSplit[0]; + $(this).find(":checkbox").attr("checked", checked); + if (checked) { + if (type == "pl") { + checkedPLCount++; + } + $(this).addClass('selected'); + } else { + if (type == "pl") { + checkedPLCount--; + } + $(this).removeClass('selected'); + } + }); + + if (checked) { + checkedCount = $('#library_display tbody tr').size(); + enableGroupBtn('library_group_add', groupAdd); + enableGroupBtn('library_group_delete', confirmDeleteGroup); + } else { + checkedCount = 0; + checkedPLCount = 0; + disableGroupBtn('library_group_add'); + disableGroupBtn('library_group_delete'); + } +} + +function enableGroupBtn(btnId, func) { + btnId = '#' + btnId; + if ($(btnId).hasClass('ui-state-disabled')) { + $(btnId).removeClass('ui-state-disabled'); + $(btnId).click(func); + } +} + +function disableGroupBtn(btnId) { + btnId = '#' + btnId; + if (!$(btnId).hasClass('ui-state-disabled')) { + $(btnId).addClass('ui-state-disabled'); + $(btnId).unbind("click"); + } +} + +function checkBoxChanged() { + var cbAll = $('#library_display thead').find(":checkbox"); + var cbAllChecked = cbAll.attr("checked"); + var checked = $(this).attr("checked"); + var size = $('#library_display tbody tr').size(); + var idSplit = $(this).parent().parent().attr('id').split("_"); + var type = idSplit[0]; + if (checked) { + if (checkedCount < size) { + checkedCount++; + } + if (type == "pl" && checkedPLCount < size) { + checkedPLCount++; + } + enableGroupBtn('library_group_add', groupAdd); + enableGroupBtn('library_group_delete', confirmDeleteGroup); + $(this).parent().parent().addClass('selected'); + } else { + if (checkedCount > 0) { + checkedCount--; + } + if (type == "pl" && checkedPLCount > 0) { + checkedPLCount--; + } + if (checkedCount == 0) { + disableGroupBtn('library_group_add'); + disableGroupBtn('library_group_delete'); + } + $(this).parent().parent().removeClass('selected'); + } + + if (cbAllChecked && checkedCount < size) { + cbAll.attr("checked", false); + } else if (!cbAllChecked && checkedCount == size) { + cbAll.attr("checked", true); + } + + +} + +function setupGroupActions() { + checkedCount = 0; + $('#library_display tr:nth-child(1)').find(":checkbox").attr("checked", false); + $('#library_display thead').find(":checkbox").change(toggleAll); + $('#library_display tbody tr').each(function() { + $(this).find(":checkbox").change(checkBoxChanged); + }); + + disableGroupBtn('library_group_add'); + disableGroupBtn('library_group_delete'); +} + +function fnShowHide(iCol) { + /* Get the DataTables object again - this is not a recreation, just a get of the object */ + var oTable = dTable; + + var bVis = oTable.fnSettings().aoColumns[iCol].bVisible; + oTable.fnSetColumnVis( iCol, bVis ? false : true ); +} + function createDataTable(data) { - var dTable = $('#library_display').dataTable( { + dTable = $('#library_display').dataTable( { "bProcessing": true, "bServerSide": true, "sAjaxSource": "/Library/contents/format/json", - "fnServerData": function ( sSource, aoData, fnCallback ) { + "fnServerData": function ( sSource, aoData, testCallback ) { $.ajax( { "dataType": 'json', "type": "POST", "url": sSource, "data": aoData, - "success": fnCallback + "success": testCallback } ); }, "fnRowCallback": dtRowCallback, "fnDrawCallback": dtDrawCallback, "aoColumns": [ - /* Id */ { "sName": "id", "bSearchable": false, "bVisible": false }, - /* Title */ { "sTitle": "Title", "sName": "track_title" }, - /* Creator */ { "sTitle": "Creator", "sName": "artist_name" }, - /* Album */ { "sTitle": "Album", "sName": "album_title" }, - /* Genre */ { "sTitle": "Genre", "sName": "genre" }, - /* Length */ { "sTitle": "Length", "sName": "length" }, - /* Type */ { "sTitle": "Type", "sName": "ftype", "bSearchable": false } - ], + /* Checkbox */ {"sTitle": "", "bSortable": false, "bSearchable": false, "mDataProp": "checkbox", "sWidth": "25px", "sClass": "library_checkbox"}, + /* Id */ {"sName": "id", "bSearchable": false, "bVisible": false, "mDataProp": "id", "sClass": "library_id"}, + /* Title */ {"sTitle": "Title", "sName": "track_title", "mDataProp": "track_title", "sClass": "library_title"}, + /* Creator */ {"sTitle": "Creator", "sName": "artist_name", "mDataProp": "artist_name", "sClass": "library_creator"}, + /* Album */ {"sTitle": "Album", "sName": "album_title", "mDataProp": "album_title", "sClass": "library_album"}, + /* Genre */ {"sTitle": "Genre", "sName": "genre", "mDataProp": "genre", "sWidth": "10%", "sClass": "library_genre"}, + /* Year */ {"sTitle": "Year", "sName": "year", "mDataProp": "year", "sWidth": "8%", "sClass": "library_year"}, + /* Length */ {"sTitle": "Length", "sName": "length", "mDataProp": "length", "sWidth": "16%", "sClass": "library_length"}, + /* Type */ {"sTitle": "Type", "sName": "ftype", "bSearchable": false, "mDataProp": "ftype", "sWidth": "9%", "sClass": "library_type"}, + /* Upload Time */ {"sTitle": "Upload Time", "sName": "upload_time", "mDataProp": "upload_time", "sClass": "library_upload_time"}, + ], "aaSorting": [[2,'asc']], "sPaginationType": "full_numbers", "bJQueryUI": true, @@ -330,9 +536,31 @@ function createDataTable(data) { "sSearch": "" }, "iDisplayLength": getNumEntriesPreference(data), - "bStateSave": true + "bStateSave": true, + // R = ColReorder, C = ColVis, see datatables doc for others + "sDom": 'Rlfr<"H"C<"library_toolbar">>t<"F"ip>', + "oColVis": { + "buttonText": "Show/Hide Columns", + "sAlign": "right", + "aiExclude": [0, 1, 2], + "sSize": "css", + "bShowAll": true + }, + "oColReorder": { + "aiOrder": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] /* code this */, + "iFixedColumns": 3 + } }); dTable.fnSetFilteringDelay(350); + + $("div.library_toolbar").html('Reset Order' + + 'Delete' + + 'Add'); + + $('#library_order_reset').click(function() { + ColReorder.fnReset( dTable ); + return false; + }); } $(document).ready(function() { @@ -341,7 +569,7 @@ $(document).ready(function() { $.ajax({ url: "/Api/library-init/format/json", dataType:"json", success:createDataTable, error:function(jqXHR, textStatus, errorThrown){}}); - checkImportStatus() + checkImportStatus(); setInterval( "checkImportStatus()", 5000 ); setInterval( "checkSCUploadStatus()", 5000 ); diff --git a/airtime_mvc/public/js/datatables/plugin/dataTables.ColReorder.js b/airtime_mvc/public/js/datatables/plugin/dataTables.ColReorder.js new file mode 100644 index 000000000..95c39fcec --- /dev/null +++ b/airtime_mvc/public/js/datatables/plugin/dataTables.ColReorder.js @@ -0,0 +1,987 @@ +/* + * File: ColReorder.js + * Version: 1.0.4 + * CVS: $Id$ + * Description: Controls for column visiblity in DataTables + * Author: Allan Jardine (www.sprymedia.co.uk) + * Created: Wed Sep 15 18:23:29 BST 2010 + * Modified: $Date$ by $Author$ + * Language: Javascript + * License: GPL v2 or BSD 3 point style + * Project: DataTables + * Contact: www.sprymedia.co.uk/contact + * + * Copyright 2010-2011 Allan Jardine, all rights reserved. + * + * 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 + * + */ + + +(function($, window, document) { + + +/** + * Switch the key value pairing of an index array to be value key (i.e. the old value is now the + * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ]. + * @method fnInvertKeyValues + * @param array aIn Array to switch around + * @returns array + */ +function fnInvertKeyValues( aIn ) +{ + var aRet=[]; + for ( var i=0, iLen=aIn.length ; i= iCols ) + { + this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom ); + return; + } + + if ( iTo < 0 || iTo >= iCols ) + { + this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo ); + return; + } + + /* + * Calculate the new column array index, so we have a mapping between the old and new + */ + var aiMapping = []; + for ( i=0, iLen=iCols ; i this.s.fixed-1 ) + { + this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh ); + } + + /* Mark the original column order for later reference */ + this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i; + } + + /* State saving */ + this.s.dt.aoStateSave.push( { + "fn": function (oS, sVal) { + return that._fnStateSave.call( that, sVal ); + }, + "sName": "ColReorder_State" + } ); + + /* An initial column order has been specified */ + var aiOrder = null; + if ( typeof this.s.init.aiOrder != 'undefined' ) + { + aiOrder = this.s.init.aiOrder.slice(); + } + + /* State loading, overrides the column order given */ + if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' && + this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length ) + { + aiOrder = this.s.dt.oLoadedState.ColReorder; + } + + /* If we have an order to apply - do so */ + if ( aiOrder ) + { + /* We might be called during or after the DataTables initialisation. If before, then we need + * to wait until the draw is done, if after, then do what we need to do right away + */ + if ( !that.s.dt._bInitComplete ) + { + var bDone = false; + this.s.dt.aoDrawCallback.push( { + "fn": function () { + if ( !that.s.dt._bInitComplete && !bDone ) + { + bDone = true; + var resort = fnInvertKeyValues( aiOrder ); + that._fnOrderColumns.call( that, resort ); + } + }, + "sName": "ColReorder_Pre" + } ); + } + else + { + var resort = fnInvertKeyValues( aiOrder ); + that._fnOrderColumns.call( that, resort ); + } + } + }, + + + /** + * Set the column order from an array + * @method _fnOrderColumns + * @param array a An array of integers which dictate the column order that should be applied + * @returns void + * @private + */ + "_fnOrderColumns": function ( a ) + { + if ( a.length != this.s.dt.aoColumns.length ) + { + this.s.dt.oInstance.oApi._fnLog( oDTSettings, 1, "ColReorder - array reorder does not "+ + "match known number of columns. Skipping." ); + return; + } + + for ( var i=0, iLen=a.length ; i 0 ) + { + this.dom.drag.removeChild( this.dom.drag.getElementsByTagName('caption')[0] ); + } + while ( this.dom.drag.getElementsByTagName('tbody').length > 0 ) + { + this.dom.drag.removeChild( this.dom.drag.getElementsByTagName('tbody')[0] ); + } + while ( this.dom.drag.getElementsByTagName('tfoot').length > 0 ) + { + this.dom.drag.removeChild( this.dom.drag.getElementsByTagName('tfoot')[0] ); + } + + $('thead tr:eq(0)', this.dom.drag).each( function () { + $('th:not(:eq('+that.s.mouse.targetIndex+'))', this).remove(); + } ); + $('tr', this.dom.drag).height( $('tr:eq(0)', that.s.dt.nTHead).height() ); + + $('thead tr:gt(0)', this.dom.drag).remove(); + + $('thead th:eq(0)', this.dom.drag).each( function (i) { + this.style.width = $('th:eq('+that.s.mouse.targetIndex+')', that.s.dt.nTHead).width()+"px"; + } ); + + this.dom.drag.style.position = "absolute"; + this.dom.drag.style.top = "0px"; + this.dom.drag.style.left = "0px"; + this.dom.drag.style.width = $('th:eq('+that.s.mouse.targetIndex+')', that.s.dt.nTHead).outerWidth()+"px"; + + + this.dom.pointer = document.createElement( 'div' ); + this.dom.pointer.className = "DTCR_pointer"; + this.dom.pointer.style.position = "absolute"; + + if ( this.s.dt.oScroll.sX === "" && this.s.dt.oScroll.sY === "" ) + { + this.dom.pointer.style.top = $(this.s.dt.nTable).offset().top+"px"; + this.dom.pointer.style.height = $(this.s.dt.nTable).height()+"px"; + } + else + { + this.dom.pointer.style.top = $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top+"px"; + this.dom.pointer.style.height = $('div.dataTables_scroll', this.s.dt.nTableWrapper).height()+"px"; + } + + document.body.appendChild( this.dom.pointer ); + document.body.appendChild( this.dom.drag ); + } +}; + + + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Static parameters + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * Array of all ColReorder instances for later reference + * @property ColReorder.aoInstances + * @type array + * @default [] + * @static + */ +ColReorder.aoInstances = []; + + + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Static functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * Reset the column ordering for a DataTables instance + * @method ColReorder.fnReset + * @param object oTable DataTables instance to consider + * @returns void + * @static + */ +ColReorder.fnReset = function ( oTable ) +{ + for ( var i=0, iLen=ColReorder.aoInstances.length ; i