Ticket #9939: meta-web4.diff
File meta-web4.diff, 36.3 KB (added by , 14 years ago) |
---|
-
new file js/dialog/dialog.css
diff --git a/js/dialog/dialog.css b/js/dialog/dialog.css new file mode 100644 index 0000000..7b73405
- + 1 /** 2 * Dialog 3 * 4 * @author Roland Franssen <franssen.roland@gmail.com> 5 * @license MIT 6 * @version 2.1 7 **/ 8 9 /* DONT CHANGE */ 10 * html .fixed { position:absolute } 11 .fixed { position:fixed } 12 13 /* DIALOG CORE */ 14 #dialog-overlay { top:0;left:0;width:100%;height:100%;z-index:900 } 15 #dialog-container { overflow:hidden;z-index:901;text-align:left } 16 17 /* DIALOG TOP */ 18 #dialog-top{background:#999;border:1px solid #fff;padding:5px;font-weight:bold} 19 #dialog-title{color:#333} 20 #dialog-close{color:#fff;padding-left:5px} 21 #dialog-close:hover{color:#ccc} 22 23 /* DIALOG BOTTOM */ 24 #dialog-bottom{background:#eee;border-top:1px solid #ccc;color:#666;padding:5px;text-align:center;font-size:12px} 25 #dialog-bottom .next, 26 #dialog-bottom .prev{color:#ccc;font-weight:bold;color:#333} 27 #dialog-bottom .next:hover, 28 #dialog-bottom .prev:hover{color:#f90} 29 #dialog-bottom .next{padding-left:10px} 30 #dialog-bottom .prev{padding-right:10px} 31 #dialog-bottom .curr{} 32 33 /* DIALOG MISC */ 34 #dialog-loading{color:#ccc;font-weight:bold;text-align:center;padding:20px} 35 36 /* DIALOG PREDEFINED */ 37 #dialog-container .alert, 38 #dialog-container .confirm { text-align:center;color:#999 } 39 #dialog-container .alert input, 40 #dialog-container .confirm input { font-weight:bold;width:75px } 41 42 /* DIALOG PERSONAL */ 43 #dialog-container .myFirstDialog { color:orange;font-size:20px } -
new file js/dialog/dialog.js
diff --git a/js/dialog/dialog.js b/js/dialog/dialog.js new file mode 100644 index 0000000..3b227ff
- + 1 /** 2 * Dialog 3 * 4 * @author Roland Franssen <franssen.roland@gmail.com> 5 * @license MIT 6 * @version 2.1 7 **/ 8 9 var Dialogs = { 10 Lang:{ 11 close: ' × ', 12 prev: '« Previous', 13 next: 'Next »', 14 loading: 'Loading...', 15 ok: 'OK', 16 yes: 'Yes', 17 no: 'No' 18 }, 19 Default:{ 20 handle: null, // css rule | element | null 21 autoOpen: false, // true | false 22 background: ['#000', '#fff'], // array 23 width: 'auto', // auto | max | integer 24 height: 'auto', // auto | max | integer 25 minWidth: null, // null | pixel value 26 minHeight: null, // null | pixel value 27 innerScroll: true, // true | false 28 opacity: .75, // float | false 29 margin: 10, // integer 30 padding: 10, // integer 31 title: null, // string | null 32 className: null, // string | null 33 content: null, // string | element | array | object | function 34 iframe: null, // string | null 35 target:{ 36 id: null, // string | null 37 auto: true // true | false 38 }, 39 ajax:{ 40 url: null, // string | null 41 jsonTemplate: null, // interpolation template string | null 42 options: {} // default ajax options 43 }, 44 close:{ 45 link: true, // true | false 46 esc: true, // true | false 47 overlay: true // true | false 48 }, 49 afterOpen: Prototype.emptyFunction, // function 50 afterClose: Prototype.emptyFunction, // function 51 afterClick: Prototype.emptyFunction, // function 52 afterIframeLoad:Prototype.emptyFunction // function 53 }, 54 Browser:{ 55 IE6:(Prototype.Browser.IE && parseInt(navigator.appVersion) == 4 && navigator.userAgent.toLowerCase().indexOf('msie 6.') != -1) 56 } 57 }; 58 59 Object.extend(Dialogs, { 60 _exec:false, 61 _open:false, 62 _elements:{ 63 overlay:['div', 'dialog-overlay', 'fixed'], 64 container:['div', 'dialog-container', 'fixed'], 65 content:['div', 'dialog-content'], 66 loading:['div', 'dialog-loading'], 67 top:['div', 'dialog-top'], 68 bottom:['div', 'dialog-bottom'], 69 title:['span', 'dialog-title'], 70 close:['a', 'dialog-close'], 71 next:['a', null, 'next'], 72 prev:['a', null, 'prev'], 73 curr:['span', null, 'curr'] 74 }, 75 fix:{ 76 scroll:Dialogs.Browser.IE6, 77 select:Dialogs.Browser.IE6 78 }, 79 view:function(){ 80 var view = document.viewport, 81 dim = view.getDimensions(), 82 data = {width:dim.width, height:dim.height}; 83 if(Dialogs.fix.scroll){ 84 var scroll = view.getScrollOffsets(); 85 data.top = scroll.top; 86 data.left = scroll.left; 87 } 88 return data; 89 }, 90 elm:function(elm){ 91 return Dialogs._elements[elm]; 92 }, 93 load:function(domready){ 94 if(!!Dialogs._exec) return; 95 Dialogs._exec = true; 96 var e = Dialogs._elements; 97 for(var x in e){ 98 var d = e[x], 99 a = {style:'display:none'}; 100 if(d[1]) a['id'] = d[1]; 101 if(d[2]) a['className'] = d[2]; 102 switch(d[0]){ 103 case 'a': a['href'] = 'javascript:;'; break; 104 } 105 var el = new Element(d[0], a); 106 if(Dialogs.Lang[x]) el.update(Dialogs.Lang[x]); 107 Dialogs._elements[x] = el; 108 } 109 var load = function(){ 110 var e = Dialogs._elements; 111 $(document.body) 112 .insert(e['overlay']) 113 .insert(e['container'] 114 .insert(e['top'] 115 .insert(e['title']) 116 .insert(e['close']) 117 ) 118 .insert(e['content']) 119 .insert(e['bottom'] 120 .insert(e['prev']) 121 .insert(e['curr']) 122 .insert(e['next']) 123 ) 124 ); 125 if(Dialogs.Browser.IE6) e['top'].insert(new Element('div', {style:'clear:both'})); 126 }; 127 if(!!domready) document.observe('dom:loaded', load); 128 else load.call(); 129 }, 130 close:function(){ 131 [Dialogs.elm('title'), Dialogs.elm('content'), Dialogs.elm('curr')].invoke('update', ''); 132 for(var x in Dialogs._elements) Dialogs._elements[x].writeAttribute('style', 'display:none'); 133 Dialogs.elm('container').setStyle('left:-99999px;top:-99999px'); 134 if(Dialogs.fix.select) 135 $$('select.dialog-hideselect').invoke('show').invoke('removeClassName', 'dialog-hideselect'); 136 Dialogs._open = false; 137 }, 138 alert:function(s){ 139 var o = new Element('input', {value:Dialogs.Lang.ok, type:'button'}), 140 a = new Dialog({ 141 className:'alert', 142 close:{link:false, esc:true}, 143 padding:20, 144 content:function(){ 145 o.observe('click', Dialogs.close); 146 return [s, '<br /><br />', o]; 147 }, 148 afterOpen:function(){ 149 o.focus(); 150 } 151 }); 152 a.open(); 153 }, 154 confirm:function(s, y_call, n_call){ 155 var y = new Element('input', {value:Dialogs.Lang.yes, type:'button'}), 156 n = new Element('input', {value:Dialogs.Lang.no, type:'button'}), 157 c = new Dialog({ 158 className:'confirm', 159 close:{link:false}, 160 padding:20, 161 content:function(){ 162 y.observe('click', function(){ 163 if(Object.isFunction(y_call)) y_call(); 164 else Dialogs.close(); 165 }); 166 n.observe('click', function(){ 167 if(Object.isFunction(n_call)) n_call(); 168 else Dialogs.close(); 169 }); 170 return [s, '<br /><br />', y, n]; 171 }, 172 afterOpen:function(){ 173 y.focus(); 174 } 175 }); 176 c.open(); 177 } 178 }); 179 var Dialog = Class.create(); 180 Dialog.prototype = { 181 initialize:function(opt){ 182 this.opt = Object.extend(Object.clone(Dialogs.Default), opt || {}); 183 var c = this.opt.content; 184 if(Object.isFunction(c)) 185 Object.extend(this.opt, {content:c()}); 186 c = this.opt.content; 187 if(Object.isString(this.opt.target.id) || Object.isElement(this.opt.target.id)){ 188 var b = $(this.opt.target.id); 189 Object.extend(this.opt, {content:b.innerHTML}); 190 if(this.opt.target.auto){ 191 var a = /#(.+)$/.exec(window.location); 192 if(Object.isArray(a) && Object.isString(a[1])){ 193 a = a[1].split(',').last(); 194 if(a == b.identify()) this.open.bind(this).delay(1); 195 } 196 } 197 }else if(Object.isHash(c)) 198 this.steps = { 199 i:0, 200 k:c.keys(), 201 v:c.values(), 202 m:c.size() 203 }; 204 this.attachEvents(); 205 if(this.opt.autoOpen) this.open(); 206 }, 207 exec:function(bool){ 208 return Dialogs._open == this._open && Dialogs.elm('overlay').visible() && bool; 209 }, 210 attachEvents:function(){ 211 Event.observe(window, 'resize', this.setDimensions.bindAsEventListener(this)); 212 if(Dialogs.fix.scroll) 213 Event.observe(window, 'scroll', this.setScroll.bindAsEventListener(this)); 214 var handles = []; 215 if(Object.isElement(this.opt.handle)) handles.push($(this.opt.handle)); 216 else if(Object.isArray(this.opt.handle)) this.opt.handle.each(function(handle){ handles.push($(handle)); }); 217 else if(Object.isString(this.opt.handle)) handles = $$(this.opt.handle); 218 handles.invoke('show').invoke('observe', 'click', function(e){ 219 e.stop(); 220 if(Object.isFunction(this.opt.afterClick)) this.opt.afterClick(e); 221 this.open(); 222 }.bindAsEventListener(this)); 223 Dialogs.elm('close').observe('click', function(){ 224 if(this.exec(this.opt.close.link)) this.close(); 225 }.bindAsEventListener(this)); 226 Dialogs.elm('overlay').observe('click', function(){ 227 if(this.exec(this.opt.close.overlay)) this.close(); 228 }.bindAsEventListener(this)); 229 document.observe('keyup', function(e){ 230 if(this.exec(this.opt.close.esc && (e.which || e.keyCode) == Event.KEY_ESC)) this.close(); 231 }.bindAsEventListener(this)); 232 if(this.steps){ 233 [Dialogs.elm('prev'), Dialogs.elm('next')].invoke('observe', 'click', this.setSteps.bindAsEventListener(this)); 234 document.observe('keydown', function(e){ 235 var c = e.which || e.keyCode; 236 if(this.exec((c == Event.KEY_LEFT) || (c == Event.KEY_RIGHT))) this.setSteps(e); 237 }.bindAsEventListener(this)); 238 } 239 }, 240 setAuto:function(){ 241 this.auto = {max:0}; 242 var t = Dialogs.elm('title'), c = Dialogs.elm('close'); 243 [t,c].invoke('setStyle', 'float:none'); 244 $w('top content bottom').each(function(b){ 245 var e = Dialogs.elm(b); 246 if(!e.visible()) this.auto[b] = {width:0,height:0}; 247 else{ 248 e.writeAttribute('style', 'display:inline;float:left;overflow:visible;white-space:nowrap'); 249 this.auto[b] = e.getDimensions(); 250 e.writeAttribute('style', 'overflow:hidden'); 251 if(b == 'content') this.auto[b].width += (parseInt(this.opt.padding) || 0) * 2; 252 if(this.auto[b].width > this.auto.max) this.auto.max = this.auto[b].width; 253 } 254 }.bind(this)); 255 t.setStyle('float:left'); 256 c.setStyle('float:right'); 257 }, 258 setDimensions:function(){ 259 if(!this.exec(true)) return; 260 this.setAuto(); 261 var a = this.auto, 262 d = Dialogs.view(), 263 t = Dialogs.elm('content'), 264 c = Dialogs.elm('container'), 265 o = { 266 m:((parseInt(this.opt.margin) || 0) * 2), 267 p:((parseInt(this.opt.padding) || 0) * 2), 268 t:a.top.height, 269 b:a.bottom.height 270 }, 271 m = {width:(d.width-o.m), height:(d.height-o.m-o.t-o.b)}, 272 h = this.opt.height, 273 w = this.opt.width, 274 x = y = false; 275 if(Object.isNumber(w)) w += o.p; 276 if(w == 'max') w = m.width; 277 if(!Object.isNumber(w)) w = a.max; 278 if(w < (this.opt.minWidth || 0)) w = this.opt.minWidth || 0; 279 if(w > m.width){ w = m.width; x = true } 280 t.setStyle('width:'+(w-o.p)+'px;height:auto'); 281 if(Object.isNumber(h)) h += o.p; 282 if(h == 'max') h = m.height; 283 if(!Object.isNumber(h)) h = t.getHeight()+o.p; 284 if(h < (this.opt.minHeight || 0)) w = this.opt.minHeight || 0; 285 if(h > m.height){ h = m.height; y = true; } 286 t.setStyle('height:'+(h-o.p)+'px;padding:'+(o.p/2)+'px'); 287 // dh: commented out to seperate overflow-x and overflow-y 288 // if(this.opt.innerScroll && (x || y)) t.setStyle('overflow:scroll'); 289 if(this.opt.innerScroll && x) t.setStyle('overflow-x:scroll'); 290 if(this.opt.innerScroll && y) t.setStyle('overflow-y:scroll'); 291 var s = {w:w,h:(h+o.t+o.b)}; 292 c.setStyle('width:'+s.w+'px;height:'+s.h+'px;top:50%;left:50%;margin:-'+parseInt(s.h/2)+'px 0 0 -'+parseInt(s.w/2)+'px'); 293 if(Dialogs.fix.scroll){ 294 Dialogs.elm('overlay').setStyle('width:'+d.width+'px;height:'+d.height+'px'); 295 this.setScroll(); 296 } 297 }, 298 setScroll:function(){ 299 if(!this.exec(true)) return; 300 var v = Dialogs.view(), 301 c = Dialogs.elm('container'), 302 d = c.getDimensions(), 303 t = v.top + parseInt((v.height - d.height) / 2), 304 l = v.left + parseInt((v.width - d.width) / 2); 305 c.setStyle('margin:0;top:'+t+'px;left:'+l+'px'); 306 Dialogs.elm('overlay').setStyle('margin:'+v.top+'px 0 0 '+v.left+'px'); 307 }, 308 setLoad:function(){ 309 var l = Dialogs.elm('loading').show(), 310 t = Dialogs.elm('content'), 311 b = t.down('#'+l.identify()); 312 if(!Object.isElement(b)) t.insert(l); 313 }, 314 setAjax:function(){ 315 this.setLoad(); 316 var o = this.opt.ajax.options || {}, 317 c = (o.onComplete && Object.isFunction(o.onComplete) ? o.onComplete : null), 318 a = function(t){ 319 var tpl = this.opt.ajax.jsonTemplate; 320 if(t.responseJSON && Object.isString(tpl)) Dialogs.elm('content').update(tpl.interpolate(t.responseJSON)); 321 else Dialogs.elm('content').update(t.responseText || ''); 322 this.setImages(); 323 this.setDimensions(); 324 if(Object.isFunction(c)) c(t); 325 }.bind(this); 326 Object.extend(o, {onComplete:a}); 327 new Ajax.Request(this.opt.ajax.url, o); 328 }, 329 setIframe:function(){ 330 this.setLoad(); 331 var f = new Element('iframe', {src:this.opt.iframe, frameborder:0, id:'dialog-iframe'}); 332 Dialogs.elm('content').insert(f); 333 f.observe('load', function(){ 334 Dialogs.elm('loading').hide(); 335 f.setStyle('width:100%;height:100%'); 336 this.setDimensions(); 337 if(Object.isFunction(this.opt.afterIframeLoad)) this.opt.afterIframeLoad(); 338 }.bindAsEventListener(this)); 339 }, 340 setSteps:function(ev){ 341 if(!this.exec(true)) return; 342 var m = this.steps.m, 343 s = false, 344 n = Dialogs.elm('next'), 345 p = Dialogs.elm('prev'); 346 if((ev.which || ev.keyCode) == Event.KEY_RIGHT || ev.element().hasClassName('next')){ 347 if(this.steps.i < (m - 1)) s = true; 348 if(s) ++this.steps.i; 349 if(((this.steps.i + 1) >= m) && n.visible()) n.hide(); 350 if(((this.steps.i - 1) >= 0) && !p.visible()) p.show(); 351 }else{ 352 if(this.steps.i > 0) s = true; 353 if(s) --this.steps.i; 354 if(((this.steps.i - 1) < 0) && p.visible()) p.hide(); 355 if(((this.steps.i + 1) <= m) && !n.visible()) n.show(); 356 } 357 if(s) this.setContent(); 358 }, 359 setContent:function(){ 360 var c = this.opt.content, 361 t = Dialogs.elm('content'); 362 t.update(''); 363 if(Object.isString(c) || Object.isElement(c)) t.insert(c); 364 else if(Object.isArray(c)) c.each(function(b){ t.insert(b); }); 365 else if(Object.isHash(c)){ 366 var b = Dialogs.elm('bottom'); 367 t.update('').insert(this.steps.v[this.steps.i]); 368 Dialogs.elm('curr').update(this.steps.k[this.steps.i]); 369 if(!b.visible()) b.show().childElements().invoke('show'); 370 if(this.steps.i <= 0) Dialogs.elm('prev').hide(); 371 if(this.steps.i >= (this.steps.m - 1)) Dialogs.elm('next').hide(); 372 }else if(Object.isString(this.opt.ajax.url)) this.setAjax(); 373 else if(Object.isString(this.opt.iframe)) this.setIframe(); 374 this.setImages(); 375 this.setDimensions.bind(this).defer(); 376 }, 377 setImages:function(){ 378 Dialogs.elm('content').select('img').each(function(el){ 379 el.onload = function(){ 380 this.setDimensions(); 381 }.bind(this); 382 }.bind(this)); 383 }, 384 open:function(){ 385 if(Dialogs.fix.select) 386 $$('select').select(function(el){ return el.visible(); }).invoke('hide').invoke('addClassName', 'dialog-hideselect'); 387 if(Object.isString(this.opt.title) || this.opt.close.link){ 388 if(Object.isString(this.opt.title)) Dialogs.elm('title').show().update(this.opt.title); 389 if(this.opt.close.link) Dialogs.elm('close').show(); 390 else Dialogs.elm('close').hide(); 391 Dialogs.elm('top').show(); 392 }else Dialogs.elm('top').hide(); 393 var o = Dialogs.elm('overlay'), c = Dialogs.elm('container'), t = Dialogs.elm('content'); 394 [o, c, t].invoke('show'); 395 // dh: commented out background so it can be set via class 396 // o.setOpacity(this.opt.opacity || 1).setStyle({background:this.opt.background[0] || '#000'}); 397 o.setOpacity(this.opt.opacity || 1); 398 // dh: commented out background so it can be set via class 399 // c.writeAttribute('style', 'left:-99999px;top:-99999px;background:'+(this.opt.background[1] || '#fff')); 400 c.writeAttribute('style', 'left:-99999px;top:-99999px;'); 401 t.writeAttribute('class', this.opt.className || ''); 402 Dialogs._open = new Date().getTime(); 403 this._open = Dialogs._open; 404 this.setContent(); 405 if(Object.isFunction(this.opt.afterOpen)) this.opt.afterOpen(); 406 }, 407 close:function(){ 408 Dialogs.close(); 409 if(Object.isFunction(this.opt.afterClose)) this.opt.afterClose(); 410 } 411 }; -
modules/_shared/tmpl/default/header.php
diff --git a/modules/_shared/tmpl/default/header.php b/modules/_shared/tmpl/default/header.php index 691d059..2d16bc0 100644
a b EOF; 70 70 </script> 71 71 72 72 <link rel="stylesheet" type="text/css" href="js/prototip/prototip.css"> 73 <link rel="stylesheet" type="text/css" href="js/dialog/dialog.css"> 73 74 <link rel="stylesheet" type="text/css" href="<?php echo skin_url ?>/style.css"> 74 75 <link rel="stylesheet" type="text/css" href="<?php echo skin_url ?>/header.css"> 75 76 <link rel="stylesheet" type="text/css" href="<?php echo skin_url ?>/menus.css"> … … EOF; 84 85 85 86 <script type="text/javascript" src="js/prototype.js"></script> 86 87 <script type="text/javascript" src="js/prototip/prototip.js"></script> 88 <script type="text/javascript" src="js/dialog/dialog.js"></script> 87 89 88 90 <script type="text/javascript" src="js/utils.js"></script> 89 91 <script type="text/javascript" src="js/AC_OETags.js"></script> -
new file modules/tv/lookup_metadata.php
diff --git a/modules/tv/lookup_metadata.php b/modules/tv/lookup_metadata.php new file mode 100644 index 0000000..ca9cfbf
- + 1 <?php 2 /** 3 * Does a query against the backend to look up metadata for a show 4 * returns the result as JSON 5 * 6 * @license GPL 7 * 8 * @package MythWeb 9 * @subpackage TV 10 * 11 /**/ 12 13 header('Content-Type: application/json'); 14 15 $url = "Video/LookupVideo"; 16 $args = array( 17 'Title' => $_REQUEST['title'], 18 'Subtitle' => $_REQUEST['subtitle'], 19 'Inetref' => $_REQUEST['inetref'], 20 'Season' => $_REQUEST['season'], 21 'Episode' => $_REQUEST['episode'], 22 'GrabberType' => $_REQUEST['grabbertype']); 23 24 echo MythBackend::find()->httpRequestAsJson($url, $args); -
modules/tv/tmpl/default/_advanced_options.php
diff --git a/modules/tv/tmpl/default/_advanced_options.php b/modules/tv/tmpl/default/_advanced_options.php index 421e9ae..5430685 100644
a b 24 24 ); 25 25 } 26 26 27 // Tries to populate the inetref, season and episode fields 28 // by doing a metadata lookup against the backend. If multiple 29 // results are return displays a dialog to let the user choose 30 // the appropriate show 31 function lookupMetadata(success, failure) { 32 ajax_add_request(); 33 34 new Ajax.Request('<?php echo root_url ?>tv/lookup_metadata', 35 { 36 parameters: { 37 'title' : "<?php echo $schedule->title ?>", 38 'subtitle' : "<?php echo $schedule->subtitle ?>", 39 'inetref' : $("inetref").value, 40 'season' : $("season").value, 41 'episode' : $("episode").value 42 }, 43 asynchronous: true, 44 method: 'get', 45 onSuccess: success, 46 onFailure: failure 47 } 48 ); 49 } 50 51 // callback for when metadata is returned for this show 52 function onMetadata(transport) { 53 ajax_remove_request(); 54 55 // make sure we got valid date 56 if (!transport || !transport.responseJSON || !transport.responseJSON.VideoLookupList) { 57 messageDialog("<?php echo t("Metadata Lookup Error")?>", 58 "<?php echo t("Server returned invalid data when attempting to retrieve metadata.")?>"); 59 } 60 61 var list = transport.responseJSON.VideoLookupList; 62 63 // display an error if there's no data 64 if (list.Count == 0) { 65 messageDialog("<?php echo t("Metadata Lookup")?>", "<?php echo t("No metadata results found.")?>"); 66 67 // populate the data immediately if there is one result 68 } else if (list.Count == 1) { 69 updateMetadata(list.VideoLookups[0]); 70 71 // if we can pick the right item from the list then use it 72 // otherwise display a dialog for the user to choose which result 73 } else { 74 var item = guessItem(list); 75 if (item) { 76 updateMetadata(item); 77 } else { 78 multipleResultDialog(list); 79 } 80 } 81 } 82 83 // tries to find the correct item in list based off of the TMSref 84 // if we can find it, cool, if not return null 85 function guessItem(list) { 86 var tmsRef = "<?php echo $schedule->seriesid ?>"; 87 for (var i=0; i < list.VideoLookups.length; i++) { 88 var item = list.VideoLookups[i]; 89 if (tmsRef && item.TMSRef == tmsRef) { 90 return item; 91 } 92 } 93 return null; 94 } 95 96 // updates the inetref, season & episode values on the page 97 // optionally creates or updates a "metdata home page" link 98 // in the "More" section of the page 99 function updateMetadata(item) { 100 $("inetref").value = item.InetRef; 101 $("season").value = item.Season; 102 $("episode").value = item.Episode; 103 104 // if the item has a real HomePage then update it 105 if (!!item.HomePage) { 106 updateHomePage(item); 107 108 // otherwise do a lookup again 109 } else { 110 lookupMetadata(onHomePage, onMetadataFailure); 111 } 112 113 } 114 115 function updateHomePage(item) { 116 var homePage = $("home-page"); 117 118 // if this item doesn't have a home page link then 119 // remove the existing link or ignore 120 if (!item.HomePage) { 121 homePage && Element.remove(homePage); 122 return; 123 } 124 125 // update the link or create it if this item does have a home page 126 if (homePage) { 127 homePage.href = item.HomePage; 128 } else { 129 $($$(".x-links")[0].children[1]).insert({top: 130 new Element("a", {href: item.HomePage, target: "_new", id: "home-page"}).update(item.Title + " " + "<?php echo t("Metadata Home Page") ?>")}); 131 } 132 133 } 134 135 function onHomePage(transport) { 136 ajax_remove_request(); 137 138 var fakeItem = {HomePage: ""}; 139 140 // make sure we got valid data; if not ignore 141 if (!transport || !transport.responseJSON || !transport.responseJSON.VideoLookupList) { 142 updateHomePage(fakeItem); 143 return; 144 } 145 146 var list = transport.responseJSON.VideoLookupList; 147 148 // ignore if there's no data 149 if (list.Count == 0) { 150 updateHomePage(fakeItem); 151 return; 152 153 // populate the data immediately if there is one result 154 } else if (list.Count == 1) { 155 updateHomePage(list.VideoLookups[0]); 156 157 // if we can pick the right item from the list then use it 158 // otherwise hich result 159 } else { 160 var item = guessItem(list); 161 if (item) { 162 updateMetadata(item); 163 } else { 164 updateHomePage(fakeItem); 165 } 166 } 167 168 169 } 170 171 // displays a dialog with an image and title of each possible show 172 // if the user clicks on one of them then populates the metadata in the page 173 function multipleResultDialog(list) { 174 // parent div for the result 175 var parent = new Element("div", {"class": "multiple-metadata"}); 176 177 // add all of the results 178 parent.insert(generateResults(list)); 179 180 // add a cancel "button" to exit without choosing an option 181 var a = new Element("a", {}).update("<?php echo t("Cancel") ?>"); 182 Event.observe(a, "click", function() { Dialogs.close(); }); 183 var d = new Element("div", {"class": "commands"}); 184 d.insert(a); 185 parent.insert(d); 186 187 // display the dialog 188 new Dialog({ 189 opacity: 0.9, 190 title: "<?php echo t("Select the correct show")?>", 191 content: parent 192 }).open(); 193 } 194 195 // returns a div with a list of all of the items neatly formatted 196 function generateResults(list) { 197 var div = new Element("div", {"class": "metadata-list"}); 198 199 for (var i=0; i < list.VideoLookups.length; i++) { 200 div.insert(generateResultsItem(list.VideoLookups[i])); 201 } 202 203 return div; 204 } 205 206 // returns a div for a single result 207 // includes hover and click event handlers 208 function generateResultsItem(item) { 209 var div = new Element("div", {"class": "metadata-item"}); 210 Event.observe(div, "mouseover", function(e) { this.addClassName("hover");}); 211 Event.observe(div, "mouseout", function(e) { this.removeClassName("hover");}); 212 Event.observe(div, "click", function(e) { updateMetadata(item); Dialogs.close();}); 213 var img = generateItemImg(item); 214 div.insert(img); 215 var title = new Element("div", {"class": "title"}); 216 var titleString = item.Title; 217 if (item.Year && item.Year > 0) { 218 var suffix = " (" + item.Year + ")"; 219 titleString = titleString.endsWith(suffix) ? titleString : titleString + suffix; 220 } 221 222 title.update(titleString); 223 div.insert(title); 224 225 var desc = new Element("div", {"class": "description"}); 226 var descString = item.Description.length > 450 ? item.Description.substring(0, 450) + "..." : item.Description; 227 desc.update(descString); 228 div.insert(desc); 229 230 return div; 231 } 232 233 // generates an image or empty div based on if there is 234 // any thumbnail art work for this item 235 function generateItemImg(item) { 236 if (item.Artwork && item.Artwork.length) { 237 var art = item.Artwork[0]; 238 var thumbUrl = art.Thumbnail; 239 240 // hack to allow proxying of ttvdb.com images since they don't allow hot linking 241 if (<?php echo $_SERVER['HTTPS'] == 'on' ? "false" : "true"?> && thumbUrl.startsWith("http://www.thetvdb.com")) { 242 thumbUrl = "<?php echo root_url ?>tv/ttvdb_proxy?url=" + thumbUrl.substring(22); 243 } 244 245 return new Element("img", {src: thumbUrl, "class": art.Type}); 246 } 247 return new Element("div", {"class": "no-art"}); 248 } 249 250 // callback for failure contacting the server 251 function onMetadataFailure(response) { 252 ajax_remove_request(); 253 messageDialog("<?php echo t("Metadata Lookup Error")?>", "<?php echo t("Error contacting server to retrieve metadata.")?>"); 254 } 255 256 // displays a dialog with a message in it and an OK button 257 function messageDialog(title, msg) { 258 $("metadata-message").update(msg); 259 new Dialog({ 260 opacity: 0.9, 261 title: title, 262 target:{ 263 id:'message-dialog', 264 auto:true 265 } 266 }).open(); 267 268 } 269 270 // Hook to start up the Dialog JS 271 Dialogs.load(); 272 27 273 // --> 28 274 </script> 29 30 275 <h3><?php echo t('Advanced Options') ?>:</h3> 31 276 (<?php 32 echo '<a href="#"onclick="toggle_advanced(false); return false;" id="hide_advanced"';277 echo '<a onclick="toggle_advanced(false); return false;" id="hide_advanced"'; 33 278 if (!$_SESSION['tv']['show_advanced_schedule']) 34 279 echo ' style="display: none"'; 35 280 echo '>', t('Hide'), '</a>', … … 133 378 <dt><?php echo t('Preferred Input') ?>:</dt> 134 379 <dd><?php input_select($schedule->prefinput, 'prefinput') ?></dd> 135 380 <dt><?php echo t('Internet Reference #') ?>:</dt> 136 <dd ><input type="text" name="inetref" value="<?php echo html_entities($schedule->inetref) ?>"></dd>381 <dd class="commands"><input id="inetref" class="inetref" type="text" name="inetref" value="<?php echo html_entities($schedule->inetref) ?>"><a onclick="lookupMetadata(onMetadata, onMetadataFailure); return false;"><?php echo t("Look up Metadata")?></a></dd> 137 382 <dt><?php echo t('Season') ?>:</dt> 138 <dd><input type="text" class="quantity" name="season" value="<?php echo html_entities($schedule->season) ?>"></dd>383 <dd><input type="text" id="season" class="quantity" name="season" value="<?php echo html_entities($schedule->season) ?>"></dd> 139 384 <dt><?php echo t('Episode') ?>:</dt> 140 <dd><input type="text" class="quantity" name="episode" value="<?php echo html_entities($schedule->episode) ?>"></dd>385 <dd><input type="text" id="episode" class="quantity" name="episode" value="<?php echo html_entities($schedule->episode) ?>"></dd> 141 386 <dt><label for="autometadata"><?php echo t('Look up Metadata') ?>:</label></dt> 142 387 <dd><input type="checkbox" class="radio" id="autometadata" name="autometadata"<?php if ($schedule->autometadata) echo ' CHECKED' ?> value="1"></dd> 143 388 <dt><label for="autocommflag"><?php echo t('Auto-flag commercials') ?>:</label></dt> … … 167 412 <dd><input type="text" class="quantity" name="endoffset" value="<?php echo html_entities($schedule->endoffset) ?>"> 168 413 <?php echo t('minutes') ?></dd> 169 414 </dl> 415 416 <div style="display: none;" id="message-dialog"> 417 <div id="metadata-message"></div> 418 <div class="commands"> 419 <a onclick="Dialogs.close(); return false;"><?php echo t('OK') ?></a> 420 </div> 421 </div> -
modules/tv/tmpl/default/detail.php
diff --git a/modules/tv/tmpl/default/detail.php b/modules/tv/tmpl/default/detail.php index 71ed1ea..087b8ca 100644
a b 94 94 parameters: {exit: 1, 95 95 host: host, 96 96 chanid: chanid, 97 starttime: starttime ,97 starttime: starttime 98 98 } 99 99 } 100 100 ); 101 101 } 102 102 103 // Tries to find metadata for the current item 104 // If found adds a "Home Page" link to the page 105 function detailLookupMetadata() { 106 new Ajax.Request('<?php echo root_url ?>tv/lookup_metadata', 107 { 108 parameters: { 109 'title' : "<?php echo $program->title ?>", 110 'subtitle' : "<?php echo $program->subtitle ?>", 111 'inetref' : "<?php echo $program->inetref ?>", 112 'season' : "<?php echo $program->season ?>", 113 'episode' : "<?php echo $program->episode ?>" 114 }, 115 asynchronous: true, 116 method: 'get', 117 onSuccess: detailOnMetadata, 118 onFailure: detailOnMetadataFailure 119 } 120 ); 121 122 } 123 124 // if metadata is found inserts a home page link behind the inetref value 125 function detailOnMetadata(transport) { 126 var list = transport.responseJSON.VideoLookupList; 127 128 // if there are 0 or > 1 entry then we just ignore it 129 if (list.Count == 1 && !!list.VideoLookups[0].HomePage) { 130 $("metadata-home-page").insert( 131 new Element("a", {href: list.VideoLookups[0].HomePage, target: "_new"}).update("<?php echo t("Metadata Home Page")?>")); 132 } 133 134 } 135 136 // silently fail (no need to disrupt the page) 137 function detailOnMetadataFailure(transport) { 138 } 139 140 // hook to look up data once the page has started 141 detailLookupMetadata(); 103 142 // --> 104 143 </script> 105 144 … … 214 250 if (strlen($program->inetref) > 0) { 215 251 ?><tr class="x-extras"> 216 252 <th><?php echo t('Internet Reference #') ?>:</th> 217 <td><?php echo $program->inetref ?> </td>253 <td><?php echo $program->inetref ?> <span class="commands" id="metadata-home-page"></span></td> 218 254 </tr><?php 219 255 } 220 256 if ($program->season > 0) { -
modules/tv/tmpl/default/recorded.php
diff --git a/modules/tv/tmpl/default/recorded.php b/modules/tv/tmpl/default/recorded.php index d7de684..d0b483f 100644
a b 76 76 </tr> 77 77 </table> 78 78 </form> 79 80 79 <table id="recorded_list" border="0" cellpadding="0" cellspacing="0" class="list small"> 81 80 <tr class="menu"> 82 81 <td class="list"<?php if ($group_field) echo ' colspan="2"' ?>> </td> -
new file modules/tv/ttvdb_proxy.php
diff --git a/modules/tv/ttvdb_proxy.php b/modules/tv/ttvdb_proxy.php new file mode 100644 index 0000000..ba1433c
- + 1 <?php 2 /** 3 * Proxies requests to ttvdb.com so that we can grab images from their site 4 * Tries to avoid being a total open proxy by only proxying to thetvdb.com 5 * 6 * @license GPL 7 * 8 * @package MythWeb 9 * @subpackage TV 10 * 11 /**/ 12 13 header('Content-Type: image/jpg'); 14 15 echo @file_get_contents("http://www.thetvdb.com" . $_REQUEST['url']); -
skins/default/style.css
diff --git a/skins/default/style.css b/skins/default/style.css index 2fbb26e..7353351 100644
a b 235 235 #feed_buttons a { 236 236 padding-right: 1em; 237 237 } 238 239 #dialog-overlay { 240 background-color: #506090; 241 } 242 243 #dialog-top { 244 background-color: #203670; 245 border: 1px solid #203670; 246 } 247 248 #dialog-title { 249 color: white; 250 251 } 252 253 #dialog-content { 254 background-color: #265990; 255 text-align: center; 256 } -
skins/default/tv_detail.css
diff --git a/skins/default/tv_detail.css b/skins/default/tv_detail.css index 2148bf7..ca84088 100644
a b 270 270 color: #F0F000; 271 271 text-decoration: underline; 272 272 } 273 274 #metadata-home-page { 275 margin-left: 20px; 276 } -
skins/default/tv_schedule.css
diff --git a/skins/default/tv_schedule.css b/skins/default/tv_schedule.css index b1a7ed9..2740816 100644
a b 56 56 width: 18em; 57 57 } 58 58 59 #schedule input.inetref { 60 width: 5em !important; 61 margin-right: 20px; 62 } 63 59 64 /* A special subclass for options with extra-long input fields */ 60 65 #schedule .x-options dl.x-long input, .x-options dl.x-long textarea { 61 66 width: 32em; … … 73 78 padding: .5em 0 1em 0; 74 79 } 75 80 81 /* Metadata specific classes */ 82 83 #metadata-message { 84 margin-bottom: 10px; 85 } 86 87 .multiple-metadata { 88 } 89 90 .metadata-item { 91 width: 500px; 92 min-height: 150px; 93 margin: 0px 15px 10px 0px; 94 border: 1px solid white; 95 } 96 97 .metadata-item.hover { 98 background-color: white; 99 cursor: pointer; 100 color: #265990; 101 } 102 103 .metadata-item img { 104 float: left; 105 } 106 107 .metadata-item img.coverart { 108 height: 150px; 109 width: 100px; 110 margin-right: 10px; 111 } 112 113 .metadata-item img.fanart { 114 height: 150px; 115 width: 100px; 116 margin-right: 10px; 117 } 118 119 .metadata-item img.banner { 120 height: 55px; 121 width: 300px; 122 margin: 5px auto -10px; 123 float: none; 124 } 125 126 .metadata-item .no-art { 127 height: 2em; 128 } 129 130 131 .metadata-item .title { 132 font-size: 2em; 133 font-weight: bold; 134 padding-top: .5em; 135 } 136 137 .metadata-item .description { 138 padding: 0px 10px 10px; 139 text-align: left; 140 }