sintonia/legacy/public/js/airtime/showbuilder/builder.js

1537 lines
44 KiB
JavaScript

var AIRTIME = (function (AIRTIME) {
var mod,
oSchedTable,
SB_SELECTED_CLASS = "sb-selected",
CURSOR_SELECTED_CLASS = "cursor-selected-row",
NOW_PLAYING_CLASS = "sb-now-playing",
$sbContent,
$sbTable,
$toolbar,
$lib,
cursors = [],
cursorIds = [],
showInstanceIds = [],
headerFooter = [],
DISABLED_CLASS = "ui-state-disabled",
selectedRows,
$previouslySelected,
flagForDeselection;
if (AIRTIME.showbuilder === undefined) {
AIRTIME.showbuilder = {};
}
mod = AIRTIME.showbuilder;
function checkError(json) {
if (json.error !== undefined) {
alert(json.error);
}
}
mod.timeout = undefined;
mod.timestamp = -1;
mod.showInstances = [];
mod.resetTimestamp = function () {
mod.timestamp = -1;
};
mod.setTimestamp = function (timestamp) {
mod.timestamp = timestamp;
};
mod.updateCalendarStatusIcon = function (json) {
//make sure we are only executing this code on the calendar view, not
//the Now Playing view.
if (window.location.pathname.toLowerCase().indexOf("schedule") < 0) {
return;
}
var instance_id = json.schedule[0].instance;
var lastElem = json.schedule[json.schedule.length - 1];
var $elem = $("#fc-show-instance-" + instance_id);
//if the show is linked, then replace $elem to reference all linked
//instances
if ($elem.data("show-linked") == "1") {
var show_id = $elem.data("show-id");
$elem = $('*[data-show-id="' + show_id + '"]');
}
$elem.find(".show-empty, .show-partial-filled").remove();
if (json.schedule[1].empty) {
$elem
.find(".fc-event-inner")
.append(
'<span id="' +
instance_id +
'" title="' +
$.i18n._("Show is empty") +
'" class="small-icon show-empty"></span>',
);
} else if (lastElem["fRuntime"][0] == "-") {
$elem
.find(".fc-event-inner")
.append(
'<span id="' +
instance_id +
'" title="' +
$.i18n._("Show is partially filled") +
'" class="small-icon show-partial-filled"></span>',
);
}
};
mod.getTimestamp = function () {
if (mod.timestamp !== undefined) {
return mod.timestamp;
} else {
return -1;
}
};
mod.setShowInstances = function (showInstances) {
mod.showInstances = showInstances;
};
mod.getShowInstances = function () {
return mod.showInstances;
};
mod.refresh = function (schedId) {
mod.resetTimestamp();
// once a track plays out we need to check if we can update
// the is_scheduled flag in cc_files
if (schedId > 0) {
$.post(
baseUrl + "schedule/update-future-is-scheduled",
{ format: "json", schedId: schedId },
function (data) {
if (data.redrawLibTable !== undefined && data.redrawLibTable) {
$("#library_content")
.find("#library_display")
.dataTable()
.fnStandingRedraw();
}
},
);
oSchedTable.fnDraw();
}
};
mod.checkSelectButton = function () {
var $selectable = $sbTable.find("tr");
if ($selectable.length !== 0) {
AIRTIME.button.enableButton("btn-group #timeline-select", false);
} else {
AIRTIME.button.disableButton("btn-group #timeline-select", false);
}
//need to check if the 'Select' button is disabled
var select = $(".btn-group #timeline-select");
if (select.is(":disabled")) {
select.removeAttr("disabled");
}
};
mod.checkTrimButton = function () {
var $over = $sbTable.find(".sb-over.sb-allowed");
if ($over.length !== 0) {
AIRTIME.button.enableButton("icon-cut", true);
} else {
AIRTIME.button.disableButton("icon-cut", true);
}
};
mod.checkDeleteButton = function () {
var $selected = $("." + SB_SELECTED_CLASS);
var button = $("#show_builder").find(".icon-trash").parent();
if ($selected.length !== 0) {
button.removeClass(DISABLED_CLASS);
button.removeAttr("disabled");
} else {
button.addClass(DISABLED_CLASS);
button.attr("disabled", "disabled");
}
};
mod.checkJumpToCurrentButton = function () {
var $current = $sbTable.find("." + NOW_PLAYING_CLASS);
if ($current.length !== 0) {
AIRTIME.button.enableButton("icon-step-forward", true);
} else {
AIRTIME.button.disableButton("icon-step-forward", true);
}
};
mod.checkCancelButton = function () {
var $current = $sbTable.find(".sb-current-show"),
//this user type should be refactored into a separate users module later
//when there's more time and more JS will need to know user data.
userType = localStorage.getItem("user-type"),
canCancel = false;
if ($current.length !== 0 && $current.hasClass("sb-allowed")) {
canCancel = true;
} else if (
$current.length !== 0 &&
(userType === "A" || userType === "P")
) {
canCancel = true;
}
if (canCancel === true) {
AIRTIME.button.enableButton("icon-ban-circle", true);
} else {
AIRTIME.button.disableButton("icon-ban-circle", true);
}
};
mod.checkToolBarIcons = function () {
//library may not be on the page.
if (AIRTIME.library !== undefined) {
AIRTIME.library.checkAddButton();
}
mod.checkSelectButton();
mod.checkTrimButton();
mod.checkDeleteButton();
mod.checkJumpToCurrentButton();
mod.checkCancelButton();
};
mod.selectCursor = function ($el) {
$el.addClass(CURSOR_SELECTED_CLASS);
mod.checkToolBarIcons();
};
mod.removeCursor = function ($el) {
$el.removeClass(CURSOR_SELECTED_CLASS);
mod.checkToolBarIcons();
};
/*
* sNot is an optional string to filter selected elements by. (ex removing the currently playing item)
*/
mod.getSelectedData = function (sNot) {
var $selected = $sbTable.find("tr." + SB_SELECTED_CLASS),
aData = [],
i,
length,
$item;
if (sNot !== undefined) {
$selected = $selected.not("." + sNot);
}
for (i = 0, length = $selected.length; i < length; i++) {
$item = $($selected.get(i));
aData.push($item.data("aData"));
}
return aData.reverse();
};
mod.selectAll = function () {
var $trs = $sbTable.find("tr.lib-audio").not(".sb-past, .sb-empty");
$trs
.addClass(SB_SELECTED_CLASS)
.find(".sb-checkbox > input")
.prop("checked", true);
mod.checkToolBarIcons();
};
mod.selectNone = function () {
var $trs = $sbTable.find("tr.lib-audio");
$trs
.removeClass(SB_SELECTED_CLASS)
.find(".sb-checkbox > input")
.prop("checked", false);
$previouslySelected = undefined;
selectedRows = undefined;
mod.checkToolBarIcons();
};
mod.disableUI = function () {
$lib.block({
message: "",
theme: true,
applyPlatformOpacityRules: false,
});
$sbContent.block({
message: "",
theme: true,
applyPlatformOpacityRules: false,
});
};
mod.enableUI = function () {
$lib.unblock();
$sbContent.unblock();
};
mod.fnItemCallback = function (json) {
checkError(json);
mod.getSelectedCursors();
selectedRows = $("." + SB_SELECTED_CLASS);
oSchedTable.fnDraw();
mod.enableUI();
//Unneccessary reload of the library pane after moving tracks in the showbuilder pane.
//$("#library_content").find("#library_display").dataTable().fnStandingRedraw();
getUsabilityHint();
};
mod.getSelectedCursors = function () {
cursorIds = [];
/* We need to keep record of which show the cursor belongs to
* in the case where more than one show is displayed in the show builder
* because header and footer rows have the same id
*/
showInstanceIds = [];
/* Keeps track if the row is a footer. We need to do this because
* header and footer rows have the save cursorIds and showInstanceId
* so both will be selected in the draw callback
*/
headerFooter = [];
cursors = $(".cursor-selected-row");
for (i = 0; i < cursors.length; i++) {
cursorIds.push($(cursors.get(i)).attr("id"));
showInstanceIds.push($(cursors.get(i)).attr("si_id"));
if ($(cursors.get(i)).hasClass("sb-footer")) {
headerFooter.push("f");
} else {
headerFooter.push("n");
}
}
};
mod.fnAdd = function (aMediaIds, aSchedIds) {
AIRTIME.library.selectNone();
mod.disableUI();
$.post(
baseUrl + "showbuilder/schedule-add",
{ format: "json", mediaIds: aMediaIds, schedIds: aSchedIds },
mod.fnItemCallback,
);
};
mod.fnMove = function (aSelect, aAfter) {
mod.disableUI();
$.post(
baseUrl + "showbuilder/schedule-move",
{ format: "json", selectedItem: aSelect, afterItem: aAfter },
mod.fnItemCallback,
);
};
mod.fnRemove = function (aItems) {
mod.disableUI();
if (confirm($.i18n._("Delete selected item(s)?"))) {
mod.selectNone();
$.post(
baseUrl + "showbuilder/schedule-remove",
{ items: aItems, format: "json" },
mod.fnItemCallback,
);
} else {
mod.enableUI();
}
};
mod.fnRemoveSelectedItems = function () {
var aData = mod.getSelectedData(),
i,
length,
temp,
aItems = [];
for (i = 0, length = aData.length; i < length; i++) {
temp = aData[i];
aItems.push({
id: temp.id,
instance: temp.instance,
timestamp: temp.timestamp,
});
}
mod.fnRemove(aItems);
};
mod.fnServerData = function fnBuilderServerData(sSource, aoData, fnCallback) {
aoData.push({ name: "timestamp", value: mod.getTimestamp() });
aoData.push({ name: "instances", value: mod.getShowInstances() });
aoData.push({ name: "format", value: "json" });
if (mod.fnServerData.hasOwnProperty("start")) {
aoData.push({ name: "start", value: mod.fnServerData.start });
}
if (mod.fnServerData.hasOwnProperty("end")) {
aoData.push({ name: "end", value: mod.fnServerData.end });
}
if (mod.fnServerData.hasOwnProperty("ops")) {
aoData.push({ name: "myShows", value: mod.fnServerData.ops.myShows });
aoData.push({
name: "showFilter",
value: mod.fnServerData.ops.showFilter,
});
aoData.push({
name: "showInstanceFilter",
value: mod.fnServerData.ops.showInstanceFilter,
});
}
$.ajax({
dataType: "json",
type: "POST",
url: sSource,
data: aoData,
success: function (json) {
mod.updateCalendarStatusIcon(json);
mod.setTimestamp(json.timestamp);
mod.setShowInstances(json.instances);
mod.getSelectedCursors();
fnCallback(json);
},
});
};
mod.jumpToCurrentTrack = function () {
var $scroll = $sbContent.find(".dataTables_scrolling");
var scrolled = $scroll.scrollTop();
var scrollingTop = $scroll.offset().top;
var current = $sbTable.find("." + NOW_PLAYING_CLASS);
var currentTop = current.offset().top;
$scroll.scrollTop(currentTop - scrollingTop + scrolled);
};
mod.builderDataTable = function () {
$sbContent = $("#show_builder");
$lib = $("#library_content");
$sbTable = $sbContent.find("table");
var isInitialized = false;
var lockedPreviewIcon = document.createElement("span");
lockedPreviewIcon.setAttribute("class", "ui-icon ui-icon-locked");
var previewIcon = document.createElement("img");
previewIcon.setAttribute("src", baseUrl + "css/images/icon_audioclip.png");
previewIcon.setAttribute("title", $.i18n._("Track preview"));
oSchedTable = $sbTable.dataTable({
aoColumns: [
/* checkbox */ {
mDataProp: "allowed",
sTitle: "",
sWidth: "16px",
sClass: "sb-checkbox",
},
/* Type */ {
mDataProp: "image",
sTitle: "",
sClass: "library_image sb-image",
sWidth: "16px",
},
/* starts */ {
mDataProp: "starts",
sTitle: $.i18n._("Start"),
sClass: "sb-starts",
sWidth: "60px",
},
/* ends */ {
mDataProp: "ends",
sTitle: $.i18n._("End"),
sClass: "sb-ends",
sWidth: "60px",
},
/* runtime */ {
mDataProp: "runtime",
sTitle: $.i18n._("Duration"),
sClass: "library_length sb-length",
sWidth: "65px",
},
/* title */ {
mDataProp: "title",
sTitle: $.i18n._("Title"),
sClass: "sb-title",
},
/* creator */ {
mDataProp: "creator",
sTitle: $.i18n._("Creator"),
sClass: "sb-creator",
},
/* album */ {
mDataProp: "album",
sTitle: $.i18n._("Album"),
sClass: "sb-album",
},
/* cue in */ {
mDataProp: "cuein",
sTitle: $.i18n._("Cue In"),
bVisible: false,
sClass: "sb-cue-in",
},
/* cue out */ {
mDataProp: "cueout",
sTitle: $.i18n._("Cue Out"),
bVisible: false,
sClass: "sb-cue-out",
},
/* fade in */ {
mDataProp: "fadein",
sTitle: $.i18n._("Fade In"),
bVisible: false,
sClass: "sb-fade-in",
},
/* fade out */ {
mDataProp: "fadeout",
sTitle: $.i18n._("Fade Out"),
bVisible: false,
sClass: "sb-fade-out",
},
/* Mime */ {
mDataProp: "mime",
sTitle: $.i18n._("Mime"),
bVisible: false,
sClass: "sb-mime",
},
],
bJQueryUI: true,
bSort: false,
bFilter: false,
bProcessing: true,
bServerSide: true,
bInfo: false,
bAutoWidth: false,
bDeferRender: true,
bStateSave: true,
fnStateSaveParams: function (oSettings, oData) {
//remove oData components we don't want to save.
delete oData.oSearch;
delete oData.aoSearchCols;
},
fnStateSave: function fnStateSave(oSettings, oData) {
localStorage.setItem("datatables-timeline", JSON.stringify(oData));
},
fnStateLoad: function fnBuilderStateLoad(oSettings) {
var settings = localStorage.getItem("datatables-timeline");
if (settings !== "") {
return JSON.parse(settings);
}
},
fnStateLoadParams: function (oSettings, oData) {
var i,
length,
a = oData.abVisCols;
//putting serialized data back into the correct js type to make
//sure everything works properly.
for (i = 0, length = a.length; i < length; i++) {
if (typeof a[i] === "string") {
a[i] = a[i] === "true";
}
}
oData.iCreate = parseInt(oData.iCreate, 10);
},
fnServerData: mod.fnServerData,
fnRowCallback: function fnRowCallback(
nRow,
aData,
iDisplayIndex,
iDisplayIndexFull,
) {
var i,
length,
sSeparatorHTML,
fnPrepareSeparatorRow,
$node,
cl = "",
//background-color to imitate calendar color.
r,
g,
b,
a,
$nRow = $(nRow),
$image,
$div,
headerIcon;
fnPrepareSeparatorRow = function fnPrepareSeparatorRow(
sRowContent,
sClass,
iNodeIndex,
) {
//Albert:
//$(nRow.children[iNodeIndex]).replaceWith(emptyNode);
$node = $(nRow.children[iNodeIndex]);
$node.html(sRowContent);
$node.attr("colspan", 100);
for (
i = iNodeIndex + 1, length = nRow.children.length;
i < length;
i = i + 1
) {
$node = $(nRow.children[i]);
$node.html("");
$node.attr("style", "display : none");
}
$nRow.addClass(sClass);
};
if (aData.header === true) {
//remove the column classes from all tds.
$nRow.find("td").removeClass();
$node = $(nRow.children[0]);
$node.html("");
cl = "sb-header";
if (aData.record === true) {
$div = $("<div/>", {
class: "small-icon " + headerIcon,
});
$node.append($div);
} else if (aData.rebroadcast === true) {
$div = $("<div/>", {
class: "small-icon rebroadcast",
});
$node.append($div);
}
sSeparatorHTML =
'<span class="show-title">' + aData.title + "</span>";
if (aData.rebroadcast === true) {
sSeparatorHTML += "<span>" + aData.rebroadcast_title + "</span>";
}
sSeparatorHTML += '<span class="push-right">';
if (aData.startDate === aData.endDate) {
sSeparatorHTML +=
'<span class="show-date">' +
aData.startDate +
'</span><span class="show-time">' +
aData.startTime +
"</span>";
sSeparatorHTML +=
'-<span class="show-time">' + aData.endTime + "</span>";
} else {
sSeparatorHTML +=
'<span class="show-date">' +
aData.startDate +
'</span><span class="show-time">' +
aData.startTime +
"</span>";
sSeparatorHTML +=
'-<span class="show-date">' +
aData.endDate +
'</span><span class="show-time">' +
aData.endTime +
"</span>";
}
sSeparatorHTML += "</span>";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
} else if (aData.footer === true) {
//remove the column classes from all tds.
$nRow.find("td").removeClass();
$node = $(nRow.children[0]);
cl = "sb-footer";
//check the show's content status.
if (aData.runtime >= 0) {
$node.html('<span class="ui-icon ui-icon-check"></span>');
cl = cl + " ui-state-highlight";
} else {
$node.html('<span class="ui-icon ui-icon-notice"></span>');
cl = cl + " ui-state-error";
}
sSeparatorHTML = "<span>" + aData.fRuntime + "</span>";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
} else if (aData.empty === true) {
//remove the column classes from all tds.
$nRow.find("td").removeClass();
$node = $(nRow.children[0]);
if ($node) {
$node.empty();
}
sSeparatorHTML =
"<span>" +
$.i18n._("Drag tracks here from the library") +
"</span>";
cl = cl + " sb-empty odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
} else if (aData.record === true) {
//remove the column classes from all tds.
$nRow.find("td").removeClass();
$node = $(nRow.children[0]);
$node.html("");
sSeparatorHTML =
"<span>" + $.i18n._("Recording From Line In") + "</span>";
cl = cl + " sb-record odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
} else {
//add the play function if the file exists on disk.
$image = $nRow.find("td.sb-image");
$image.empty();
//check if the file exists.
if (aData.image === true) {
$nRow.addClass("lib-audio");
if (!isAudioSupported(aData.mime)) {
//$image.html('<span class="ui-icon ui-icon-locked"></span>');
$image.append(lockedPreviewIcon);
} else {
$image.append(previewIcon.cloneNode(false));
$image.click(function () {
open_show_preview(aData.instance, aData.pos);
return false;
});
}
} else {
$image.html('<span class="ui-icon ui-icon-alert"></span>');
$image.find(".ui-icon-alert").qtip({
content: {
text: $.i18n._(
'Airtime is unsure about the status of this file. This can happen when the file is on a remote drive that is unaccessible or the file is in a directory that isn\'t "watched" anymore.',
),
},
style: {
classes: "ui-tooltip-dark",
},
show: "mouseover",
hide: "mouseout",
});
}
$node = $(nRow.children[0]);
if (
aData.allowed === true &&
aData.scheduled >= 1 &&
aData.linked_allowed
) {
$node.html(
'<input type="checkbox" name="' + aData.id + '"></input>',
);
} else {
$node.empty();
}
}
//add the show colour to the leftmost td
if (aData.footer !== true) {
if ($nRow.hasClass("sb-header")) {
a = 1;
} else if ($nRow.hasClass("odd")) {
a = 0.3;
} else if ($nRow.hasClass("even")) {
a = 0.4;
}
//convert from hex to rgb.
r = parseInt(aData.backgroundColor.substring(0, 2), 16);
g = parseInt(aData.backgroundColor.substring(2, 4), 16);
b = parseInt(aData.backgroundColor.substring(4, 6), 16);
$nRow
.find("td:first")
.css(
"background",
"rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
);
}
//save some info for reordering purposes.
$nRow.data({ aData: aData });
if (aData.scheduled === 1) {
$nRow.addClass(NOW_PLAYING_CLASS);
} else if (aData.scheduled === 0 || aData.scheduled === undefined) {
$nRow.addClass("sb-past");
} else {
$nRow.addClass("sb-future");
}
if (aData.allowed !== true || aData.linked_allowed === false) {
$nRow.addClass("sb-not-allowed");
} else {
$nRow.addClass("sb-allowed");
$nRow.attr("id", aData.id);
$nRow.attr("si_id", aData.instance);
}
//status used to colour tracks.
if (aData.status === 2) {
$nRow.addClass("sb-boundry");
} else if (aData.status === 0) {
$nRow.addClass("sb-over");
}
if (aData.currentShow === true) {
$nRow.addClass("sb-current-show");
}
},
//remove any selected nodes before the draw.
fnPreDrawCallback: function (oSettings) {
//make sure any dragging helpers are removed or else they'll be stranded on the screen.
$("#draggingContainer").remove();
},
fnDrawCallback: function fnBuilderDrawCallback(oSettings, json) {
if (!isInitialized) {
//when coming to 'Now Playing' page we want the page
//to jump to the current track
if ($(this).find("." + NOW_PLAYING_CLASS).length > 0) {
mod.jumpToCurrentTrack();
}
}
isInitialized = true;
var aData, elements, i, length, temp;
clearTimeout(mod.timeout);
//order of importance of elements for setting the next timeout.
elements = [
$sbTable.find("tr." + NOW_PLAYING_CLASS),
$sbTable
.find("tbody")
.find("tr.sb-future.sb-footer, tr.sb-future.sb-header")
.filter(":first"),
];
//check which element we should set a timeout relative to.
for (i = 0, length = elements.length; i < length; i++) {
temp = elements[i];
if (temp.length > 0) {
aData = temp.data("aData");
// max time interval
// setTimeout allows only up to (2^31)-1 millisecs timeout value
maxRefreshInterval = Math.pow(2, 31) - 1;
refreshInterval = aData.refresh * 1000;
if (refreshInterval > maxRefreshInterval) {
refreshInterval = maxRefreshInterval;
}
mod.timeout = setTimeout(function () {
mod.refresh(aData.id);
}, refreshInterval); //need refresh in milliseconds
break;
}
}
mod.checkToolBarIcons();
var sid;
if (selectedRows !== undefined) {
selectedRows.each(function (i, el) {
sid = $(el).attr("id");
var tr = $("#" + sid);
tr.addClass(SB_SELECTED_CLASS);
tr.find("input[type='checkbox']").prop("checked", true);
});
mod.checkToolBarIcons();
}
},
// R = ColReorder, C = ColVis
sDom: 'R<"dt-process-rel"r><"sb-padded"<"H"C>><"dataTables_scrolling sb-padded"t><"F">',
oColVis: {
aiExclude: [0, 1],
buttonText: $.i18n._("Columns"),
},
oColReorder: {
iFixedColumns: 2,
},
sAjaxDataProp: "schedule",
oLanguage: getDatatablesStrings({
sZeroRecords: $.i18n._(
"There are no shows scheduled during the specified time period.",
),
sEmptyTable: $.i18n._(
"There are no shows scheduled during the specified time period.",
),
}),
sAjaxSource: baseUrl + "showbuilder/builder-feed",
bScrollCollapseY: false,
});
$sbTable
.find("tbody")
.on(
"mousedown",
"tr:not(.sb-not-allowed, .sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty)) > td.sb-checkbox",
function (ev) {
var $tr = $(this).parent(),
// Get the ID of the selected row
$rowId = $tr.attr("id");
if (!$tr.hasClass(SB_SELECTED_CLASS)) {
if (ev.shiftKey && $previouslySelected !== undefined) {
if ($previouslySelected.attr("id") == $rowId) {
return;
}
// If the selected row comes before the previously selected row,
// we want to select previous rows, otherwise we select next
if ($previouslySelected.prevAll("#" + $rowId).length !== 0) {
$previouslySelected.prevUntil($tr).each(function (i, el) {
$(el).addClass(SB_SELECTED_CLASS);
$(el).find(".sb-checkbox > input").prop("checked", true);
});
} else {
$previouslySelected.nextUntil($tr).each(function (i, el) {
$(el).addClass(SB_SELECTED_CLASS);
$(el).find(".sb-checkbox > input").prop("checked", true);
});
}
}
$tr.addClass(SB_SELECTED_CLASS);
$tr.find(".sb-checkbox > input").prop("checked", true);
} else {
flagForDeselection = true;
}
selectedRows = $("." + SB_SELECTED_CLASS);
// Remember this row so we can properly multiselect
$previouslySelected = $tr;
},
);
$sbTable
.find("tbody")
.on(
"mousedown",
"tr:not(.sb-not-allowed, .sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty)) > td:not(.sb-checkbox)",
function (ev) {
var $tr = $(this).parent(),
// Get the ID of the selected row
$rowId = $tr.attr("id");
if (ev.which === 3 /* Right click */) {
mod.selectNone();
$tr.addClass(SB_SELECTED_CLASS);
$tr.find(".sb-checkbox > input").prop("checked", true);
mod.checkToolBarIcons();
return;
}
if (!$tr.hasClass(SB_SELECTED_CLASS)) {
if (ev.shiftKey && $previouslySelected !== undefined) {
if ($previouslySelected.attr("id") == $rowId) {
return;
}
// If the selected row comes before the previously selected row,
// we want to select previous rows, otherwise we select next
if ($previouslySelected.prevAll("#" + $rowId).length !== 0) {
$previouslySelected.prevUntil($tr).each(function (i, el) {
$(el).addClass(SB_SELECTED_CLASS);
$(el).find(".sb-checkbox > input").prop("checked", true);
});
} else {
$previouslySelected.nextUntil($tr).each(function (i, el) {
$(el).addClass(SB_SELECTED_CLASS);
$(el).find(".sb-checkbox > input").prop("checked", true);
});
}
} else if (!ev.ctrlKey) {
mod.selectNone();
}
$tr.addClass(SB_SELECTED_CLASS);
$tr.find(".sb-checkbox > input").prop("checked", true);
mod.checkToolBarIcons();
} else if (ev.ctrlKey) {
flagForDeselection = true;
}
selectedRows = $("." + SB_SELECTED_CLASS);
// Remember this row so we can properly multiselect
$previouslySelected = $tr;
},
);
$sbTable
.find("tbody")
.on(
"click",
"tr:not(.sb-not-allowed, .sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty)) > td.sb-checkbox",
function () {
var tr = $(this).parent();
if (flagForDeselection) {
flagForDeselection = false;
$previouslySelected = undefined;
tr.removeClass(SB_SELECTED_CLASS);
tr.find(".sb-checkbox > input").prop("checked", false);
} else {
tr.addClass(SB_SELECTED_CLASS);
tr.find(".sb-checkbox > input").prop("checked", true);
}
mod.checkToolBarIcons();
selectedRows = $("." + SB_SELECTED_CLASS);
},
);
$sbTable
.find("tbody")
.on(
"click",
"tr:not(.sb-not-allowed, .sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty)) > td:not(.sb-checkbox)",
function (e) {
var tr = $(this).parent();
if (flagForDeselection) {
flagForDeselection = false;
$previouslySelected = undefined;
tr.removeClass(SB_SELECTED_CLASS);
tr.find(".sb-checkbox > input").prop("checked", false);
} else if (!(e.shiftKey || e.ctrlKey)) {
mod.selectNone();
tr.addClass(SB_SELECTED_CLASS);
tr.find(".sb-checkbox > input").prop("checked", true);
$previouslySelected = tr;
}
mod.checkToolBarIcons();
selectedRows = $("." + SB_SELECTED_CLASS);
},
);
//begin context menu initialization.
$.contextMenu({
selector: "#show_builder tr.lib-audio:not(.sb-not-allowed, .sb-past)",
trigger: "right",
build: function ($el, e) {
var items,
$tr = $el,
data = $tr.data("aData"),
cursorClass = "cursor-selected-row",
callback;
function processMenuItems(oItems) {
//define a preview callback.
if (oItems.preview !== undefined) {
callback = function () {
open_show_preview(data.instance, data.pos);
};
oItems.preview.callback = callback;
}
//define a select cursor callback.
if (oItems.selCurs !== undefined) {
callback = function () {
var $tr = $(this).parents("tr").next();
mod.selectCursor($tr);
};
oItems.selCurs.callback = callback;
}
//define a remove cursor callback.
if (oItems.delCurs !== undefined) {
callback = function () {
var $tr = $(this).parents("tr").next();
mod.removeCursor($tr);
};
oItems.delCurs.callback = callback;
}
//define a delete callback.
if (oItems.del !== undefined) {
callback = function () {
AIRTIME.showbuilder.fnRemove([
{
id: data.id,
timestamp: data.timestamp,
instance: data.instance,
},
]);
};
oItems.del.callback = callback;
}
//only show the cursor selecting options if the library is visible on the page.
if ($tr.next().find(".marker").length === 0) {
delete oItems.selCurs;
delete oItems.delCurs;
}
//check to include either select or remove cursor.
else {
if ($tr.next().hasClass(cursorClass)) {
delete oItems.selCurs;
} else {
delete oItems.delCurs;
}
}
items = oItems;
}
request = $.ajax({
url: baseUrl + "showbuilder/context-menu",
type: "GET",
data: { id: data.id, format: "json" },
dataType: "json",
async: false,
success: function (json) {
processMenuItems(json.items);
},
});
return {
items: items,
};
},
});
var sortableConf = (function () {
var origTrs,
aItemData = [],
oPrevData,
fnAdd,
fnMove,
fnReceive,
fnUpdate,
i,
html,
helperData,
draggingContainer;
fnAdd = function () {
var aMediaIds = [],
aSchedIds = [];
for (i = 0; i < aItemData.length; i++) {
aMediaIds.push({ id: aItemData[i].id, type: aItemData[i].ftype });
}
aSchedIds.push({
id: oPrevData.id,
instance: oPrevData.instance,
timestamp: oPrevData.timestamp,
});
mod.fnAdd(aMediaIds, aSchedIds);
};
fnMove = function () {
var aSelect = [],
aAfter = [];
for (i = 0; i < helperData.length; i++) {
aSelect.push({
id: helperData[i].id,
instance: helperData[i].instance,
timestamp: helperData[i].timestamp,
});
}
aAfter.push({
id: oPrevData.id,
instance: oPrevData.instance,
timestamp: oPrevData.timestamp,
});
mod.fnMove(aSelect, aAfter);
};
fnReceive = function (event, ui) {
var aItems = [];
if (
AIRTIME.library.getCurrentTable() == AIRTIME.library.libraryDataTable
) {
AIRTIME.library.addToChosen(ui.item);
aItems = AIRTIME.library.getSelectedData();
origTrs = aItems;
html = ui.helper.html();
AIRTIME.library.removeFromChosen(ui.item);
} else if (
AIRTIME.library.getCurrentTable() ==
AIRTIME.library.podcastEpisodeDataTable
) {
origTrs = [$(ui.item).data("aData")];
html = ui.helper.html();
}
};
fnUpdate = function (event, ui) {
var prev = ui.item.prev();
//can't add items outside of shows.
if (
prev.find("td:first").hasClass("dataTables_empty") ||
prev.length === 0
) {
alert($.i18n._("Cannot schedule outside a show."));
ui.item.remove();
return;
}
//if item is added after a footer, add the item after the last item in the show.
if (prev.hasClass("sb-footer")) {
prev = prev.prev();
}
aItemData = [];
oPrevData = prev.data("aData");
//item was dragged in
if (origTrs !== undefined) {
$sbTable.find("tr.ui-draggable").empty().after(html);
aItemData = origTrs;
origTrs = undefined;
fnAdd();
}
//item was reordered.
else {
//ui.item
// .empty()
// .after(draggingContainer.html());
aItemData.push(ui.item.data("aData"));
fnMove();
}
};
return {
placeholder: "sb-placeholder ui-state-highlight",
//forcePlaceholderSize: true,
distance: 25,
helper: function (event, item) {
var selected = mod.getSelectedData(NOW_PLAYING_CLASS),
thead = $("#show_builder_table thead"),
colspan = thead.find("th").length,
trfirst = thead.find("tr:first"),
width = trfirst.width(),
height = trfirst.height(),
message;
//if nothing is checked select the dragged item.
if (selected.length === 0) {
selected = [item.data("aData")];
}
if (selected.length === 1) {
message = sprintf($.i18n._("Moving %s"), selected[0].title);
//draggingContainer = item; //Default DataTables drag and drop
draggingContainer = $("<tr/>")
.addClass("sb-helper")
.append("<td/>")
.find("td")
.attr("colspan", colspan)
.width(width)
.height(height)
.addClass("ui-state-highlight")
.append(message)
.end();
} else {
message = sprintf($.i18n._("Moving %s Items"), selected.length);
draggingContainer = $("<tr/>")
.addClass("sb-helper")
.append("<td/>")
.find("td")
.attr("colspan", colspan)
.width(width)
.height(height)
.addClass("ui-state-highlight")
.append(message)
.end();
}
helperData = selected;
return draggingContainer;
},
items:
"tr:not(:first, :last, .sb-header, .sb-not-allowed, .sb-past, .sb-now-playing, .sb-empty)",
cancel: ".sb-footer",
receive: fnReceive,
update: fnUpdate,
axis: "y",
containment: "document",
start: function (event, ui) {
var elements = $sbTable
.find("tr." + SB_SELECTED_CLASS)
.not("." + NOW_PLAYING_CLASS);
elements.hide();
},
stop: function () {
var elements = $sbTable
.find("tr." + SB_SELECTED_CLASS)
.not("." + NOW_PLAYING_CLASS);
elements.show();
},
};
})();
$sbTable.sortable(sortableConf);
//start setup of the builder toolbar.
$toolbar = $(".sb-content .fg-toolbar:first");
var footer = $(".sb-content .fg-toolbar:last"),
timerange = $(".sb-timerange");
$toolbar.append(timerange);
$menu = $("<div class='btn-toolbar'/>");
$menu
.append(
"<div class='btn-group'>" +
"<button class='btn btn-small dropdown-toggle' id='timeline-select' data-toggle='dropdown'>" +
$.i18n._("Select") +
" <span class='caret'></span>" +
"</button>" +
"<ul class='dropdown-menu'>" +
"<li id='timeline-sa'><a href='#'>" +
$.i18n._("Select all") +
"</a></li>" +
"<li id='timeline-sn'><a href='#'>" +
$.i18n._("Select none") +
"</a></li>" +
"</ul>" +
"</div>",
)
.append(
"<div class='btn-group'>" +
"<button title='" +
$.i18n._("Trim overbooked shows") +
"' class='ui-state-disabled btn btn-small btn-icon-text' disabled='disabled'>" +
"<i class='icon-white icon-cut'></i>" +
"<span>" +
$.i18n._("Trim overbooked shows") +
"</span>" +
"</button></div>",
)
.append(
"<div class='btn-group'>" +
"<button title='" +
$.i18n._("Remove selected scheduled items") +
"' class='ui-state-disabled btn btn-small btn-icon-text btn-danger' disabled='disabled'>" +
"<i class='icon-white icon-trash'></i>" +
"<span>" +
$.i18n._("Remove") +
"</span>" +
"</button></div>",
);
//if 'Add/Remove content' was chosen from the context menu
//in the Calendar do not append these buttons
if ($(".ui-dialog-content").length === 0) {
$menu
.append(
"<div class='btn-group'>" +
"<button title='" +
$.i18n._("Jump to the current playing track") +
"' class='ui-state-disabled btn btn-small btn-icon-text' disabled='disabled'>" +
"<i class='icon-white icon-step-forward'></i>" +
"<span>" +
$.i18n._("Jump to Current") +
"</span>" +
"</button></div>",
)
.append(
"<div class='btn-group'>" +
"<button title='" +
$.i18n._("Cancel current show") +
"' class='ui-state-disabled btn btn-small btn-icon-text btn-danger' disabled='disabled'>" +
"<i class='icon-white icon-ban-circle'></i>" +
"<span>" +
$.i18n._("Cancel current show") +
"</span>" +
"</button></div>",
);
}
if (localStorage.getItem("user-type") != "G") {
$toolbar.append($menu);
}
$menu = undefined;
$("#timeline-sa").click(function () {
mod.selectAll();
});
$("#timeline-sn").click(function () {
mod.selectNone();
});
//cancel current show
$toolbar
.find(".icon-ban-circle")
.parent()
.click(function () {
var $tr,
data,
msg = $.i18n._("Cancel Current Show?");
if (AIRTIME.button.isDisabled("icon-ban-circle", true) === true) {
return;
}
$tr = $sbTable.find("tr.sb-future:first");
if ($tr.hasClass("sb-current-show")) {
data = $tr.data("aData");
if (data.record === true) {
msg = $.i18n._("Stop recording current show?");
}
if (confirm(msg)) {
var url = baseUrl + "Schedule/cancel-current-show";
$.ajax({
url: url,
data: { format: "json", id: data.instance },
success: function (data) {
$("#library_content")
.find("#library_display")
.dataTable()
.fnStandingRedraw();
var oTable = $sbTable.dataTable();
oTable.fnDraw();
},
});
}
}
});
//jump to current
$toolbar
.find(".icon-step-forward")
.parent()
.click(function () {
if (AIRTIME.button.isDisabled("icon-step-forward", true) === true) {
return;
}
/*
var $scroll = $sbContent.find(".dataTables_scrolling"),
scrolled = $scroll.scrollTop(),
scrollingTop = $scroll.offset().top,
current = $sbTable.find("."+NOW_PLAYING_CLASS),
currentTop = current.offset().top;
$scroll.scrollTop(currentTop - scrollingTop + scrolled);
*/
mod.jumpToCurrentTrack();
});
//delete overbooked tracks.
$toolbar
.find(".icon-cut", true)
.parent()
.click(function () {
if (AIRTIME.button.isDisabled("icon-cut", true) === true) {
return;
}
var temp,
aItems = [],
trs = $sbTable.find(".sb-over.sb-future.sb-allowed");
trs.each(function () {
temp = $(this).data("aData");
aItems.push({
id: temp.id,
instance: temp.instance,
timestamp: temp.timestamp,
});
});
mod.fnRemove(aItems);
});
//delete selected tracks
$toolbar
.find(".icon-trash")
.parent()
.click(function () {
var button = $("#show_builder").find(".icon-trash").parent();
if (button.hasClass(DISABLED_CLASS)) {
return;
}
mod.fnRemoveSelectedItems();
});
//add events to cursors.
$sbTable.find("tbody").on("click", "div.marker", function (event) {
var $tr = $(this).parents("tr"),
$trs;
if ($tr.hasClass(CURSOR_SELECTED_CLASS)) {
mod.removeCursor($tr);
} else {
mod.selectCursor($tr);
}
if (event.ctrlKey === false) {
$trs = $sbTable.find("." + CURSOR_SELECTED_CLASS).not($tr);
mod.removeCursor($trs);
}
return false;
});
/*
* Select button dropdown state in the toolbar.
* The button has to be disabled to prevent the dropdown
* from opening
*/
$sbContent.on("mouseenter", ".btn-group #timeline-select", function (ev) {
$el = $(this);
if ($el.hasClass("ui-state-disabled")) {
$el.attr("disabled", "disabled");
} else {
$el.removeAttr("disabled");
}
});
};
return AIRTIME;
})(AIRTIME || {});