From 1957c7827f3743e4f5235f9ef5acffe4a5d9e283 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Wed, 12 Aug 2015 14:35:27 -0400 Subject: [PATCH] SAAS-948, SAAS-983 - Showbuilder editor redesign, look and feel fixes --- .../controllers/NewPlaylistController.php | 13 +- .../controllers/ShowBuilderController.php | 2 +- .../forms/SmartBlockCriteriaNew.php | 615 ++++++++++++++++++ .../layouts/scripts/showbuilder.phtml | 6 +- .../scripts/form/_smart-block-criteria.phtml | 148 +++++ .../views/scripts/playlist/_playlist.phtml | 4 +- .../views/scripts/playlist/_smart-block.phtml | 7 +- .../views/scripts/show-builder/index.phtml | 2 +- airtime_mvc/public/css/_showbuilder.css | 124 +++- airtime_mvc/public/css/media_library.css | 6 +- airtime_mvc/public/css/styles.css | 62 +- .../public/js/airtime/common/common.js | 28 +- .../public/js/airtime/library/_library.js | 12 +- airtime_mvc/public/js/airtime/library/_spl.js | 7 +- .../airtime/playlist/_smart_blockbuilder.js | 609 +++++++++++++++++ .../js/airtime/playlist/smart_blockbuilder.js | 2 +- .../public/js/airtime/showbuilder/_builder.js | 8 +- .../js/airtime/showbuilder/_main_builder.js | 6 +- 18 files changed, 1573 insertions(+), 88 deletions(-) create mode 100644 airtime_mvc/application/forms/SmartBlockCriteriaNew.php create mode 100644 airtime_mvc/application/views/scripts/form/_smart-block-criteria.phtml create mode 100644 airtime_mvc/public/js/airtime/playlist/_smart_blockbuilder.js diff --git a/airtime_mvc/application/controllers/NewPlaylistController.php b/airtime_mvc/application/controllers/NewPlaylistController.php index 217f21943..b8d2f231a 100644 --- a/airtime_mvc/application/controllers/NewPlaylistController.php +++ b/airtime_mvc/application/controllers/NewPlaylistController.php @@ -86,7 +86,7 @@ class NewPlaylistController extends Zend_Controller_Action $this->view->length = $formatter->format(); if ($isBlock) { - $form = new Application_Form_SmartBlockCriteria(); + $form = new Application_Form_SmartBlockCriteriaNew(); $form->removeDecorator('DtDdWrapper'); $form->startForm($obj->getId(), $formIsValid); @@ -531,7 +531,7 @@ class NewPlaylistController extends Zend_Controller_Action } catch (BlockNotFoundException $e) { $this->playlistNotFound('block', true); } - $form = new Application_Form_SmartBlockCriteria(); + $form = new Application_Form_SmartBlockCriteriaNew(); $form->startForm($params['obj_id']); if ($form->isValid($params)) { $this->setPlaylistNameDescAction(); @@ -547,14 +547,15 @@ class NewPlaylistController extends Zend_Controller_Action $this->view->id = $bl->getId(); $result['html'] = $this->view->render($viewPath); $result['result'] = 1; - $result['type'] = "sb"; - $result['id'] = $bl->getId(); } + $result['type'] = "sb"; + $result['id'] = $bl->getId(); + $result["modified"] = $bl->getLastModified("U"); } else if ($params['type'] == 'playlist') { + $result["modified"] = $this->view->modified; $this->setPlaylistNameDescAction(); } - $result["modified"] = $this->view->modified; $this->_helper->json->sendJson($result); } @@ -567,7 +568,7 @@ class NewPlaylistController extends Zend_Controller_Action try { $bl = new Application_Model_Block($params['obj_id']); - $form = new Application_Form_SmartBlockCriteria(); + $form = new Application_Form_SmartBlockCriteriaNew(); $form->startForm($params['obj_id']); if ($form->isValid($params)) { $result = $bl->generateSmartBlock($params['data']); diff --git a/airtime_mvc/application/controllers/ShowBuilderController.php b/airtime_mvc/application/controllers/ShowBuilderController.php index c9bdd65e6..e25367795 100644 --- a/airtime_mvc/application/controllers/ShowBuilderController.php +++ b/airtime_mvc/application/controllers/ShowBuilderController.php @@ -46,7 +46,7 @@ class ShowBuilderController extends Zend_Controller_Action { // MEDIA BUILDER $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/_spl.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/playlist/smart_blockbuilder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/playlist/_smart_blockbuilder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headLink()->appendStylesheet($baseUrl.'css/playlist_builder.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.ui.timepicker.css?'.$CC_CONFIG['airtime_version']); diff --git a/airtime_mvc/application/forms/SmartBlockCriteriaNew.php b/airtime_mvc/application/forms/SmartBlockCriteriaNew.php new file mode 100644 index 000000000..ff2d87c38 --- /dev/null +++ b/airtime_mvc/application/forms/SmartBlockCriteriaNew.php @@ -0,0 +1,615 @@ + "", + "album_title" => "s", + "bit_rate" => "n", + "bpm" => "n", + "composer" => "s", + "conductor" => "s", + "copyright" => "s", + "cuein" => "n", + "cueout" => "n", + "artist_name" => "s", + "encoded_by" => "s", + "utime" => "n", + "mtime" => "n", + "lptime" => "n", + "genre" => "s", + "isrc_number" => "s", + "label" => "s", + "language" => "s", + "length" => "n", + "mime" => "s", + "mood" => "s", + "owner_id" => "s", + "replay_gain" => "n", + "sample_rate" => "n", + "track_title" => "s", + "track_number" => "n", + "info_url" => "s", + "year" => "n" + ); + + private function getCriteriaOptions($option = null) + { + if (!isset($this->criteriaOptions)) { + $this->criteriaOptions = array( + 0 => _("Select criteria"), + "album_title" => _("Album"), + "bit_rate" => _("Bit Rate (Kbps)"), + "bpm" => _("BPM"), + "composer" => _("Composer"), + "conductor" => _("Conductor"), + "copyright" => _("Copyright"), + "cuein" => _("Cue In"), + "cueout" => _("Cue Out"), + "artist_name" => _("Creator"), + "encoded_by" => _("Encoded By"), + "genre" => _("Genre"), + "isrc_number" => _("ISRC"), + "label" => _("Label"), + "language" => _("Language"), + "mtime" => _("Last Modified"), + "lptime" => _("Last Played"), + "length" => _("Length"), + "mime" => _("Mime"), + "mood" => _("Mood"), + "owner_id" => _("Owner"), + "replay_gain" => _("Replay Gain"), + "sample_rate" => _("Sample Rate (kHz)"), + "track_title" => _("Title"), + "track_number" => _("Track Number"), + "utime" => _("Uploaded"), + "info_url" => _("Website"), + "year" => _("Year") + ); + } + + if (is_null($option)) return $this->criteriaOptions; + else return $this->criteriaOptions[$option]; + } + + private function getStringCriteriaOptions() + { + if (!isset($this->stringCriteriaOptions)) { + $this->stringCriteriaOptions = array( + "0" => _("Select modifier"), + "contains" => _("contains"), + "does not contain" => _("does not contain"), + "is" => _("is"), + "is not" => _("is not"), + "starts with" => _("starts with"), + "ends with" => _("ends with") + ); + } + return $this->stringCriteriaOptions; + } + + private function getNumericCriteriaOptions() + { + if (!isset($this->numericCriteriaOptions)) { + $this->numericCriteriaOptions = array( + "0" => _("Select modifier"), + "is" => _("is"), + "is not" => _("is not"), + "is greater than" => _("is greater than"), + "is less than" => _("is less than"), + "is in the range" => _("is in the range") + ); + } + return $this->numericCriteriaOptions; + } + + private function getLimitOptions() + { + if (!isset($this->limitOptions)) { + $this->limitOptions = array( + "hours" => _("hours"), + "minutes" => _("minutes"), + "items" => _("items") + ); + } + return $this->limitOptions; + } + private function getSortOptions() + { + if (!isset($this->sortOptions)) { + $this->sortOptions = array( + "random" => _("random"), + "newest" => _("newest"), + "oldest" => _("oldest") + ); + } + return $this->sortOptions; + } + + + public function init() + { + } + + /* + * converts UTC timestamp citeria into user timezone strings. + */ + private function convertTimestamps(&$criteria) + { + $columns = array("utime", "mtime", "lptime"); + + foreach ($columns as $column) { + + if (isset($criteria[$column])) { + + foreach ($criteria[$column] as &$constraint) { + + $constraint['value'] = + Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['value']); + + if (isset($constraint['extra'])) { + $constraint['extra'] = + Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['extra']); + } + } + } + } + } + + public function startForm($p_blockId, $p_isValid = false) + { + // load type + $out = CcBlockQuery::create()->findPk($p_blockId); + if ($out->getDbType() == "static") { + $blockType = 0; + } else { + $blockType = 1; + } + + $spType = new Zend_Form_Element_Radio('sp_type'); + $spType->setLabel(_('Set smart block type:')) + ->setDecorators(array('viewHelper')) + ->setMultiOptions(array( + 'static' => _('Static'), + 'dynamic' => _('Dynamic') + )) + ->setValue($blockType); + $this->addElement($spType); + + $bl = new Application_Model_Block($p_blockId); + $storedCrit = $bl->getCriteria(); + + //need to convert criteria to be displayed in the user's timezone if there's some timestamp type. + self::convertTimestamps($storedCrit["crit"]); + + /* $modRoadMap stores the number of same criteria + * Ex: 3 Album titles, and 2 Track titles + * We need to know this so we display the form elements properly + */ + $modRowMap = array(); + + $openSmartBlockOption = false; + if (!empty($storedCrit)) { + $openSmartBlockOption = true; + } + + $criteriaKeys = array(); + if (isset($storedCrit["crit"])) { + $criteriaKeys = array_keys($storedCrit["crit"]); + } + $numElements = count($this->getCriteriaOptions()); + for ($i = 0; $i < $numElements; $i++) { + $criteriaType = ""; + + if (isset($criteriaKeys[$i])) { + $critCount = count($storedCrit["crit"][$criteriaKeys[$i]]); + } else { + $critCount = 1; + } + + $modRowMap[$i] = $critCount; + + /* Loop through all criteria with the same field + * Ex: all criteria for 'Album' + */ + for ($j = 0; $j < $critCount; $j++) { + /****************** CRITERIA ***********/ + if ($j > 0) { + $invisible = ' sp-invisible'; + } else { + $invisible = ''; + } + + $criteria = new Zend_Form_Element_Select("sp_criteria_field_".$i."_".$j); + $criteria->setAttrib('class', 'input_select sp_input_select'.$invisible) + ->setValue('Select criteria') + ->setDecorators(array('viewHelper')) + ->setMultiOptions($this->getCriteriaOptions()); + if ($i != 0 && !isset($criteriaKeys[$i])) { + $criteria->setAttrib('disabled', 'disabled'); + } + + if (isset($criteriaKeys[$i])) { + $criteriaType = $this->criteriaTypes[$storedCrit["crit"][$criteriaKeys[$i]][$j]["criteria"]]; + $criteria->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["criteria"]); + } + $this->addElement($criteria); + + /****************** MODIFIER ***********/ + $criteriaModifers = new Zend_Form_Element_Select("sp_criteria_modifier_".$i."_".$j); + $criteriaModifers->setValue('Select modifier') + ->setAttrib('class', 'input_select sp_input_select') + ->setDecorators(array('viewHelper')); + if ($i != 0 && !isset($criteriaKeys[$i])) { + $criteriaModifers->setAttrib('disabled', 'disabled'); + } + if (isset($criteriaKeys[$i])) { + if ($criteriaType == "s") { + $criteriaModifers->setMultiOptions($this->getStringCriteriaOptions()); + } else { + $criteriaModifers->setMultiOptions($this->getNumericCriteriaOptions()); + } + $criteriaModifers->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["modifier"]); + } else { + $criteriaModifers->setMultiOptions(array('0' => _('Select modifier'))); + } + $this->addElement($criteriaModifers); + + /****************** VALUE ***********/ + $criteriaValue = new Zend_Form_Element_Text("sp_criteria_value_".$i."_".$j); + $criteriaValue->setAttrib('class', 'input_text sp_input_text') + ->setDecorators(array('viewHelper')); + if ($i != 0 && !isset($criteriaKeys[$i])) { + $criteriaValue->setAttrib('disabled', 'disabled'); + } + if (isset($criteriaKeys[$i])) { + $criteriaValue->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["value"]); + } + $this->addElement($criteriaValue); + + /****************** EXTRA ***********/ + $criteriaExtra = new Zend_Form_Element_Text("sp_criteria_extra_".$i."_".$j); + $criteriaExtra->setAttrib('class', 'input_text sp_extra_input_text') + ->setDecorators(array('viewHelper')); + if (isset($criteriaKeys[$i]) && isset($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"])) { + $criteriaExtra->setValue($storedCrit["crit"][$criteriaKeys[$i]][$j]["extra"]); + $criteriaValue->setAttrib('class', 'input_text sp_extra_input_text'); + } else { + $criteriaExtra->setAttrib('disabled', 'disabled'); + } + $this->addElement($criteriaExtra); + + }//for + + }//for + + $repeatTracks = new Zend_Form_Element_Checkbox('sp_repeat_tracks'); + $repeatTracks->setDecorators(array('viewHelper')) + ->setLabel(_('Allow Repeat Tracks:')); + if (isset($storedCrit["repeat_tracks"])) { + $repeatTracks->setChecked($storedCrit["repeat_tracks"]["value"] == 1?true:false); + } + $this->addElement($repeatTracks); + + $sort = new Zend_Form_Element_Select('sp_sort_options'); + $sort->setAttrib('class', 'sp_input_select') + ->setDecorators(array('viewHelper')) + ->setMultiOptions($this->getSortOptions()); + if (isset($storedCrit["sort"])) { + $sort->setValue($storedCrit["sort"]["value"]); + } + $this->addElement($sort); + + $limit = new Zend_Form_Element_Select('sp_limit_options'); + $limit->setAttrib('class', 'sp_input_select') + ->setDecorators(array('viewHelper')) + ->setMultiOptions($this->getLimitOptions()); + if (isset($storedCrit["limit"])) { + $limit->setValue($storedCrit["limit"]["modifier"]); + } + $this->addElement($limit); + + $limitValue = new Zend_Form_Element_Text('sp_limit_value'); + $limitValue->setAttrib('class', 'sp_input_text_limit') + ->setLabel(_('Limit to')) + ->setDecorators(array('viewHelper')); + $this->addElement($limitValue); + if (isset($storedCrit["limit"])) { + $limitValue->setValue($storedCrit["limit"]["value"]); + } else { + // setting default to 1 hour + $limitValue->setValue(1); + } + + //getting block content candidate count that meets criteria + $bl = new Application_Model_Block($p_blockId); + if ($p_isValid) { + $files = $bl->getListofFilesMeetCriteria(); + $showPoolCount = true; + } else { + $files = null; + $showPoolCount = false; + } + + $generate = new Zend_Form_Element_Button('generate_button'); + $generate->setAttrib('class', 'sp-button btn'); + $generate->setAttrib('title', _('Generate playlist content and save criteria')); + $generate->setIgnore(true); + $generate->setLabel(_('Generate')); + $generate->setDecorators(array('viewHelper')); + $this->addElement($generate); + + $shuffle = new Zend_Form_Element_Button('shuffle_button'); + $shuffle->setAttrib('class', 'sp-button btn'); + $shuffle->setAttrib('title', _('Shuffle playlist content')); + $shuffle->setIgnore(true); + $shuffle->setLabel(_('Shuffle')); + $shuffle->setDecorators(array('viewHelper')); + $this->addElement($shuffle); + + $this->setDecorators(array( + array('ViewScript', array('viewScript' => 'form/_smart-block-criteria.phtml', "openOption"=> $openSmartBlockOption, + 'criteriasLength' => count($this->getCriteriaOptions()), 'poolCount' => $files['count'], 'modRowMap' => $modRowMap, + 'showPoolCount' => $showPoolCount)) + )); + } + + public function preValidation($params) + { + $data = Application_Model_Block::organizeSmartPlaylistCriteria($params['data']); + // add elelments that needs to be added + // set multioption for modifier according to criteria_field + $modRowMap = array(); + foreach ($data['criteria'] as $critKey=>$d) { + $count = 1; + foreach ($d as $modKey=>$modInfo) { + if ($modKey == 0) { + $eleCrit = $this->getElement("sp_criteria_field_".$critKey."_".$modKey); + $eleCrit->setValue($this->getCriteriaOptions($modInfo['sp_criteria_field'])); + $eleCrit->setAttrib("disabled", null); + + $eleMod = $this->getElement("sp_criteria_modifier_".$critKey."_".$modKey); + $criteriaType = $this->criteriaTypes[$modInfo['sp_criteria_field']]; + if ($criteriaType == "s") { + $eleMod->setMultiOptions($this->getStringCriteriaOptions()); + } elseif ($criteriaType == "n") { + $eleMod->setMultiOptions($this->getNumericCriteriaOptions()); + } else { + $eleMod->setMultiOptions(array('0' => _('Select modifier'))); + } + $eleMod->setValue($modInfo['sp_criteria_modifier']); + $eleMod->setAttrib("disabled", null); + + $eleValue = $this->getElement("sp_criteria_value_".$critKey."_".$modKey); + $eleValue->setValue($modInfo['sp_criteria_value']); + $eleValue->setAttrib("disabled", null); + + if (isset($modInfo['sp_criteria_extra'])) { + $eleExtra = $this->getElement("sp_criteria_extra_".$critKey."_".$modKey); + $eleExtra->setValue($modInfo['sp_criteria_extra']); + $eleValue->setAttrib('class', 'input_text sp_extra_input_text'); + $eleExtra->setAttrib("disabled", null); + } + + } else { + $criteria = new Zend_Form_Element_Select("sp_criteria_field_".$critKey."_".$modKey); + $criteria->setAttrib('class', 'input_select sp_input_select sp-invisible') + ->setValue('Select criteria') + ->setDecorators(array('viewHelper')) + ->setMultiOptions($this->getCriteriaOptions()); + + $criteriaType = $this->criteriaTypes[$modInfo['sp_criteria_field']]; + $criteria->setValue($this->getCriteriaOptions($modInfo['sp_criteria_field'])); + $this->addElement($criteria); + + /****************** MODIFIER ***********/ + $criteriaModifers = new Zend_Form_Element_Select("sp_criteria_modifier_".$critKey."_".$modKey); + $criteriaModifers->setValue('Select modifier') + ->setAttrib('class', 'input_select sp_input_select') + ->setDecorators(array('viewHelper')); + + if ($criteriaType == "s") { + $criteriaModifers->setMultiOptions($this->getStringCriteriaOptions()); + } elseif ($criteriaType == "n") { + $criteriaModifers->setMultiOptions($this->getNumericCriteriaOptions()); + } else { + $criteriaModifers->setMultiOptions(array('0' => _('Select modifier'))); + } + $criteriaModifers->setValue($modInfo['sp_criteria_modifier']); + $this->addElement($criteriaModifers); + + /****************** VALUE ***********/ + $criteriaValue = new Zend_Form_Element_Text("sp_criteria_value_".$critKey."_".$modKey); + $criteriaValue->setAttrib('class', 'input_text sp_input_text') + ->setDecorators(array('viewHelper')); + $criteriaValue->setValue($modInfo['sp_criteria_value']); + $this->addElement($criteriaValue); + + /****************** EXTRA ***********/ + $criteriaExtra = new Zend_Form_Element_Text("sp_criteria_extra_".$critKey."_".$modKey); + $criteriaExtra->setAttrib('class', 'input_text sp_extra_input_text') + ->setDecorators(array('viewHelper')); + if (isset($modInfo['sp_criteria_extra'])) { + $criteriaExtra->setValue($modInfo['sp_criteria_extra']); + $criteriaValue->setAttrib('class', 'input_text sp_extra_input_text'); + } else { + $criteriaExtra->setAttrib('disabled', 'disabled'); + } + $this->addElement($criteriaExtra); + $count++; + } + } + $modRowMap[$critKey] = $count; + } + + $decorator = $this->getDecorator("ViewScript"); + $existingModRow = $decorator->getOption("modRowMap"); + foreach ($modRowMap as $key=>$v) { + $existingModRow[$key] = $v; + } + $decorator->setOption("modRowMap", $existingModRow); + + // reconstruct the params['criteria'] so we can populate the form + $formData = array(); + foreach ($params['data'] as $ele) { + $formData[$ele['name']] = $ele['value']; + } + + $this->populate($formData); + + return $data; + } + + public function isValid($params) + { + $isValid = true; + $data = $this->preValidation($params); + $criteria2PeerMap = array( + 0 => "Select criteria", + "album_title" => "DbAlbumTitle", + "artist_name" => "DbArtistName", + "bit_rate" => "DbBitRate", + "bpm" => "DbBpm", + "composer" => "DbComposer", + "conductor" => "DbConductor", + "copyright" => "DbCopyright", + "cuein" => "DbCuein", + "cueout" => "DbCueout", + "encoded_by" => "DbEncodedBy", + "utime" => "DbUtime", + "mtime" => "DbMtime", + "lptime" => "DbLPtime", + "genre" => "DbGenre", + "info_url" => "DbInfoUrl", + "isrc_number" => "DbIsrcNumber", + "label" => "DbLabel", + "language" => "DbLanguage", + "length" => "DbLength", + "mime" => "DbMime", + "mood" => "DbMood", + "owner_id" => "DbOwnerId", + "replay_gain" => "DbReplayGain", + "sample_rate" => "DbSampleRate", + "track_title" => "DbTrackTitle", + "track_number" => "DbTrackNumber", + "year" => "DbYear" + ); + + // things we need to check + // 1. limit value shouldn't be empty and has upperbound of 24 hrs + // 2. sp_criteria or sp_criteria_modifier shouldn't be 0 + // 3. validate formate according to DB column type + $multiplier = 1; + $result = 0; + + // validation start + if ($data['etc']['sp_limit_options'] == 'hours') { + $multiplier = 60; + } + if ($data['etc']['sp_limit_options'] == 'hours' || $data['etc']['sp_limit_options'] == 'mins') { + $element = $this->getElement("sp_limit_value"); + if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) { + $element->addError(_("Limit cannot be empty or smaller than 0")); + $isValid = false; + } else { + $mins = floatval($data['etc']['sp_limit_value']) * $multiplier; + if ($mins > 1440) { + $element->addError(_("Limit cannot be more than 24 hrs")); + $isValid = false; + } + } + } else { + $element = $this->getElement("sp_limit_value"); + if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) { + $element->addError(_("Limit cannot be empty or smaller than 0")); + $isValid = false; + } elseif (!ctype_digit($data['etc']['sp_limit_value'])) { + $element->addError(_("The value should be an integer")); + $isValid = false; + } elseif (intval($data['etc']['sp_limit_value']) > 500) { + $element->addError(_("500 is the max item limit value you can set")); + $isValid = false; + } + } + + if (isset($data['criteria'])) { + foreach ($data['criteria'] as $rowKey=>$row) { + foreach ($row as $key=>$d) { + $element = $this->getElement("sp_criteria_field_".$rowKey."_".$key); + // check for not selected select box + if ($d['sp_criteria_field'] == "0" || $d['sp_criteria_modifier'] == "0") { + $element->addError(_("You must select Criteria and Modifier")); + $isValid = false; + } else { + $column = CcFilesPeer::getTableMap()->getColumnByPhpName($criteria2PeerMap[$d['sp_criteria_field']]); + // validation on type of column + if (in_array($d['sp_criteria_field'], array('length', 'cuein', 'cueout'))) { + if (!preg_match("/^(\d{2}):(\d{2}):(\d{2})/", $d['sp_criteria_value'])) { + $element->addError(_("'Length' should be in '00:00:00' format")); + $isValid = false; + } + } elseif ($column->getType() == PropelColumnTypes::TIMESTAMP) { + if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_value'])) { + $element->addError(_("The value should be in timestamp format (e.g. 0000-00-00 or 0000-00-00 00:00:00)")); + $isValid = false; + } else { + $result = Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_value']); + if (!$result["success"]) { + // check for if it is in valid range( 1753-01-01 ~ 12/31/9999 ) + $element->addError($result["errMsg"]); + $isValid = false; + } + } + + if (isset($d['sp_criteria_extra'])) { + if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_extra'])) { + $element->addError(_("The value should be in timestamp format (e.g. 0000-00-00 or 0000-00-00 00:00:00)")); + $isValid = false; + } else { + $result = Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_extra']); + if (!$result["success"]) { + // check for if it is in valid range( 1753-01-01 ~ 12/31/9999 ) + $element->addError($result["errMsg"]); + $isValid = false; + } + } + } + } elseif ($column->getType() == PropelColumnTypes::INTEGER && + $d['sp_criteria_field'] != 'owner_id') { + if (!is_numeric($d['sp_criteria_value'])) { + $element->addError(_("The value has to be numeric")); + $isValid = false; + } + // length check + if ($d['sp_criteria_value'] >= pow(2,31)) { + $element->addError(_("The value should be less then 2147483648")); + $isValid = false; + } + } elseif ($column->getType() == PropelColumnTypes::VARCHAR) { + if (strlen($d['sp_criteria_value']) > $column->getSize()) { + $element->addError(sprintf(_("The value should be less than %s characters"), $column->getSize())); + $isValid = false; + } + } + } + + if ($d['sp_criteria_value'] == "") { + $element->addError(_("Value cannot be empty")); + $isValid = false; + } + }//end foreach + }//for loop + }//if + + return $isValid; + } +} diff --git a/airtime_mvc/application/layouts/scripts/showbuilder.phtml b/airtime_mvc/application/layouts/scripts/showbuilder.phtml index 4f373fd91..d2f0f78eb 100644 --- a/airtime_mvc/application/layouts/scripts/showbuilder.phtml +++ b/airtime_mvc/application/layouts/scripts/showbuilder.phtml @@ -101,10 +101,10 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
- -
>
-
+ +
>
+ layout()->content ?>
diff --git a/airtime_mvc/application/views/scripts/form/_smart-block-criteria.phtml b/airtime_mvc/application/views/scripts/form/_smart-block-criteria.phtml new file mode 100644 index 000000000..ca85d3e2e --- /dev/null +++ b/airtime_mvc/application/views/scripts/form/_smart-block-criteria.phtml @@ -0,0 +1,148 @@ +
+
+ +
+ + element->getElement('sp_type')->getValue(); + foreach ($this->element->getElement('sp_type')->getMultiOptions() as $radio) : ?> + + + + + +
+ +
+ criteriasLength; $i++) { + // modRowMap holds the number of modifier rows for each criteria element + // i.e. if we have 'Album contains 1' and 'Album contains 2' the modRowMap + // for Album is 2 + ?> + modRowMap[$i]; $j++) { + // determine if logic label should be 'and' or 'or' + if ($this->modRowMap[$i] > 1 && $j != $this->modRowMap[$i]-1) $logicLabel = _('or'); + else $logicLabel = _('and'); + + $disabled = $this->element->getElement("sp_criteria_field_".$i."_".$j)->getAttrib('disabled') == 'disabled'?true:false; + // determine if the next row is disabled and only display the logic label if it isn't + if ($j == $this->modRowMap[$i]-1 && $i < 25) { + $n = $i+1; + $nextIndex = $n."_0"; + } elseif ($j+1 <= $this->modRowMap[$i]-1) { + $n = $j+1; + $nextIndex = $i."_".$n; + + } + $nextDisabled = $this->element->getElement("sp_criteria_field_".$nextIndex)->getAttrib('disabled') == 'disabled'?true:false; + ?> +
0) && $disabled) { + echo 'style=display:none'; + } ?>> + element->getElement("sp_criteria_field_".$i."_".$j) ?> + + + + element->getElement("sp_criteria_modifier_".$i."_".$j) ?> + element->getElement("sp_criteria_value_".$i."_".$j) ?> + element->getElement("sp_criteria_extra_".$i."_".$j)->getAttrib("disabled") == "disabled"?'style="display:none;"':""?>>element->getElement('sp_criteria_extra_'.$i."_".$j) ?> + + + + + > + + + element->getElement("sp_criteria_field_".$i."_".$j)->hasErrors()) : ?> + element->getElement("sp_criteria_field_".$i."_".$j)->getMessages() as $error): ?> + + + + + +
+ + + +
+ +
+ element->getElement('sp_repeat_tracks')->getLabel() ?> + element->getElement('sp_repeat_tracks')?> + element->getElement("sp_repeat_tracks")->hasErrors()) : ?> + element->getElement("sp_repeat_tracks")->getMessages() as $error): ?> + + + + + + +
+
+ Sort tracks by + element->getElement('sp_sort_options') ?> + element->getElement("sp_sort_options")->hasErrors()) : ?> + element->getElement("sp_sort_options")->getMessages() as $error): ?> + + + + + +
+
+ element->getElement('sp_limit_value')->getLabel() ?> + element->getElement('sp_limit_value')?> + element->getElement('sp_limit_options') ?> + element->getElement("sp_limit_value")->hasErrors()) : ?> + element->getElement("sp_limit_value")->getMessages() as $error): ?> + + + + + +
+ + showPoolCount) { ?> +
+ + poolCount > 1) { + echo $this->poolCount; + ?> + + + + poolCount == 1) { + echo $this->poolCount; + ?> + + + + + 0 + + + +
+ +
+ +
+ +
+
+ element->getElement('generate_button');?> +
+
+ element->getElement('shuffle_button');?> +
+
diff --git a/airtime_mvc/application/views/scripts/playlist/_playlist.phtml b/airtime_mvc/application/views/scripts/playlist/_playlist.phtml index 92937300d..ebaa94fc6 100644 --- a/airtime_mvc/application/views/scripts/playlist/_playlist.phtml +++ b/airtime_mvc/application/views/scripts/playlist/_playlist.phtml @@ -27,10 +27,10 @@ if (isset($this->obj)) {
- +
- +
"> diff --git a/airtime_mvc/application/views/scripts/playlist/_smart-block.phtml b/airtime_mvc/application/views/scripts/playlist/_smart-block.phtml index e3e7d37fd..1a31f7784 100644 --- a/airtime_mvc/application/views/scripts/playlist/_smart-block.phtml +++ b/airtime_mvc/application/views/scripts/playlist/_smart-block.phtml @@ -26,17 +26,14 @@ if (isset($this->obj)) {
- +
form; ?>
- +
"> diff --git a/airtime_mvc/application/views/scripts/show-builder/index.phtml b/airtime_mvc/application/views/scripts/show-builder/index.phtml index a34edbce6..3744c2670 100644 --- a/airtime_mvc/application/views/scripts/show-builder/index.phtml +++ b/airtime_mvc/application/views/scripts/show-builder/index.phtml @@ -24,7 +24,7 @@
diff --git a/airtime_mvc/public/css/_showbuilder.css b/airtime_mvc/public/css/_showbuilder.css index 2ba7678b4..ec79cbd49 100644 --- a/airtime_mvc/public/css/_showbuilder.css +++ b/airtime_mvc/public/css/_showbuilder.css @@ -68,7 +68,11 @@ div.ColVis_collectionBackground { .content-pane { position: relative; + + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; box-sizing: border-box; + height: 100%; border: 1px solid #202020; @@ -105,6 +109,7 @@ div.ColVis_collectionBackground { .wrapper { -webkit-flex-flow: column !important; flex-flow: column !important; + padding-top: 10px !important; } .content-pane { height: auto !important; @@ -114,11 +119,15 @@ div.ColVis_collectionBackground { flex: 8 100%; min-height: 50%; } -} - -@media screen and (max-width: 780px) { - .wrapper { - padding: 4px 4px 40px !important; + .usability_hint { + left: 0; + top: auto; + bottom: 0; + width: 100%; + z-index: 100; + } + .spl_sortable { + max-height: 50%; } } @@ -187,15 +196,10 @@ div.ColVis_collectionBackground { top: 2px; } -div.btn > span -{ +div.btn > span { color: red; } -.fg-toolbar.ui-toolbar { - border: none; -} - .fg-toolbar.ui-toolbar.ui-widget-header.ui-corner-bl.ui-corner-br.ui-helper-clearfix { position: absolute; right: 0; @@ -228,6 +232,19 @@ div.btn > span border: none; } +#library_display_wrapper > .dataTables_scrolling, +#show_builder_table_wrapper > .dataTables_scrolling { + border: 1px solid #5b5b5b; +} + +thead th.ui-state-default { + cursor: move; + + border: solid #666; + border-width: 0 0 1px 1px; + color: #ccc; +} + /* Library Context Menu */ .context-menu-item.icon { @@ -315,6 +332,10 @@ div.btn > span float: right; } +#show_builder .fg-toolbar.ui-corner-tl.ui-corner-tr { + border-top: none; +} + .sb-content .dataTables_wrapper { margin: 0; } @@ -349,7 +370,7 @@ div.btn > span } .nav-tabs { - border-bottom: 1px solid #474747; + border-bottom: 1px solid #5b5b5b; font-family: Arial, Helvetica, sans-serif; font-size: 12px; padding: 0; @@ -364,13 +385,12 @@ div.btn > span text-decoration: none; background-color: rgba(71,71,71,.5); - } .nav-tabs :not(.active) a:hover { background-color: rgba(239, 76, 10, .5) !important; border: 1px solid transparent !important; - border-bottom: 1px solid #474747 !important; + border-bottom: 1px solid transparent !important; } .nav-tabs > .active > a, .nav-tabs > .active > a:hover { @@ -431,6 +451,20 @@ textarea { /* Playlist/Block/Webstream Editors */ +dl > dd > input, dl > dd > textarea { + margin: 0; +} + +span.errors.sp-errors { + color: #902d2d; +} + +li.ui-state-default { + border: 1px solid #7e7e7e; +} + +/* Playlist Editor */ + .side_playlist { position: relative; width: 100%; /* Override because we're using flexbox */ @@ -452,9 +486,14 @@ textarea { .side_playlist label, .side_playlist h4, .side_playlist span { color: #efefef; font-size: 14px; + line-height: 24px; font-weight: normal; } +.sb-content fieldset label { + font-size: 14px; +} + .editor_pane_wrapper { display: -webkit-box; display: -moz-box; @@ -471,7 +510,14 @@ textarea { } .inner_editor_wrapper { + max-height: 60%; + overflow-x: hidden; width: 100%; + flex: 1 0 100%; +} + +.clearfix:after, .side_playlist li:after { + display: none !important; } .spl-no-margin { @@ -534,10 +580,45 @@ textarea { outline-width: 0; } +/* Smart Block Editor */ + +.btn-toolbar { + margin: 0; +} + +.sp-button { + margin-left: 0; +} + +.sp-button + .sp-button { + margin: 0 5px 0 0; +} + +.side_playlist .zend_form input { + box-sizing: border-box; + height: 26px; +} + +.smart-block-form input[type='radio'] { + vertical-align: bottom; + margin: 0 5px -2px; +} + +.smart-block-form input[type='checkbox'] { + vertical-align: bottom; + margin: 0 4px; +} + +/* Hacky */ +.smart-block-form + .btn-toolbar, +.smart-block-form + .btn-toolbar + .btn-toolbar { + padding-top: 4px; +} + /* Media type selector */ #media_type_nav { - position: absolute; + position: fixed; top: 139px; bottom: 0; @@ -626,6 +707,10 @@ textarea { padding: 3px 9px; } +.ColVis_title { + color: #000; +} + #show_builder .fg-toolbar > .btn-toolbar { position: absolute; top: 36px; @@ -661,6 +746,10 @@ textarea { text-align: center; } +.datatable tr td { + text-align: left; +} + th.library_checkbox { text-align: center !important; } @@ -673,6 +762,11 @@ th.library_checkbox { position: fixed !important; } +/* Since the z-index gets applied dynamically to the */ +#ui-datepicker-div { + z-index: 1000 !important; +} + .datatable .ui-state-highlight, .spl_sortable .ui-state-highlight { background: rgba(255, 93, 26, .6); border: none; diff --git a/airtime_mvc/public/css/media_library.css b/airtime_mvc/public/css/media_library.css index d06323d45..40f3aa4d7 100644 --- a/airtime_mvc/public/css/media_library.css +++ b/airtime_mvc/public/css/media_library.css @@ -168,7 +168,7 @@ td.library_bitrate { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - height: 25px; + height: 26px; width:60%; } .dataTables_filter label { @@ -186,13 +186,13 @@ td.library_bitrate { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - height: 25px; + height: 26px; } .search-criteria .criteria-element > div .btn-small { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - height: 25px; + height: 26px; padding: 3px 6px; } diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 2dc42905a..fb2496954 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -493,13 +493,17 @@ fieldset { /* END Master Panel */ - .wrapper { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: absolute; top: 141px; - left: 10px; - right: 10px; - padding:10px 0 0 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px; } .alpha-block { @@ -932,7 +936,7 @@ dl.inline-list dd { .DataTables_sort_wrapper .ui-icon { display: block; float: right; - margin: 5px 5px 5px 5px; + margin: 5px 0; } .dataTables_type { float:right; @@ -991,9 +995,10 @@ dl.inline-list dd { .dataTables_info { float: left; - padding: 10px; - font-size:12px; - font-weight:normal; + padding-left: 10px; + font-size: 15px; + font-weight: normal; + line-height: 36px; } .dataTables_paginate { @@ -1019,10 +1024,13 @@ dl.inline-list dd { font-family: Arial,Helvetica,sans-serif; font-size: 12px; height: 25px; - margin: 0; + margin: 0 0 0 4px; padding: 2px 2px 2px 0; vertical-align: top; } +.dataTables_empty { + cursor: auto; +} table.dataTable tbody tr, table.dataTable span.DataTables_sort_icon { cursor: pointer; @@ -1370,10 +1378,14 @@ input[type="checkbox"] { } #schedule_calendar { + box-sizing: border-box; + width: 100%; + margin: 0; } #schedule_block_table { + padding-right: 5px; width: 100%; } @@ -1399,8 +1411,7 @@ div.fc-agenda > div:nth-child(2) { width: 100%; height: 100%; - padding: 0px; - padding-right: 20px !important; + padding: 0 5px 0 0; } .schedule_change_slots /** The time span combobox */ @@ -1409,7 +1420,7 @@ div.fc-agenda > div:nth-child(2) } #schedule_block_table td { - padding: 0px; + padding: 0; vertical-align: top; } @@ -3392,29 +3403,18 @@ dd .stream-status { /* Usability Hints */ -/* - * This is a temporary solution to a larger issue; - * we should revisit this (and really all of the - * absolute positioning in Airtime) when we want - * to focus on responsive design. - */ -.usability_hint:not(.hidden) + .wrapper { - top: 170px; - - -webkit-transition: top 0.2s linear; - -moz-transition: top 0.2s linear; - -o-transition: top 0.2s linear; - transition: top 0.2s linear; -} - .usability_hint { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 5px 10px 5px 10px; border: 1px solid #ff611f; background-color: #ff611f; color: white; font-size: 14px; - position: absolute; + position: fixed; top: 139px; width: 100%; z-index: 1; @@ -3427,3 +3427,9 @@ dd .stream-status { .calendar-context-menu { min-width: 200px !important; } + +/* Add Media Page */ + +#upload_form, #recent_uploads_wrapper { + box-sizing: border-box; +} diff --git a/airtime_mvc/public/js/airtime/common/common.js b/airtime_mvc/public/js/airtime/common/common.js index 8b78de61d..988b87a11 100644 --- a/airtime_mvc/public/js/airtime/common/common.js +++ b/airtime_mvc/public/js/airtime/common/common.js @@ -1,5 +1,6 @@ var previewWidth = 482, - previewHeight = 110; + previewHeight = 110, + USABILITY_HINT_PADDING = 40; $(document).ready(function() { @@ -12,6 +13,9 @@ $(document).ready(function() { //this statement tells the browser to fade out any success message after 5 seconds setTimeout(function(){$(".success").fadeOut("slow", function(){$(this).empty()});}, 5000); + if ($('.usability_hint:visible')) { + $(".wrapper").css("padding-top", USABILITY_HINT_PADDING); // Account for usability hint + } }); /* @@ -162,6 +166,17 @@ function removeSuccessMsg() { $status.fadeOut("slow", function(){$status.empty()}); } +function hideHint(h) { + h.hide("slow").addClass("hidden"); + $(".wrapper").css("padding-top", 10); +} + +function showHint(h) { + console.log("test"); + h.show("slow").removeClass("hidden"); + $(".wrapper").css("padding-top", USABILITY_HINT_PADDING); // Account for usability hint +} + function getUsabilityHint() { var pathname = window.location.pathname; $.getJSON("/api/get-usability-hint", {"format": "json", "userPath": pathname}, function(json) { @@ -169,26 +184,25 @@ function getUsabilityHint() { var current_hint = $hint_div.html(); if (json === "") { // there are no more hints to display to the user - $hint_div.hide("slow").addClass("hidden"); + hideHint($hint_div); } else if (current_hint !== json) { // we only change the message if it is new if ($hint_div.is(":visible")) { - $hint_div.hide("slow").addClass("hidden"); + hideHint($hint_div); } $hint_div.html(json); - $hint_div.show("slow").removeClass("hidden"); + showHint($hint_div); } else { // hint is the same before we hid it so we just need to show it if ($hint_div.is(":hidden")) { - $hint_div.show("slow").removeClass("hidden"); + showHint($hint_div); } } }); } $(document).mouseup(function (e) { - var mb = $("#menu-btn"), - w = $(window).width(); + var mb = $("#menu-btn"), w = $(window).width(); if (!mb.is(e.target) && mb.has(e.target).length === 0 && w <= 970) { $('#nav .responsive-menu').slideUp(); } diff --git a/airtime_mvc/public/js/airtime/library/_library.js b/airtime_mvc/public/js/airtime/library/_library.js index 877749cd1..d6bbd8169 100644 --- a/airtime_mvc/public/js/airtime/library/_library.js +++ b/airtime_mvc/public/js/airtime/library/_library.js @@ -559,16 +559,16 @@ var AIRTIME = (function(AIRTIME) { /* ISRC Number */ { "sTitle" : $.i18n._("ISRC") , "mDataProp" : "isrc_number" , "bVisible" : false , "sClass" : "library_isrc" , "sWidth" : "150px" }, /* Label */ { "sTitle" : $.i18n._("Label") , "mDataProp" : "label" , "bVisible" : false , "sClass" : "library_label" , "sWidth" : "125px" }, /* Language */ { "sTitle" : $.i18n._("Language") , "mDataProp" : "language" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, - /* Last Modified */ { "sTitle" : $.i18n._("Last Modified") , "mDataProp" : "mtime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "125px" }, - /* Last Played */ { "sTitle" : $.i18n._("Last Played") , "mDataProp" : "lptime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "125px" }, + /* Last Modified */ { "sTitle" : $.i18n._("Last Modified") , "mDataProp" : "mtime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155" }, + /* Last Played */ { "sTitle" : $.i18n._("Last Played") , "mDataProp" : "lptime" , "bVisible" : false , "sClass" : "library_modified_time" , "sWidth" : "155px" }, /* Length */ { "sTitle" : $.i18n._("Length") , "mDataProp" : "length" , "sClass" : "library_length" , "sWidth" : "80px" } , /* Mime */ { "sTitle" : $.i18n._("Mime") , "mDataProp" : "mime" , "bVisible" : false , "sClass" : "library_mime" , "sWidth" : "80px" }, /* Mood */ { "sTitle" : $.i18n._("Mood") , "mDataProp" : "mood" , "bVisible" : false , "sClass" : "library_mood" , "sWidth" : "70px" }, /* Owner */ { "sTitle" : $.i18n._("Owner") , "mDataProp" : "owner_id" , "bVisible" : false , "sClass" : "library_language" , "sWidth" : "125px" }, - /* Replay Gain */ { "sTitle" : $.i18n._("Replay Gain") , "mDataProp" : "replay_gain" , "bVisible" : false , "sClass" : "library_replay_gain" , "sWidth" : "80px" }, - /* Sample Rate */ { "sTitle" : $.i18n._("Sample Rate") , "mDataProp" : "sample_rate" , "bVisible" : false , "sClass" : "library_sr" , "sWidth" : "80px" }, - /* Track Number */ { "sTitle" : $.i18n._("Track Number") , "mDataProp" : "track_number" , "bVisible" : false , "sClass" : "library_track" , "sWidth" : "65px" }, - /* Upload Time */ { "sTitle" : $.i18n._("Uploaded") , "mDataProp" : "utime" , "bVisible" : false , "sClass" : "library_upload_time" , "sWidth" : "125px" } , + /* Replay Gain */ { "sTitle" : $.i18n._("Replay Gain") , "mDataProp" : "replay_gain" , "bVisible" : false , "sClass" : "library_replay_gain" , "sWidth" : "125px" }, + /* Sample Rate */ { "sTitle" : $.i18n._("Sample Rate") , "mDataProp" : "sample_rate" , "bVisible" : false , "sClass" : "library_sr" , "sWidth" : "125px" }, + /* Track Number */ { "sTitle" : $.i18n._("Track Number") , "mDataProp" : "track_number" , "bVisible" : false , "sClass" : "library_track" , "sWidth" : "125px" }, + /* Upload Time */ { "sTitle" : $.i18n._("Uploaded") , "mDataProp" : "utime" , "bVisible" : false , "sClass" : "library_upload_time" , "sWidth" : "155px" } , /* Website */ { "sTitle" : $.i18n._("Website") , "mDataProp" : "info_url" , "bVisible" : false , "sClass" : "library_url" , "sWidth" : "150px" }, /* Year */ { "sTitle" : $.i18n._("Year") , "mDataProp" : "year" , "bVisible" : false , "sClass" : "library_year" , "sWidth" : "60px" } ], diff --git a/airtime_mvc/public/js/airtime/library/_spl.js b/airtime_mvc/public/js/airtime/library/_spl.js index a9912145a..28b6888f2 100644 --- a/airtime_mvc/public/js/airtime/library/_spl.js +++ b/airtime_mvc/public/js/airtime/library/_spl.js @@ -586,7 +586,7 @@ var AIRTIME = (function(AIRTIME){ position: { my: "left bottom", at: "right center" - }, + } }) } else { $(value).bind("click", openAudioPreview); @@ -968,7 +968,6 @@ var AIRTIME = (function(AIRTIME){ alert(json.error); } if (json.html !== undefined) { - console.log(json); closeTab(); openPlaylist(json); } @@ -1547,8 +1546,8 @@ var AIRTIME = (function(AIRTIME){ mod.onResize = function() { var h = $(".panel-header .nav").height(); - $(".pl-content").css("margin-top", h + 4); // 8px extra for padding - $("#show_builder_table_wrapper").css("top", h + 4); + $(".pl-content").css("margin-top", h + 5); // 8px extra for padding + $("#show_builder_table_wrapper").css("top", h + 5); }; return AIRTIME; diff --git a/airtime_mvc/public/js/airtime/playlist/_smart_blockbuilder.js b/airtime_mvc/public/js/airtime/playlist/_smart_blockbuilder.js new file mode 100644 index 000000000..a7335fdfd --- /dev/null +++ b/airtime_mvc/public/js/airtime/playlist/_smart_blockbuilder.js @@ -0,0 +1,609 @@ +$(document).ready(function() { + setSmartBlockEvents(); +}); + +function setSmartBlockEvents() { + var activeTab = $('.active-tab'), + form = activeTab.find('.smart-block-form'); + + /********** ADD CRITERIA ROW **********/ + form.find('#criteria_add').live('click', function(){ + + var div = $('dd[id="sp_criteria-element"]').children('div:visible:last'); + + div.find('.db-logic-label').text('and').show(); + div = div.next().show(); + + div.children().removeAttr('disabled'); + div = div.next(); + if (div.length === 0) { + $(this).hide(); + } + + appendAddButton(); + appendModAddButton(); + removeButtonCheck(); + }); + + /********** ADD MODIFIER ROW **********/ + form.find('a[id^="modifier_add"]').live('click', function(){ + var criteria_value = $(this).siblings('select[name^="sp_criteria_field"]').val(); + + + //make new modifier row + var newRow = $(this).parent().clone(), + newRowCrit = newRow.find('select[name^="sp_criteria_field"]'), + newRowMod = newRow.find('select[name^="sp_criteria_modifier"]'), + newRowVal = newRow.find('input[name^="sp_criteria_value"]'), + newRowExtra = newRow.find('input[name^="sp_criteria_extra"]'), + newRowRemove = newRow.find('a[id^="criteria_remove"]'); + + //remove error msg + if (newRow.children().hasClass('errors sp-errors')) { + newRow.find('span[class="errors sp-errors"]').remove(); + } + + //hide the critieria field select box + newRowCrit.addClass('sp-invisible'); + + //keep criteria value the same + newRowCrit.val(criteria_value); + + //reset all other values + newRowMod.val('0'); + newRowVal.val(''); + newRowExtra.val(''); + disableAndHideExtraField(newRowVal); + sizeTextBoxes(newRowVal, 'sp_extra_input_text', 'sp_input_text'); + + //remove the 'criteria add' button from new modifier row + newRow.find('#criteria_add').remove(); + + $(this).parent().after(newRow); + reindexElements(); + appendAddButton(); + appendModAddButton(); + removeButtonCheck(); + }); + + /********** REMOVE ROW **********/ + form.find('a[id^="criteria_remove"]').live('click', function(){ + var curr = $(this).parent(); + var curr_pos = curr.index(); + var list = curr.parent(); + var list_length = list.find("div:visible").length; + var count = list_length - curr_pos; + var next = curr.next(); + var item_to_hide; + var prev; + var index; + + //remove error message from current row, if any + var error_element = curr.find('span[class="errors sp-errors"]'); + if (error_element.is(':visible')) { + error_element.remove(); + } + + /* assign next row to current row for all rows below and including + * the row getting removed + */ + for (var i=0; i 0) { + /* If the criteria field is hidden we know it is a modifier row + * and can hide the previous row's modifier add button + */ + if ($(div).find('select[name^="sp_criteria_field"]').hasClass('sp-invisible')) { + $(div).prev().find('a[id^="modifier_add"]').addClass('sp-invisible'); + } else { + $(div).prev().find('a[id^="modifier_add"]').removeClass('sp-invisible'); + } + } + + //always add modifier add button to the last row + if (i+1 == divs.length) { + $(div).find('a[id^="modifier_add"]').removeClass('sp-invisible'); + } + }); +} + +/* This function re-indexes all the form elements. + * We need to do this everytime a row gets deleted + */ +function reindexElements() { + var divs = $('.active-tab .smart-block-form').find('div select[name^="sp_criteria_field"]').parent(), + index = 0, + modIndex = 0; + /* Hide all logic labels + * We will re-add them as each row gets indexed + */ + $('.db-logic-label').text('').hide(); + + $.each(divs, function(i, div){ + if (i > 0 && index < 26) { + + /* If the current row's criteria field is hidden we know it is + * a modifier row + */ + if ($(div).find('select[name^="sp_criteria_field"]').hasClass('sp-invisible')) { + if ($(div).is(':visible')) { + $(div).prev().find('.db-logic-label').text('or').show(); + } + modIndex++; + } else { + if ($(div).is(':visible')) { + $(div).prev().find('.db-logic-label').text('and').show(); + } + index++; + modIndex = 0; + } + + $(div).find('select[name^="sp_criteria_field"]').attr('name', 'sp_criteria_field_'+index+'_'+modIndex); + $(div).find('select[name^="sp_criteria_field"]').attr('id', 'sp_criteria_field_'+index+'_'+modIndex); + $(div).find('select[name^="sp_criteria_modifier"]').attr('name', 'sp_criteria_modifier_'+index+'_'+modIndex); + $(div).find('select[name^="sp_criteria_modifier"]').attr('id', 'sp_criteria_modifier_'+index+'_'+modIndex); + $(div).find('input[name^="sp_criteria_value"]').attr('name', 'sp_criteria_value_'+index+'_'+modIndex); + $(div).find('input[name^="sp_criteria_value"]').attr('id', 'sp_criteria_value_'+index+'_'+modIndex); + $(div).find('input[name^="sp_criteria_extra"]').attr('name', 'sp_criteria_extra_'+index+'_'+modIndex); + $(div).find('input[name^="sp_criteria_extra"]').attr('id', 'sp_criteria_extra_'+index+'_'+modIndex); + $(div).find('a[name^="modifier_add"]').attr('id', 'modifier_add_'+index); + $(div).find('a[id^="criteria_remove"]').attr('id', 'criteria_remove_'+index+'_'+modIndex); + } else if (i > 0) { + $(div).remove(); + } + }); +} + +function buttonClickAction(clickType, url){ + var data = $('.active-tab .smart-block-form').serializeArray(), + obj_id = $('.active-tab .obj_id').val(); + + enableLoadingIcon(); + $.post(url, {format: "json", data: data, obj_id: obj_id}, function(data){ + callback(data, clickType); + disableLoadingIcon(); + }); +} + +function setupUI() { + var activeTab = $('.active-tab'), + playlist_type = activeTab.find('input:radio[name=sp_type]:checked').val(); + + /* Activate or Deactivate shuffle button + * It is only active if playlist is not empty + */ + var sortable = activeTab.find('.spl_sortable'), + plContents = sortable.children(), + shuffleButton = activeTab.find('.sp-button, #pl-bl-clear-content'); + + if (!plContents.hasClass('spl_empty')) { + if (shuffleButton.hasClass('ui-state-disabled')) { + shuffleButton.removeClass('ui-state-disabled'); + shuffleButton.removeAttr('disabled'); + } + } else if (!shuffleButton.hasClass('ui-state-disabled')) { + shuffleButton.addClass('ui-state-disabled'); + shuffleButton.attr('disabled', 'disabled'); + } + + if (activeTab.find('.obj_type').val() == 'block') { + if (playlist_type == "0") { + shuffleButton.parent().show(); + sortable.show(); + } else { + shuffleButton.parent().hide(); + sortable.hide(); + } + } + + $(".playlist_type_help_icon").qtip({ + content: { + text: $.i18n._("A static smart block will save the criteria and generate the block content immediately. This allows you to edit and view it in the Library before adding it to a show.")+"

" + + $.i18n._("A dynamic smart block will only save the criteria. The block content will get generated upon adding it to a show. You will not be able to view and edit the content in the Library.") + }, + hide: { + delay: 500, + fixed: true + }, + style: { + border: { + width: 0, + radius: 4 + }, + classes: "ui-tooltip-dark ui-tooltip-rounded" + }, + position: { + my: "left bottom", + at: "right center" + } + }); + + $(".repeat_tracks_help_icon").qtip({ + content: { + text: sprintf($.i18n._("The desired block length will not be reached if %s cannot find enough unique tracks to match your criteria. Enable this option if you wish to allow tracks to be added multiple times to the smart block."), PRODUCT_NAME) + }, + hide: { + delay: 500, + fixed: true + }, + style: { + border: { + width: 0, + radius: 4 + }, + classes: "ui-tooltip-dark ui-tooltip-rounded" + }, + position: { + my: "left bottom", + at: "right center" + } + }); +} + +function enableAndShowExtraField(valEle, index) { + var spanExtra = valEle.nextAll("#extra_criteria"); + spanExtra.children('#sp_criteria_extra_'+index).removeAttr("disabled"); + spanExtra.show(); + + //make value input smaller since we have extra element now + var criteria_val = $('#sp_criteria_value_'+index); + sizeTextBoxes(criteria_val, 'sp_input_text', 'sp_extra_input_text'); +} + +function disableAndHideExtraField(valEle, index) { + var spanExtra = valEle.nextAll("#extra_criteria"); + spanExtra.children('#sp_criteria_extra_'+index).val("").attr("disabled", "disabled"); + spanExtra.hide(); + + //make value input larger since we don't have extra field now + var criteria_value = $('#sp_criteria_value_'+index); + sizeTextBoxes(criteria_value, 'sp_extra_input_text', 'sp_input_text'); +} + +function sizeTextBoxes(ele, classToRemove, classToAdd) { + if (ele.hasClass(classToRemove)) { + ele.removeClass(classToRemove).addClass(classToAdd); + } +} + +function populateModifierSelect(e, popAllMods) { + var criteria_type = getCriteriaOptionType(e), + index = getRowIndex($(e).parent()), + divs; + + if (popAllMods) { + index = index.substring(0, 1); + } + divs = $(e).parents().find('select[id^="sp_criteria_modifier_'+index+'"]'); + + $.each(divs, function(i, div){ + $(div).children().remove(); + + if (criteria_type == 's') { + $.each(stringCriteriaOptions, function(key, value){ + $(div).append($('') + .attr('value', key) + .text(value)); + }); + } else { + $.each(numericCriteriaOptions, function(key, value){ + $(div).append($('') + .attr('value', key) + .text(value)); + }); + } + }); +} + +function getCriteriaOptionType(e) { + var criteria = $(e).val(); + return criteriaTypes[criteria]; +} + +function callback(json, type) { + var dt = $('table[id="library_display"]').dataTable(), + form = $('.active-tab .smart-block-form'); + + if (type == 'shuffle' || type == 'generate') { + if (json.error !== undefined) { + alert(json.error); + } + AIRTIME.playlist.closeTab(); + AIRTIME.playlist.fnOpenPlaylist(json); + if (json.result == "0") { + if (type == 'shuffle') { + form.find('.success').text($.i18n._('Smart block shuffled')); + } else if (type == 'generate') { + form.find('.success').text($.i18n._('Smart block generated and criteria saved')); + //redraw library table so the length gets updated + dt.fnStandingRedraw(); + } + form.find('.success').show(); + } + form.find('#smart_block_options').removeClass("closed"); + } else { + AIRTIME.playlist.closeTab(); + AIRTIME.playlist.fnOpenPlaylist(json); + if (json.result == "0") { + $('.active-tab #sp-success-saved').text($.i18n._('Smart block saved')).show(); + + //redraw library table so the length gets updated + dt.fnStandingRedraw(); + } + form.find('#smart_block_options').removeClass("closed"); + } + setTimeout(removeSuccessMsg, 5000); +} + +function appendAddButton() { + var add_button = "" + + ""; + var rows = $('.active-tab #smart_block_options'), + enabled = rows.find('select[name^="sp_criteria_field"]:enabled'); + + rows.find('#criteria_add').remove(); + + if (enabled.length > 1) { + rows.find('select[name^="sp_criteria_field"]:enabled:last') + .siblings('a[id^="criteria_remove"]') + .after(add_button); + } else { + enabled.siblings('span[id="extra_criteria"]') + .after(add_button); + } +} + +function removeButtonCheck() { + var rows = $('.active-tab dd[id="sp_criteria-element"]').children('div'), + enabled = rows.find('select[name^="sp_criteria_field"]:enabled'), + rmv_button = enabled.siblings('a[id^="criteria_remove"]'); + if (enabled.length == 1) { + rmv_button.attr('disabled', 'disabled'); + rmv_button.hide(); + } else { + rmv_button.removeAttr('disabled'); + rmv_button.show(); + } +} + +function enableLoadingIcon() { + $(".side_playlist.active-tab").block({ + message: $.i18n._("Processing..."), + theme: true, + allowBodyStretch: true, + applyPlatformOpacityRules: false + }); +} + +function disableLoadingIcon() { + $(".side_playlist.active-tab").unblock() +} +// We need to know if the criteria value will be a string +// or numeric value in order to populate the modifier +// select list +var criteriaTypes = { + 0 : "", + "album_title" : "s", + "bit_rate" : "n", + "bpm" : "n", + "composer" : "s", + "conductor" : "s", + "copyright" : "s", + "cuein" : "n", + "cueout" : "n", + "artist_name" : "s", + "encoded_by" : "s", + "utime" : "n", + "mtime" : "n", + "lptime" : "n", + "genre" : "s", + "isrc_number" : "s", + "label" : "s", + "language" : "s", + "length" : "n", + "mime" : "s", + "mood" : "s", + "owner_id" : "s", + "replay_gain" : "n", + "sample_rate" : "n", + "track_title" : "s", + "track_number" : "n", + "info_url" : "s", + "year" : "n" +}; + +var stringCriteriaOptions = { + "0" : $.i18n._("Select modifier"), + "contains" : $.i18n._("contains"), + "does not contain" : $.i18n._("does not contain"), + "is" : $.i18n._("is"), + "is not" : $.i18n._("is not"), + "starts with" : $.i18n._("starts with"), + "ends with" : $.i18n._("ends with") +}; + +var numericCriteriaOptions = { + "0" : $.i18n._("Select modifier"), + "is" : $.i18n._("is"), + "is not" : $.i18n._("is not"), + "is greater than" : $.i18n._("is greater than"), + "is less than" : $.i18n._("is less than"), + "is in the range" : $.i18n._("is in the range") +}; diff --git a/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js b/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js index f0e0399cd..6a654b9fd 100644 --- a/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js +++ b/airtime_mvc/public/js/airtime/playlist/smart_blockbuilder.js @@ -331,7 +331,7 @@ function reindexElements() { function buttonClickAction(clickType, url){ var data = $('#smart-block-form').serializeArray(), - obj_id = $('input[id="obj_id"]').val(); + obj_id = $('.obj_id').val(); enableLoadingIcon(); $.post(url, {format: "json", data: data, obj_id: obj_id}, function(data){ diff --git a/airtime_mvc/public/js/airtime/showbuilder/_builder.js b/airtime_mvc/public/js/airtime/showbuilder/_builder.js index 11889c5a4..51310e616 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/_builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/_builder.js @@ -584,7 +584,7 @@ var AIRTIME = (function(AIRTIME){ $node.empty(); } - sSeparatorHTML = ''+$.i18n._("Show Empty")+''; + sSeparatorHTML = ''+$.i18n._("Drag tracks here from the library")+''; cl = cl + " sb-empty odd"; fnPrepareSeparatorRow(sSeparatorHTML, cl, 1); @@ -773,12 +773,12 @@ var AIRTIME = (function(AIRTIME){ "sAjaxDataProp": "schedule", "oLanguage": datatables_dict, - "sAjaxSource": baseUrl+"showbuilder/builder-feed" , + "sAjaxSource": baseUrl+"showbuilder/builder-feed", "bScrollCollapseY": false }); - $sbTable.find("tbody").on("mousedown", "tr:not(.sb-past, .sb-empty)", function(ev) { + $sbTable.find("tbody").on("mousedown", "tr:not(.sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty))", function(ev) { var $tr = $(this), // Get the ID of the selected row $rowId = $tr.attr("id"); @@ -814,7 +814,7 @@ var AIRTIME = (function(AIRTIME){ $previouslySelected = $tr; }); - $sbTable.find("tbody").on("click", "tr:not(.sb-past, .sb-empty)", function(ev) { + $sbTable.find("tbody").on("click", "tr:not(.sb-header, .sb-footer, .sb-past, .sb-empty, :has(td.dataTables_empty))", function(ev) { if (flagForDeselection) { flagForDeselection = false; $(this).removeClass(SB_SELECTED_CLASS); diff --git a/airtime_mvc/public/js/airtime/showbuilder/_main_builder.js b/airtime_mvc/public/js/airtime/showbuilder/_main_builder.js index 65b4cf323..768b24d55 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/_main_builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/_main_builder.js @@ -165,8 +165,10 @@ AIRTIME = (function(AIRTIME) { $builder = $("#show_builder"); $fs = $builder.find('fieldset'); - $("#timeline-tab").on("click", function() { - AIRTIME.showbuilder.switchTab($("#show_builder .outer-datatable-wrapper"), $(this)); + $("#schedule-tab").on("click", function() { + if (!$(this).hasClass('active')) { + AIRTIME.showbuilder.switchTab($("#show_builder .outer-datatable-wrapper"), $(this)); + } }); /*