From 4d9abb5f7cd8f3ab58d7b3c686c687cb2479ee92 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 30 Mar 2012 16:30:17 -0400 Subject: [PATCH 1/6] CC-3533: "On the fly" Stream Rebroadcasting-> interruption problem while pause in-between DJ stream tracks - fixed by wrapping all the input.harbor source with mksafe() --- .../pypo/liquidsoap_scripts/ls_script.liq | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index f1b08588a..fa5d631f8 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -126,21 +126,21 @@ end def append_dj_inputs(master_harbor_input_port, master_harbor_input_mount_point, dj_harbor_input_port, dj_harbor_input_mount_point, s) = if master_harbor_input_port != 0 and master_harbor_input_mount_point != "" and dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then - master_dj = input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client, - max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect) - dj_live = input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client, - max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect) + master_dj = mksafe(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client, + max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)) + dj_live = mksafe(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client, + max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)) ignore(output.dummy(master_dj, fallible=true)) ignore(output.dummy(dj_live, fallible=true)) switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition, transition], [({!master_dj_enabled},master_dj), ({!live_dj_enabled},dj_live), ({true}, s)]) elsif master_harbor_input_port != 0 and master_harbor_input_mount_point != "" then - master_dj = input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client, - max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect) + master_dj = mksafe(input.harbor(id="master_harbor", master_harbor_input_mount_point, port=master_harbor_input_port, auth=check_master_dj_client, + max=40., on_connect=master_dj_connect, on_disconnect=master_dj_disconnect)) ignore(output.dummy(master_dj, fallible=true)) switch(id="master_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!master_dj_enabled},master_dj), ({true}, s)]) elsif dj_harbor_input_port != 0 and dj_harbor_input_mount_point != "" then - dj_live = input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client, - max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect) + dj_live = mksafe(input.harbor(id="live_dj_harbor", dj_harbor_input_mount_point, port=dj_harbor_input_port, auth=check_dj_client, + max=40., on_connect=live_dj_connect, on_disconnect=live_dj_disconnect)) ignore(output.dummy(dj_live, fallible=true)) switch(id="live_dj_switch", track_sensitive=false, transitions=[transition, transition], [({!live_dj_enabled},dj_live), ({true}, s)]) else From ebf530a97ca3168ed3072a38c2e800b100111c3b Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 2 Apr 2012 11:02:03 +0200 Subject: [PATCH 2/6] CC-3558 : Playlist->EditMetadata->Save Changes -> 404 page not found page appears --- airtime_mvc/application/controllers/LibraryController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index 5007d2932..36537c282 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -204,8 +204,7 @@ class LibraryController extends Zend_Controller_Action Logging::log($data['MDATA_KEY_FILEPATH']); Application_Model_RabbitMq::SendMessageToMediaMonitor("md_update", $data); - - $this->_helper->redirector('index'); + $this->_redirect('playlist/index'); } } From 9f0741a684d8e4e7687f6a1a19db224275670462 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 2 Apr 2012 11:07:28 +0200 Subject: [PATCH 3/6] CC-3174 : timeline don't highlight empty timeline. --- airtime_mvc/public/js/airtime/showbuilder/builder.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index c4407e06b..7e625f05c 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -413,7 +413,10 @@ var AIRTIME = (function(AIRTIME){ //if there is only 1 cursor on the page highlight it by default. if ($cursorRows.length === 1) { - $cursorRows.addClass("cursor-selected-row"); + $td = $cursorRows.find("td:first"); + if (!$td.hasClass("dataTables_empty")) { + $cursorRows.addClass("cursor-selected-row"); + } } } From eeb7273909ae650e8c40017e210e79716c1d6b37 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 2 Apr 2012 15:20:13 +0200 Subject: [PATCH 4/6] CC-3542 : When you delete the current show it takes all songs from that show out of the playout history --- .../controllers/ScheduleController.php | 27 ++-- airtime_mvc/application/models/Scheduler.php | 130 ++++++++++++------ .../application/models/ShowBuilder.php | 20 ++- airtime_mvc/public/css/showbuilder.css | 6 +- .../public/js/airtime/schedule/schedule.js | 38 +++-- .../public/js/airtime/showbuilder/builder.js | 80 ++++++++--- 6 files changed, 210 insertions(+), 91 deletions(-) diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index b970b55f3..37b347064 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -22,6 +22,7 @@ class ScheduleController extends Zend_Controller_Action ->addActionContext('edit-show', 'json') ->addActionContext('add-show', 'json') ->addActionContext('cancel-show', 'json') + ->addActionContext('cancel-current-show', 'json') ->addActionContext('get-form', 'json') ->addActionContext('upload-to-sound-cloud', 'json') ->addActionContext('content-context-menu', 'json') @@ -816,21 +817,21 @@ class ScheduleController extends Zend_Controller_Action public function cancelCurrentShowAction() { - $userInfo = Zend_Auth::getInstance()->getStorage()->read(); - $user = new Application_Model_User($userInfo->id); + $user = Application_Model_User::GetCurrentUser(); - if($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) { - $showInstanceId = $this->_getParam('id'); - try{ - $showInstance = new Application_Model_ShowInstance($showInstanceId); - }catch(Exception $e){ - $this->view->show_error = true; - return false; + if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) { + $id = $this->_getParam('id'); + + try { + $scheduler = new Application_Model_Scheduler(); + $scheduler->cancelShow($id); + } + catch (Exception $e) { + $this->view->error = $e->getMessage(); + Logging::log($e->getMessage()); + Logging::log("{$e->getFile()}"); + Logging::log("{$e->getLine()}"); } - $showInstance->clearShow(); - $showInstance->delete(); - - Application_Model_RabbitMq::PushSchedule(); } } diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php index 1e7a27752..c5adc6c6e 100644 --- a/airtime_mvc/application/models/Scheduler.php +++ b/airtime_mvc/application/models/Scheduler.php @@ -93,10 +93,10 @@ class Application_Model_Scheduler { throw new OutDatedScheduleException("The show {$show->getDbName()} is over and cannot be scheduled."); } - $origTs = intval($instanceInfo[$id]); - $currTs = intval($instance->getDbLastScheduled("U")) ? : 0; - if ($origTs !== $currTs) { - Logging::log("orig {$origTs} current {$currTs}"); + $ts = intval($instanceInfo[$id]); + $lastSchedTs = intval($instance->getDbLastScheduled("U")) ? : 0; + if ($ts < $lastSchedTs) { + Logging::log("ts {$ts} last sched {$lastSchedTs}"); throw new OutDatedScheduleException("The show {$show->getDbName()} has been previously updated!"); } } @@ -215,6 +215,41 @@ class Application_Model_Scheduler { return $nextDT; } + + /* + * @param int $showInstance + * @param array $exclude + * ids of sched items to remove from the calulation. + */ + private function removeGaps($showInstance, $exclude=null) { + + Logging::log("removing gaps from show instance #".$showInstance); + + $instance = CcShowInstancesQuery::create()->findPK($showInstance, $this->con); + if (is_null($instance)) { + throw new OutDatedScheduleException("The schedule you're viewing is out of date!"); + } + + $itemStartDT = $instance->getDbStarts(null); + + $schedule = CcScheduleQuery::create() + ->filterByDbInstanceId($showInstance) + ->filterByDbId($exclude, Criteria::NOT_IN) + ->orderByDbStarts() + ->find($this->con); + + + foreach ($schedule as $item) { + + $itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength()); + + $item->setDbStarts($itemStartDT) + ->setDbEnds($itemEndDT) + ->save($this->con); + + $itemStartDT = $itemEndDT; + } + } /* * @param array $scheduledIds @@ -280,16 +315,16 @@ class Application_Model_Scheduler { $sched = new CcSchedule(); } - $sched->setDbStarts($nextStartDT); - $sched->setDbEnds($endTimeDT); - $sched->setDbFileId($file['id']); - $sched->setDbCueIn($file['cuein']); - $sched->setDbCueOut($file['cueout']); - $sched->setDbFadeIn($file['fadein']); - $sched->setDbFadeOut($file['fadeout']); - $sched->setDbClipLength($file['cliplength']); - $sched->setDbInstanceId($instance->getDbId()); - $sched->save($this->con); + $sched->setDbStarts($nextStartDT) + ->setDbEnds($endTimeDT) + ->setDbFileId($file['id']) + ->setDbCueIn($file['cuein']) + ->setDbCueOut($file['cueout']) + ->setDbFadeIn($file['fadein']) + ->setDbFadeOut($file['fadeout']) + ->setDbClipLength($file['cliplength']) + ->setDbInstanceId($instance->getDbId()) + ->save($this->con); $nextStartDT = $endTimeDT; } @@ -504,40 +539,45 @@ class Application_Model_Scheduler { throw $e; } } - + /* - * @param int $showInstance - * @param array $exclude - * ids of sched items to remove from the calulation. + * Used for cancelling the current show instance. + * + * @param $p_id id of the show instance to cancel. */ - private function removeGaps($showInstance, $exclude=null) { - - Logging::log("removing gaps from show instance #".$showInstance); - - $instance = CcShowInstancesQuery::create()->findPK($showInstance, $this->con); - if (is_null($instance)) { - throw new OutDatedScheduleException("The schedule you're viewing is out of date!"); - } - - $itemStartDT = $instance->getDbStarts(null); - - $schedule = CcScheduleQuery::create() - ->filterByDbInstanceId($showInstance) - ->filterByDbId($exclude, Criteria::NOT_IN) - ->orderByDbStarts() - ->find($this->con); - - - foreach ($schedule as $item) { - - $itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength()); - - $item->setDbStarts($itemStartDT); - $item->setDbEnds($itemEndDT); - $item->save($this->con); - - $itemStartDT = $itemEndDT; + public function cancelShow($p_id) { + + $this->con->beginTransaction(); + + try { + + $instance = CcShowInstancesQuery::create()->findPK($p_id); + + $items = CcScheduleQuery::create() + ->filterByDbInstanceId($p_id) + ->filterByDbEnds($this->nowDT, Criteria::GREATER_THAN) + ->find($this->con); + + $remove = array(); + $ts = $this->nowDT->format('U'); + + for($i = 0; $i < count($items); $i++) { + $remove[$i]["instance"] = $p_id; + $remove[$i]["timestamp"] = $ts; + $remove[$i]["id"] = $items[$i]->getDbId(); + } + + $this->removeItems($remove, false); + + $instance->setDbEnds($this->nowDT); + $instance->save($this->con); + + $this->con->commit(); } + catch (Exception $e) { + $this->con->rollback(); + throw $e; + } } } diff --git a/airtime_mvc/application/models/ShowBuilder.php b/airtime_mvc/application/models/ShowBuilder.php index 8ccf59aa6..e1e5af664 100644 --- a/airtime_mvc/application/models/ShowBuilder.php +++ b/airtime_mvc/application/models/ShowBuilder.php @@ -18,6 +18,7 @@ class Application_Model_ShowBuilder { private $pos; private $contentDT; private $epoch_now; + private $currentShow; private $defaultRowArray = array( "header" => false, @@ -54,6 +55,7 @@ class Application_Model_ShowBuilder { $this->user = Application_Model_User::GetCurrentUser(); $this->opts = $p_opts; $this->epoch_now = floatval(microtime(true)); + $this->currentShow = false; } //check to see if this row should be editable by the user. @@ -118,7 +120,7 @@ class Application_Model_ShowBuilder { else if ($row["footer"] === true && $this->epoch_now < $p_epochItemEnd) { $row["scheduled"] = 2; } - else if ($row["header"] === true && $this->epoch_now > $p_epochItemStart) { + else if ($row["header"] === true && $this->epoch_now >= $p_epochItemStart) { $row["scheduled"] = 0; } else if ($row["header"] === true && $this->epoch_now < $p_epochItemEnd) { @@ -156,6 +158,14 @@ class Application_Model_ShowBuilder { $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC")); $showEndDT->setTimezone(new DateTimeZone($this->timezone)); $endsEpoch = floatval($showEndDT->format("U.u")); + + if ($startsEpoch < $this->epoch_now && $endsEpoch > $this->epoch_now) { + $row["currentShow"] = true; + $this->currentShow = true; + } + else { + $this->currentShow = false; + } $row["header"] = true; $row["starts"] = $showStartDT->format("Y-m-d H:i"); @@ -224,6 +234,10 @@ class Application_Model_ShowBuilder { $row["id"] = 0 ; $row["instance"] = intval($p_item["si_id"]); } + + if ($this->currentShow = true) { + $row["currentShow"] = true; + } $this->getItemColor($p_item, $row); $this->getRowTimestamp($p_item, $row); @@ -256,6 +270,10 @@ class Application_Model_ShowBuilder { $endsEpoch = floatval($showEndDT->format("U.u")); $row["refresh"] = floatval($showEndDT->format("U.u")) - $this->epoch_now; + + if ($this->currentShow = true) { + $row["currentShow"] = true; + } $this->getScheduledStatus($startsEpoch, $endsEpoch, $row); $this->isAllowed($p_item, $row); diff --git a/airtime_mvc/public/css/showbuilder.css b/airtime_mvc/public/css/showbuilder.css index aa03fe85c..ff66a83ae 100644 --- a/airtime_mvc/public/css/showbuilder.css +++ b/airtime_mvc/public/css/showbuilder.css @@ -24,7 +24,7 @@ .sb-content .fg-toolbar ul { float: left; padding: 0; - margin: 0.5em 0 0 0; + margin: 0.5em 10px 0 0; cursor: pointer; } @@ -176,6 +176,10 @@ table.datatable tr.sb-header.odd:hover td, table.datatable tr.sb-header.even:hov border-bottom-color:#6b6a6a !important; } +.sb-header div.ui-state-default { + float: left; +} + .sb-content table th.ui-state-default { background: -moz-linear-gradient(top, #b2b2b2 0, #a2a2a2 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #b2b2b2), color-stop(100%, #a2a2a2)); diff --git a/airtime_mvc/public/js/airtime/schedule/schedule.js b/airtime_mvc/public/js/airtime/schedule/schedule.js index 6513bb056..a720dd164 100644 --- a/airtime_mvc/public/js/airtime/schedule/schedule.js +++ b/airtime_mvc/public/js/airtime/schedule/schedule.js @@ -1,8 +1,14 @@ -/** -* -* Schedule Dialog creation methods. -* -*/ +var AIRTIME = (function(AIRTIME){ + var mod; + + if (AIRTIME.schedule === undefined) { + AIRTIME.schedule = {}; + } + mod = AIRTIME.schedule; + + return AIRTIME; + +}(AIRTIME || {})); var serverTimezoneOffset = 0; @@ -27,21 +33,27 @@ function checkShowLength(json) { } function confirmCancelShow(show_instance_id){ - if (confirm('Erase current show and stop playback?')){ - var url = "/Schedule/cancel-current-show/id/"+show_instance_id; + if (confirm('Cancel Current Show?')) { + var url = "/Schedule/cancel-current-show"; $.ajax({ - url: url, - success: function(data){scheduleRefetchEvents(data);} + url: url, + data: {format: "json", id: show_instance_id}, + success: function(data){ + scheduleRefetchEvents(data); + } }); } } function confirmCancelRecordedShow(show_instance_id){ - if (confirm('Erase current show and stop recording?')){ - var url = "/Schedule/cancel-current-show/id/"+show_instance_id; + if (confirm('Erase current show and stop recording?')) { + var url = "/Schedule/cancel-current-show"; $.ajax({ - url: url, - success: function(data){scheduleRefetchEvents(data);} + url: url, + data: {format: "json", id: show_instance_id}, + success: function(data){ + scheduleRefetchEvents(data); + } }); } } diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index 7e625f05c..02470e6db 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -363,6 +363,10 @@ var AIRTIME = (function(AIRTIME){ else if (aData.status === 0) { $(nRow).addClass("sb-over"); } + + if (aData.currentShow === true) { + $(nRow).addClass("sb-current-show"); + } //call the context menu so we can prevent the event from propagating. $(nRow).find('td:gt(1)').click(function(e){ @@ -372,6 +376,20 @@ var AIRTIME = (function(AIRTIME){ return false; }); }, + //remove any selected nodes before the draw. + "fnPreDrawCallback": function( oSettings ) { + var oTT; + + oTT = TableTools.fnGetInstance('show_builder_table'); + oTT.fnSelectNone(); + + //disable jump to current button. + AIRTIME.button.disableButton("sb-button-current"); + //disable deleting of overbooked tracks. + AIRTIME.button.disableButton("sb-button-trim"); + //disable cancelling current show. + AIRTIME.button.disableButton("sb-button-cancel"); + }, "fnDrawCallback": function(oSettings, json) { var wrapperDiv, markerDiv, @@ -454,22 +472,16 @@ var AIRTIME = (function(AIRTIME){ //enable deleting of overbooked tracks. AIRTIME.button.enableButton("sb-button-trim"); } + + $tr = $sbTable.find('tr.sb-future:first'); + if ($tr.hasClass('sb-current-show')) { + //enable cancelling current show. + AIRTIME.button.enableButton("sb-button-cancel"); + } }, "fnHeaderCallback": function(nHead) { $(nHead).find("input[type=checkbox]").attr("checked", false); }, - //remove any selected nodes before the draw. - "fnPreDrawCallback": function( oSettings ) { - var oTT; - - oTT = TableTools.fnGetInstance('show_builder_table'); - oTT.fnSelectNone(); - - //disable jump to current button. - AIRTIME.button.disableButton("sb-button-current"); - //disable deleting of overbooked tracks. - AIRTIME.button.disableButton("sb-button-trim"); - }, "oColVis": { "aiExclude": [ 0, 1 ] @@ -707,12 +719,44 @@ var AIRTIME = (function(AIRTIME){ //start setup of the builder toolbar. $toolbar = $(".sb-content .fg-toolbar"); - $toolbar - .append("
    ") - .find('ul') - .append('
  • ') - .append('
  • ') - .append('
  • '); + $ul = $("
      "); + $ul.append('
    • ') + .append('
    • '); + $toolbar.append($ul); + + $ul = $("
        "); + $ul.append('
      • ') + .append('
      • '); + $toolbar.append($ul); + + //jump to current + $toolbar.find('.sb-button-cancel') + .click(function() { + var $tr, + data; + + if (AIRTIME.button.isDisabled('sb-button-cancel') === true) { + return; + } + + $tr = $sbTable.find('tr.sb-future:first'); + + if ($tr.hasClass('sb-current-show')) { + data = $tr.data("aData"); + + if (confirm('Cancel Current Show?')) { + var url = "/Schedule/cancel-current-show"; + $.ajax({ + url: url, + data: {format: "json", id: data.instance}, + success: function(data){ + var oTable = $sbTable.dataTable(); + oTable.fnDraw(); + } + }); + } + } + }); //jump to current $toolbar.find('.sb-button-current') From c5b761bff34a9ba4a87e72e884fa27bbf0684184 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Mon, 2 Apr 2012 15:31:02 +0200 Subject: [PATCH 5/6] CC-3542 : When you delete the current show it takes all songs from that show out of the playout history --- airtime_mvc/application/models/Scheduler.php | 33 +++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php index c5adc6c6e..30e5a385d 100644 --- a/airtime_mvc/application/models/Scheduler.php +++ b/airtime_mvc/application/models/Scheduler.php @@ -552,23 +552,26 @@ class Application_Model_Scheduler { try { $instance = CcShowInstancesQuery::create()->findPK($p_id); - - $items = CcScheduleQuery::create() - ->filterByDbInstanceId($p_id) - ->filterByDbEnds($this->nowDT, Criteria::GREATER_THAN) - ->find($this->con); - $remove = array(); - $ts = $this->nowDT->format('U'); - - for($i = 0; $i < count($items); $i++) { - $remove[$i]["instance"] = $p_id; - $remove[$i]["timestamp"] = $ts; - $remove[$i]["id"] = $items[$i]->getDbId(); + if (!$instance->getDbRecord()) { + + $items = CcScheduleQuery::create() + ->filterByDbInstanceId($p_id) + ->filterByDbEnds($this->nowDT, Criteria::GREATER_THAN) + ->find($this->con); + + $remove = array(); + $ts = $this->nowDT->format('U'); + + for($i = 0; $i < count($items); $i++) { + $remove[$i]["instance"] = $p_id; + $remove[$i]["timestamp"] = $ts; + $remove[$i]["id"] = $items[$i]->getDbId(); + } + + $this->removeItems($remove, false); } - - $this->removeItems($remove, false); - + $instance->setDbEnds($this->nowDT); $instance->save($this->con); From 3639e2b4867ed63c6363df30a9de2f5762bbc5ca Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 2 Apr 2012 20:31:26 +0200 Subject: [PATCH 6/6] CC-3482 --- .../public/css/images/source-info_lines.png | Bin 1893 -> 3350 bytes .../css/images/source_to_switch_lines.png | Bin 0 -> 970 bytes airtime_mvc/public/css/masterpanel.css | 99 ++++++++++++------ 3 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 airtime_mvc/public/css/images/source_to_switch_lines.png diff --git a/airtime_mvc/public/css/images/source-info_lines.png b/airtime_mvc/public/css/images/source-info_lines.png index 75a1f435d03a8b5193da0a56dba4710a29de1818..2da86de83d1c33c825b3ea03fe78fdd985c04aa5 100644 GIT binary patch literal 3350 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 literal 1893 zcmaJ?4NMbf7_NX?>#)^8#-_q&RWKrLuay=~ip3U29qky>2pK5-xt5CU9qmC&5hDJC z>dbW1Fe>6AFcbd;jG|17Q>OUChHNrrYz&nRIz_;rF+_H)P)#;=$=!F~m*;)n=Y8Mj zayt^GANW$ww8<%WiX;J6qB@4$g<+U=7!ge(MXfYra%C2R0|lbd>iP8k1_vEz zRebu|a0yF-36V@~yv2a5u}G7ZmMmqYioP-$M44fNK!@ORVAgHa8(}k_?&gJwch@nK z4%`qti%)+oDn*hAgs1@l;S5%olEq~KF2sO1%XvKBGQeg*EGCQ1gxFya59Y9877L7D zbRwHUrG}G4apSp&9iN_w;~30jnoK5!iNin*8YUDO8R_C+v%?5Pm@!X}%gteW2{M^}xj_SD-mKpH3)!U4jnt(CUrjZ6X@RG|Mq2#9+BfngB{9{}0vaJZK}H zguK!FpTx%GJPctbAx1REpd<#TUg%1N!9oKf$5BHvif)|fVqzwWqsB}W1Hv`oV69fK zLQO_DS|WkPdLu5^D-p4XPbcCTTCEBWk6?uhOKd#0NA!f(4TPyHC>I^Xx6&aN#Uz{W{n-ggI}+zL${T6} zE+8#k_RuR!R}@>RJyl>Bl&)^ymc~QuPH^F1p*K1j zy8Q0ex=UX;N20H{%dY(Wjdi_DmfCS9HND+A>VKo7Q@)8xr&SJ_>(5RvT2PZ9-~Sw( zYrEu^-hQDV?Ox!6e&?)#TiR)Ede-m3oeMyC&~Vije?p5_(NT^&b>lPdfn1Zq?*{d8 z<|XUQC}7N`42X__UDE#WQFn zewMt;<^D0A#u3UIH+VxfX`k<|lVIeyz}j;Ew^u!5-+noxX<+R1!REmIzLiF2OnaC8 zDL0>Olj41a`E}oo9<<}3WQb*J=_?Z#&sN`FP|Dw7$E_H(o3-w55?#*i3ChschK5@w1(X0K3V>!J~9Nc`0<$xV889KkD zOsq-mnKxR*adyj|+EynR&UWNj zt!qc_XE_J->pbOb7G$&*5&c`r={JgZi%S4bWjcHUNDb(yqLq@acK!Iou~N~ART*3U E0npU0HV%95sqmRh5bmT?*65U5R~XH*sxZN@BZhBwR&FP&3xCFOAjOXY4C>yMSP7 z#lVJG`2*TNfPopr#)3MruvJ1414u}ebDE?K1WV`p(0lLqaqo^Ao9pv)x92#Ho3C$} zO*UU+V{vwd{qKH!b;hP!)auX{>Cx1QF<13S7lV4>?BFJLyn{#YafRawz;AbG$9|x> zBoLe&BV<9u*c?|`%_7I$#T0b$jvwm$x3}+j;Cnj1RkEQS8MyCn9LBhHxY>3OcU{%v zSMPyJrZIs4QwLevlnkQjrK0E;dxLr_Knl9v@l zxdSAKpa>-qNkyb+vZO%>Mjy|j#a>Tqnrow2Y^U>mN+V4a(=-)QSs?L_h*VY0H6*FX z5XIymq)t{0lclkOi4!;WBkGe7{Fd*nO>FS02i(8YCA=NVt{eNR(rP_asASurdXAq%arPOFAgwq(^>s~P1n*Cg(K zfI~XwdRN@iRBmpBAYzeC9Q%*4w-%EC3=7u$>A6T#^+sH8dM@%*uE>%R^K1R