Index: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilLayout.js |
— | — | @@ -5,21 +5,21 @@ |
6 | 6 | } |
7 | 7 | |
8 | 8 | mw.SmilLayout.prototype = { |
9 | | - // Stores the number of assets we are currently loading |
| 9 | + // Stores the number of assets we are currently loading |
10 | 10 | mediaLoadingCount : 0, |
11 | 11 | |
12 | 12 | // Stores the callback function for once assets are loaded |
13 | 13 | mediaLoadedCallback : null, |
14 | 14 | |
15 | | - //Stores the current top z-index for "putting things on top" |
| 15 | + // Stores the current top z-index for "putting things on top" |
16 | 16 | topZindex: 1, |
17 | 17 | |
18 | | - // Constructor: |
| 18 | + // Constructor: |
19 | 19 | init: function( smilObject ){ |
20 | 20 | // Setup a pointer to parent smil Object |
21 | 21 | this.smil = smilObject; |
22 | 22 | |
23 | | - // Set the smil layout dom: |
| 23 | + // Set the smil layout dom: |
24 | 24 | this.$dom = this.smil.getDom().find( 'layout' ); |
25 | 25 | |
26 | 26 | // Reset the htmlDOM cache |
— | — | @@ -36,13 +36,13 @@ |
37 | 37 | }, |
38 | 38 | |
39 | 39 | /* |
40 | | - * Get layout |
41 | | - */ |
| 40 | + * Get layout |
| 41 | + */ |
42 | 42 | getRootLayout: function(){ |
43 | 43 | var _this = this; |
44 | 44 | mw.log( "SmilLayout::getRootLayout:" ); |
45 | 45 | if( !this.$rootLayout ){ |
46 | | - // Setup target Size: |
| 46 | + // Setup target Size: |
47 | 47 | this.targetWidth = this.smil.embedPlayer.getWidth(); |
48 | 48 | this.targetHeight = this.smil.embedPlayer.getHeight(); |
49 | 49 | |
— | — | @@ -56,7 +56,7 @@ |
57 | 57 | 'overflow': 'hidden' |
58 | 58 | }); |
59 | 59 | |
60 | | - // Update the root layout css |
| 60 | + // Update the root layout css |
61 | 61 | this.$rootLayout.css( _this.getRootLayoutCss() ); |
62 | 62 | |
63 | 63 | // Update the root layout html |
— | — | @@ -66,18 +66,19 @@ |
67 | 67 | }, |
68 | 68 | |
69 | 69 | /** |
70 | | - * Get and increment the top z-index counter: |
| 70 | + * Get and increment the top z-index counter: |
71 | 71 | */ |
72 | 72 | getTopZIndex: function(){ |
73 | 73 | return this.topZindex++; |
74 | 74 | }, |
75 | 75 | |
76 | 76 | /** |
77 | | - * Draw a smilElement to the layout. |
78 | | - * |
79 | | - * If the element does not exist in the html dom add it. |
80 | | - * @parma {Element} smilElement to be drawn. |
81 | | - */ |
| 77 | + * Draw a smilElement to the layout. |
| 78 | + * |
| 79 | + * If the element does not exist in the html dom add it. |
| 80 | + * |
| 81 | + * @parma {Element} smilElement to be drawn. |
| 82 | + */ |
82 | 83 | drawElement: function( smilElement ) { |
83 | 84 | var _this = this; |
84 | 85 | // Check for quick "show" path: |
— | — | @@ -87,9 +88,10 @@ |
88 | 89 | return ; |
89 | 90 | } |
90 | 91 | |
91 | | - // Else draw the node into the regionTarget |
| 92 | + // Else draw the node into the regionTarget |
92 | 93 | |
93 | | - //mw.log( "SmilLayout::drawElement: " + nodeName + '.' + $j( smilElement ).attr('id' ) + ' into ' + regionId ); |
| 94 | + // mw.log( "SmilLayout::drawElement: " + nodeName + '.' + $j( |
| 95 | + // smilElement ).attr('id' ) + ' into ' + regionId ); |
94 | 96 | var $regionTarget = this.getRegionTarget( smilElement ); |
95 | 97 | |
96 | 98 | // Make sure we have a $regionTarget |
— | — | @@ -106,14 +108,16 @@ |
107 | 109 | _this.getSmilElementHtml( smilElement ) |
108 | 110 | ) |
109 | 111 | } else { |
110 | | - // Make sure the element is visible ( may be faster to just call show directly) |
| 112 | + // Make sure the element is visible ( may be faster to just call |
| 113 | + // show directly) |
111 | 114 | if( $targetElement.is(':hidden') ) { |
112 | 115 | $targetElement.show(); |
113 | 116 | } |
114 | 117 | } |
115 | 118 | }, |
116 | 119 | |
117 | | - drawElementThumb: function( $target, $node, relativeTime, callback){ |
| 120 | + drawElementThumb: function( $target, $node, relativeTime, callback){ |
| 121 | + var _this = this; |
118 | 122 | mw.log('SmilLayout::drawElementThumb: ' + $node.attr('id') + ' relative time:' + relativeTime ); |
119 | 123 | if( $target.length == 0 ){ |
120 | 124 | mw.log("Error drawElementThumb to empty target"); |
— | — | @@ -128,13 +132,25 @@ |
129 | 133 | } |
130 | 134 | switch ( this.smil.getRefType( $node ) ){ |
131 | 135 | case 'img': |
132 | | - // xxx we could eventually use canvas as well but for now just add it at 100% |
| 136 | + // xxx we should use canvas here but for now just hack it up: |
| 137 | + var $playerImage = this.getSmilImgHtml( $node ); |
133 | 138 | $target.html( |
134 | | - this.getSmilImgHtml( $node, { |
135 | | - 'width' : $target.width(), |
136 | | - 'height' : $target.height() |
| 139 | + $j('<img />') |
| 140 | + .attr({ |
| 141 | + 'src' : this.smil.getAssetUrl( $node.attr( 'src' ) ) |
137 | 142 | }) |
138 | 143 | ); |
| 144 | + var img = $target.find('img').get(0) |
| 145 | + _this.getNaturalSize( img, function( natrualSize ){ |
| 146 | + _this.fitMeetBest( |
| 147 | + img, |
| 148 | + natrualSize, |
| 149 | + { |
| 150 | + 'width' : $target.width(), |
| 151 | + 'height' : $target.height() |
| 152 | + } |
| 153 | + ) |
| 154 | + }); |
139 | 155 | break; |
140 | 156 | case 'cdata_html': |
141 | 157 | // Scale down the html into the target width |
— | — | @@ -180,7 +196,7 @@ |
181 | 197 | naturaSize.width = drawElement.videoWidth; |
182 | 198 | |
183 | 199 | // Draw the thumb via canvas grab |
184 | | - // NOTE canvas scale issue prevents redraw at thumb resolution |
| 200 | + // NOTE canvas scale issue prevents redraw at thumb resolution |
185 | 201 | // xxx should revisit thumb size issue: |
186 | 202 | try{ |
187 | 203 | $target.html( $j('<canvas />') |
— | — | @@ -204,7 +220,8 @@ |
205 | 221 | callback(); |
206 | 222 | } |
207 | 223 | |
208 | | - // check if relativeTime transform matches current absolute time then render directly: |
| 224 | + // check if relativeTime transform matches current absolute time then |
| 225 | + // render directly: |
209 | 226 | var drawTime = ( relativeTime + this.smil.parseTime( $j( $node ).attr('clipBegin') ) ); |
210 | 227 | if( this.smil.isSameFrameTime( drawElement.currentTime, drawTime ) ) { |
211 | 228 | mw.log("getVideoCanvasThumb: Draw time:" + drawTime + " matches video time drawFrame:" +drawElement.currentTime ); |
— | — | @@ -216,7 +233,7 @@ |
217 | 234 | var $tmpFrameNode = $node.clone(); |
218 | 235 | $tmpFrameNode.attr('id', $node.attr('id') + '_tmpFrameNode' ); |
219 | 236 | this.smil.getBuffer().bufferedSeekRelativeTime( $tmpFrameNode, relativeTime, function(){ |
220 | | - // update the drawElement |
| 237 | + // update the drawElement |
221 | 238 | drawElement = $j( '#' + _this.smil.getPageDomId( $tmpFrameNode ) ).get(0); |
222 | 239 | drawFrame( drawElement ); |
223 | 240 | // Remove the temporary node from dom |
— | — | @@ -226,7 +243,7 @@ |
227 | 244 | }, |
228 | 245 | |
229 | 246 | /** |
230 | | - * Get a region target for a given smilElement |
| 247 | + * Get a region target for a given smilElement |
231 | 248 | */ |
232 | 249 | getRegionTarget: function( smilElement ){ |
233 | 250 | var regionId = $j( smilElement ).attr( 'region'); |
— | — | @@ -238,15 +255,15 @@ |
239 | 256 | return false; |
240 | 257 | } |
241 | 258 | } else { |
242 | | - // No region provided use the rootLayout: |
| 259 | + // No region provided use the rootLayout: |
243 | 260 | $regionTarget = this.$rootLayout; |
244 | 261 | } |
245 | 262 | return $regionTarget; |
246 | 263 | }, |
247 | 264 | |
248 | 265 | /** |
249 | | - * Hide a smilElement in the layout |
250 | | - */ |
| 266 | + * Hide a smilElement in the layout |
| 267 | + */ |
251 | 268 | hideElement: function( smilElement ){ |
252 | 269 | // Check that the element is already in the dom |
253 | 270 | var $targetElement = this.$rootLayout.find( '#' + this.smil.getPageDomId( smilElement ) ); |
— | — | @@ -258,13 +275,15 @@ |
259 | 276 | |
260 | 277 | /** |
261 | 278 | * Get the transformed smil element in html format |
262 | | - * @param |
| 279 | + * |
| 280 | + * @param |
263 | 281 | */ |
264 | 282 | getSmilElementHtml: function( smilElement ) { |
265 | 283 | var smilType = this.smil.getRefType( smilElement ) |
266 | 284 | |
267 | 285 | switch( smilType ){ |
268 | | - // Not part of strict smil, but saves time being able have an "html" display mode |
| 286 | + // Not part of strict smil, but saves time being able have an "html" |
| 287 | + // display mode |
269 | 288 | case 'cdata_html': |
270 | 289 | return this.getSmilCDATAHtml( smilElement ); |
271 | 290 | break; |
— | — | @@ -277,7 +296,8 @@ |
278 | 297 | case 'audio': |
279 | 298 | return this.getSmilAudioHtml( smilElement ); |
280 | 299 | break; |
281 | | - // Smil Text: http://www.w3.org/TR/SMIL/smil-text.html ( obviously we support a subset ) |
| 300 | + // Smil Text: http://www.w3.org/TR/SMIL/smil-text.html ( obviously |
| 301 | + // we support a subset ) |
282 | 302 | case 'smiltext': |
283 | 303 | return this.getSmilTextHtml( smilElement ); |
284 | 304 | break; |
— | — | @@ -295,8 +315,8 @@ |
296 | 316 | }, |
297 | 317 | |
298 | 318 | /** |
299 | | - * Return the video |
300 | | - */ |
| 319 | + * Return the video |
| 320 | + */ |
301 | 321 | getSmilVideoHtml: function( smilElement ){ |
302 | 322 | return $j('<video />') |
303 | 323 | .attr( { |
— | — | @@ -319,11 +339,12 @@ |
320 | 340 | }, |
321 | 341 | |
322 | 342 | /** |
323 | | - * Get Smil CDATA ( passed through jQuery .clean as part of fragment creation ) |
324 | | - * XXX Security XXX |
325 | | - * Here we are parsing in SMIL -> HTML should be careful about XSS or script elevation |
326 | | - * |
327 | | - * @@TODO check all sources are "local" only smil and enforce domain on all asset sources |
| 343 | + * Get Smil CDATA ( passed through jQuery .clean as part of fragment |
| 344 | + * creation ) XXX Security XXX Here we are parsing in SMIL -> HTML should be |
| 345 | + * careful about XSS or script elevation |
| 346 | + * |
| 347 | + * @@TODO check all sources are "local" only smil and enforce domain on all |
| 348 | + * asset sources |
328 | 349 | */ |
329 | 350 | getSmilCDATAHtml: function( smilElement, targetWidth ){ |
330 | 351 | // Default target width if unset: |
— | — | @@ -337,7 +358,7 @@ |
338 | 359 | var xmlCdata = ''; |
339 | 360 | for ( var i=0; i < el.childNodes.length; i++ ) { |
340 | 361 | var node = el.childNodes[i]; |
341 | | - // Check for text cdata Node type: |
| 362 | + // Check for text cdata Node type: |
342 | 363 | if( node.nodeType == 4 ) { |
343 | 364 | xmlCdata += node.nodeValue; |
344 | 365 | } |
— | — | @@ -345,16 +366,17 @@ |
346 | 367 | |
347 | 368 | var textCss = this.transformSmilCss( smilElement , targetWidth); |
348 | 369 | |
349 | | - // We pass the xmlCdata via jQuery fragment creation, this runs jquery.clean() |
350 | | - // and filters the result html. |
| 370 | + // We pass the xmlCdata via jQuery fragment creation, this runs |
| 371 | + // jquery.clean() |
| 372 | + // and filters the result html. |
351 | 373 | var $cdataHtml = $j( '<div />' ).append( |
352 | 374 | $j( xmlCdata ) |
353 | 375 | ) |
354 | 376 | |
355 | | - //See if we need to scale |
| 377 | + // See if we need to scale |
356 | 378 | var scalePercent = ( targetWidth / this.getVirtualWidth() ); |
357 | 379 | |
358 | | - // Links go to a new window and are disable scale down. |
| 380 | + // Links go to a new window and are disable scale down. |
359 | 381 | $cdataHtml.find('a').each( function(inx, link ){ |
360 | 382 | if( scalePercent < 1 ){ |
361 | 383 | $j(link).attr('href', '#'); |
— | — | @@ -365,19 +387,20 @@ |
366 | 388 | |
367 | 389 | if( scalePercent != 1 ){ |
368 | 390 | $cdataHtml.find('img').each( function(inx, image ){ |
369 | | - // make sure each image is loaded before we transform, |
370 | | - // AND via the magic of closures this updates $cdataHtml output in-place |
| 391 | + // make sure each image is loaded before we transform, |
| 392 | + // AND via the magic of closures this updates $cdataHtml output |
| 393 | + // in-place |
371 | 394 | $j( image ).load(function(){ |
372 | | - // if the image has an height or width scale by scalePercent |
| 395 | + // if the image has an height or width scale by scalePercent |
373 | 396 | if ( $j( image ).width() ){ |
374 | 397 | var imageTargetWidth = scalePercent* $j( image ).width(); |
375 | 398 | var imageTargetHeight = scalePercent* $j( image ).height() |
376 | 399 | } else if( image.naturalWidth ){ |
377 | | - // check natural width? |
| 400 | + // check natural width? |
378 | 401 | imageTargetWidth = scalePercent * image.naturalWidth; |
379 | 402 | imageTargetHeight = scalePercent * image.naturalHeight; |
380 | 403 | } |
381 | | - //scale the image: |
| 404 | + // scale the image: |
382 | 405 | $j( image ).css({ |
383 | 406 | 'width' : imageTargetWidth, |
384 | 407 | 'height' :imageTargetHeight |
— | — | @@ -386,7 +409,7 @@ |
387 | 410 | }) |
388 | 411 | } |
389 | 412 | |
390 | | - // Return the cdata |
| 413 | + // Return the cdata |
391 | 414 | return $j('<div />') |
392 | 415 | .attr( 'id' , this.smil.getPageDomId( smilElement ) ) |
393 | 416 | // Wrap in font-size percentage relative to virtual size |
— | — | @@ -399,16 +422,17 @@ |
400 | 423 | }, |
401 | 424 | |
402 | 425 | /** |
403 | | - * Get a text element html |
| 426 | + * Get a text element html |
404 | 427 | */ |
405 | 428 | getSmilTextHtml: function( textElement ) { |
406 | 429 | var _this = this; |
407 | 430 | |
408 | | - // Empty initial text value |
| 431 | + // Empty initial text value |
409 | 432 | var textValue = ''; |
410 | 433 | |
411 | | - // If the textElement has no child node directly set the text value |
412 | | - // ( if has child nodes, text will be selected by time in SmilAnimate.transformTextForTime ) |
| 434 | + // If the textElement has no child node directly set the text value |
| 435 | + // ( if has child nodes, text will be selected by time in |
| 436 | + // SmilAnimate.transformTextForTime ) |
413 | 437 | if( $j( textElement ).children().length == 0 ){ |
414 | 438 | mw.log( 'Direct text value to: ' + textValue); |
415 | 439 | textValue = $j( textElement ).text(); |
— | — | @@ -416,14 +440,14 @@ |
417 | 441 | |
418 | 442 | var textCss = _this.transformSmilCss( textElement ); |
419 | 443 | |
420 | | - // Return the htmlElement |
| 444 | + // Return the htmlElement |
421 | 445 | return $j('<span />') |
422 | 446 | .attr( 'id' , this.smil.getPageDomId( textElement ) ) |
423 | 447 | // Wrap in font-size percentage relative to virtual size |
424 | 448 | .css( 'font-size', ( ( this.targetWidth / this.getVirtualWidth() )*100 ) + '%' ) |
425 | 449 | .html( |
426 | 450 | $j('<span />') |
427 | | - // Transform smil css into html css: |
| 451 | + // Transform smil css into html css: |
428 | 452 | .css( textCss ) |
429 | 453 | // Add the text value |
430 | 454 | .text( textValue ) |
— | — | @@ -431,54 +455,128 @@ |
432 | 456 | }, |
433 | 457 | |
434 | 458 | /** |
435 | | - * Get Image html per given smil element and requested time |
436 | | - * @param {element} imgElement The image tag element to be updated |
| 459 | + * Get Image html per given smil element |
| 460 | + * |
| 461 | + * @param {element} |
| 462 | + * imgElement The image tag element to be updated |
437 | 463 | */ |
438 | | - getSmilImgHtml: function( imgElement , targetSize) { |
| 464 | + getSmilImgHtml: function( smilImg ) { |
| 465 | + var _this = this; |
| 466 | + var $image = $j('<img />') |
| 467 | + .attr( { |
| 468 | + 'id' : this.smil.getPageDomId( smilImg ), |
| 469 | + 'src' : this.smil.getAssetUrl( $j( smilImg ).attr( 'src' ) ) |
| 470 | + } ); |
439 | 471 | |
440 | | - if( ! targetSize ){ |
441 | | - targetSize = {}; |
442 | | - targetSize.width = this.targetWidth; |
443 | | - targetSize.height = this.targetHeight; |
| 472 | + _this.getNaturalSize( $image.get(0), function( naturalSize) { |
| 473 | + _this.doAssetLayout( smilImg , naturalSize); |
| 474 | + }) |
| 475 | + return $image; |
| 476 | + }, |
| 477 | + // xxx should really use a callback instead of failing if the media is not |
| 478 | + // loaded |
| 479 | + getNaturalSize: function( img , callback){ |
| 480 | + // note this just works for images atm |
| 481 | + if( !img ){ |
| 482 | + callback( false ); |
444 | 483 | } |
445 | | - var adjustImageSize = function( image ){ |
446 | | - // xxx Should read smil "imgElement" fill type |
447 | | - var imageCss = {}; |
| 484 | + if( img.naturalWidth ){ |
| 485 | + callback( { |
| 486 | + 'width' : img.naturalWidth, |
| 487 | + 'height' : img.naturalHeight, |
| 488 | + } ) |
| 489 | + } else { |
| 490 | + $j( img ).load(function(){ |
| 491 | + callback( { |
| 492 | + 'width' : this.naturalWidth, |
| 493 | + 'height' : this.naturalHeight, |
| 494 | + } ) |
| 495 | + }); |
| 496 | + } |
| 497 | + }, |
| 498 | + /** |
| 499 | + * Layout an asset |
| 500 | + */ |
| 501 | + doAssetLayout: function( smilElement, naturalSize ){ |
| 502 | + |
| 503 | + // We default smil layout to meetBest |
| 504 | + var fitMode = $j( smilElement).attr('fit'); |
| 505 | + if( !fitMode ){ |
| 506 | + fitMode = 'meetBest' |
| 507 | + } |
| 508 | + if( fitMode == 'meetBest' ){ |
| 509 | + var targetSize = { |
| 510 | + 'width' : this.smil.embedPlayer.getWidth(), |
| 511 | + 'height' : this.smil.embedPlayer.getHeight() |
| 512 | + } |
| 513 | + this.fitMeetBest( |
| 514 | + $j( '#' + this.smil.getPageDomId( smilElement ) ).get(0), |
| 515 | + naturalSize, |
| 516 | + targetSize |
| 517 | + ); |
| 518 | + } else { |
| 519 | + mw.log("Layout mode: " + fitMode + ' not yet supported'); |
| 520 | + } |
448 | 521 | |
449 | | - // Fit the image pre the provided targetWidth closure |
450 | | - if( image.naturalWidth > targetSize.width ){ |
451 | | - imageCss.width = targetSize.width; |
452 | | - imageCss.height = imageCss.width * |
453 | | - ( image.naturalHeight / image.naturalWidth ) |
454 | | - } |
455 | | - |
456 | | - // fit vertically |
457 | | - if(! imageCss.height || imageCss.height > targetSize.height ){ |
458 | | - imageCss.height = targetSize.height; |
459 | | - imageCss.width = imageCss.height * |
460 | | - ( image.naturalWidth / image.naturalHeight ) |
461 | | - } |
462 | | - $j( image ).css( imageCss ); |
| 522 | + // Check for panZoom attribute |
| 523 | + if( $j( smilElement).attr('panZoom') ){ |
| 524 | + _this.panZoomLayout( smilElement ); |
| 525 | + } |
| 526 | + }, |
463 | 527 | |
| 528 | + // http://www.w3.org/TR/SMIL/smil-layout.html#adef-fit |
| 529 | + // xxx should add the other fitting modes |
| 530 | + fitMeetBest: function( element, natrualSize, targetSize ){ |
| 531 | + var _this = this; |
| 532 | + |
| 533 | + // xxx Should read smil "imgElement" fill type |
| 534 | + var imageCss = {}; |
| 535 | + |
| 536 | + // Fit the image per the provided targetWidth closure |
| 537 | + if( natrualSize.width > targetSize.width ){ |
| 538 | + imageCss.width = '100%'; |
| 539 | + imageCss.height = ( 100 * ( natrualSize.height / natrualSize.width ) ) + '%'; |
464 | 540 | } |
465 | | - mw.log( "Add image:" + this.smil.getAssetUrl( $j( imgElement ).attr( 'src' ) ) ); |
466 | | - var $image = $j('<img />') |
467 | | - .attr( { |
468 | | - 'id' : this.smil.getPageDomId( imgElement ), |
469 | | - 'src' : this.smil.getAssetUrl( $j( imgElement ).attr( 'src' ) ) |
470 | | - } ); |
471 | | - if( $image.get(0).naturalHeight ){ |
472 | | - adjustImageSize( $image.get(0) ); |
473 | | - }else { |
474 | | - $image.load( function(){ |
475 | | - adjustImageSize( this ); |
476 | | - }); |
| 541 | + |
| 542 | + // Fit vertically |
| 543 | + if(! imageCss.height || imageCss.height > targetSize.height ){ |
| 544 | + imageCss.height = '100%'; |
| 545 | + imageCss.width = ( 100 * ( natrualSize.width / natrualSize.height ) ) + '%'; |
477 | 546 | } |
478 | | - return $image; |
| 547 | + // update the layout of the element |
| 548 | + $j( element ).css( imageCss ); |
479 | 549 | }, |
480 | | - |
481 | 550 | /** |
482 | | - * Parse pan zoom attribute string |
| 551 | + * layout function |
| 552 | + */ |
| 553 | + panZoomLayout: function( smilElement ){ |
| 554 | + var _this = this; |
| 555 | + var panZoom = this.parsePanZoom( $j( smilElement).attr('panZoom') ); |
| 556 | + var img = $j( '#' + this.smil.getPageDomId( smilElement ) ).get(0); |
| 557 | + |
| 558 | + _this.getNaturalSize( img, function( natrualSize ){ |
| 559 | + // Check if the transfrom is needed: |
| 560 | + if( parseInt( panZoom.left ) == 0 |
| 561 | + && |
| 562 | + parseInt( panZoom.top ) == 0 |
| 563 | + && |
| 564 | + ( parseInt( panZoom.width ) == 100 && panZoom.width.indexOf('%') != -1 ) |
| 565 | + && |
| 566 | + ( parseInt( panZoom.height ) == 100 && panZoom.height.indexOf('%') != -1 ) |
| 567 | + ){ |
| 568 | + // no transform is needed |
| 569 | + return ; |
| 570 | + } |
| 571 | + // Get percent values |
| 572 | + var percentValues = _this.smil.getAnimate().getPercentFromPanZoomValues( panZoomValues, natrualSize ); |
| 573 | + |
| 574 | + // Update the layout via the animation engine updateElementLayout method |
| 575 | + _this.smil.getAnimate().updateElementLayout( smilElement, panZoomValues ); |
| 576 | + }); |
| 577 | + }, |
| 578 | + /** |
| 579 | + * Parse pan zoom attribute string |
| 580 | + * |
483 | 581 | * @param panZoomString |
484 | 582 | */ |
485 | 583 | parsePanZoom: function( panZoomString ){ |
— | — | @@ -496,8 +594,8 @@ |
497 | 595 | }, |
498 | 596 | |
499 | 597 | /** |
500 | | - * Add all the regions to the root layout |
501 | | - */ |
| 598 | + * Add all the regions to the root layout |
| 599 | + */ |
502 | 600 | getRootLayoutHtml: function(){ |
503 | 601 | var _this = this; |
504 | 602 | var $layoutContainer = $j( '<div />' ); |
— | — | @@ -522,8 +620,8 @@ |
523 | 621 | }, |
524 | 622 | |
525 | 623 | /** |
526 | | - * Get the root layout object with updated html properties |
527 | | - */ |
| 624 | + * Get the root layout object with updated html properties |
| 625 | + */ |
528 | 626 | getRootLayoutCss: function( ){ |
529 | 627 | |
530 | 628 | if( this.$dom.find( 'root-layout').length ) { |
— | — | @@ -547,10 +645,10 @@ |
548 | 646 | this.virtualHeight = this.smil.getEmbedPlayer().getHeight(); |
549 | 647 | } |
550 | 648 | |
551 | | - // Merge in transform size to target |
| 649 | + // Merge in transform size to target |
552 | 650 | $j.extend( rootLayoutCss, this.transformSizeToTarget() ); |
553 | 651 | |
554 | | - // Update the layout css |
| 652 | + // Update the layout css |
555 | 653 | return rootLayoutCss; |
556 | 654 | } |
557 | 655 | return {}; |
— | — | @@ -569,35 +667,40 @@ |
570 | 668 | }, |
571 | 669 | |
572 | 670 | /** |
573 | | - * Translate a root layout pixel point into a percent location |
574 | | - * using all percentages instead of pixels lets us scale internal |
575 | | - * layout browser side transforms ( instead of a lot javascript css updates ) |
576 | | - * |
577 | | - * @param {object} layout Css layout to be translated from virtualWidth & virtualHeight |
578 | | - */ |
579 | | - transformVirtualPixleToPercent: function( layout ){ |
| 671 | + * Translate a root layout pixel point into a percent location using all |
| 672 | + * percentages instead of pixels lets us scale internal layout browser side |
| 673 | + * transforms ( instead of a lot javascript css updates ) |
| 674 | + * |
| 675 | + * @param {object} |
| 676 | + * layout Css layout to be translated from virtualWidth & |
| 677 | + * virtualHeight |
| 678 | + */ |
| 679 | + transformVirtualPixleToPercent: function( layout, virtualLayout ){ |
580 | 680 | var percent = { }; |
| 681 | + if( !virtualLayout){ |
| 682 | + virtualLayout = { 'width' : this.virtualWidth, 'height' : this.virtualHeight }; |
| 683 | + } |
581 | 684 | if( layout['width'] ) { |
582 | | - percent['width'] = ( layout['width'] / this.virtualWidth )*100 + '%'; |
| 685 | + percent['width'] = ( layout['width'] / virtualLayout.width )*100 + '%'; |
583 | 686 | } |
584 | 687 | if( layout['left'] ){ |
585 | | - percent['left'] = ( layout['left'] / this.virtualWidth )*100 + '%'; |
| 688 | + percent['left'] = ( layout['left'] / virtualLayout.width )*100 + '%'; |
586 | 689 | } |
587 | 690 | if( layout['height'] ) { |
588 | | - percent['height'] = ( layout['height'] / this.virtualHeight )*100 + '%'; |
| 691 | + percent['height'] = ( layout['height'] / virtualLayout.height )*100 + '%'; |
589 | 692 | } |
590 | 693 | if( layout['top'] ){ |
591 | | - percent['top'] = ( layout['top'] / this.virtualHeight )*100 + '%'; |
| 694 | + percent['top'] = ( layout['top'] / virtualLayout.height )*100 + '%'; |
592 | 695 | } |
593 | 696 | return percent; |
594 | 697 | }, |
595 | 698 | |
596 | 699 | /** |
597 | | - * Transform virtual height width into target size |
598 | | - */ |
| 700 | + * Transform virtual height width into target size |
| 701 | + */ |
599 | 702 | transformSizeToTarget: function(){ |
600 | 703 | |
601 | | - // Setup target height width based on max window size |
| 704 | + // Setup target height width based on max window size |
602 | 705 | var fullWidth = this.targetWidth - 2 ; |
603 | 706 | var fullHeight = this.targetHeight ; |
604 | 707 | |
— | — | @@ -605,7 +708,7 @@ |
606 | 709 | var targetWidth = fullWidth; |
607 | 710 | var targetHeight = targetWidth * ( this.virtualHeight / this.virtualWidth ) |
608 | 711 | |
609 | | - // Check if it exceeds the height constraint: |
| 712 | + // Check if it exceeds the height constraint: |
610 | 713 | if( targetHeight > fullHeight ){ |
611 | 714 | targetHeight = fullHeight; |
612 | 715 | targetWidth = targetHeight * ( this.virtualWidth / this.virtualHeight ); |
— | — | @@ -614,7 +717,8 @@ |
615 | 718 | var offsetTop = ( targetHeight < fullHeight )? ( fullHeight- targetHeight ) / 2 : 0; |
616 | 719 | var offsetLeft = ( targetWidth < fullWidth )? ( fullWidth- targetWidth ) / 2 : 0; |
617 | 720 | |
618 | | - //mw.log(" targetWidth: " + targetWidth + ' fullwidth: ' + fullWidth + ' :: ' + ( fullWidth- targetWidth ) / 2 ); |
| 721 | + // mw.log(" targetWidth: " + targetWidth + ' fullwidth: ' + fullWidth + |
| 722 | + // ' :: ' + ( fullWidth- targetWidth ) / 2 ); |
619 | 723 | return { |
620 | 724 | 'height': targetHeight, |
621 | 725 | 'width' : targetWidth, |
— | — | @@ -625,8 +729,8 @@ |
626 | 730 | }, |
627 | 731 | |
628 | 732 | /** |
629 | | - * Transform smil attributes into html attributes |
630 | | - */ |
| 733 | + * Transform smil attributes into html attributes |
| 734 | + */ |
631 | 735 | transformSmilAttributes: function ( smilElement ){ |
632 | 736 | $smilElement = $j( smilElement ); |
633 | 737 | var smilAttributes = { |
— | — | @@ -640,20 +744,22 @@ |
641 | 745 | attributes[ smilAttributes[ attr ] ] = $smilElement.attr( attr ); |
642 | 746 | } |
643 | 747 | } |
644 | | - // XXX TODO Locally scope all ids into embedPlayer.id + _id |
| 748 | + // XXX TODO Locally scope all ids into embedPlayer.id + _id |
645 | 749 | |
646 | | - // Translate rootLayout properties into div |
| 750 | + // Translate rootLayout properties into div |
647 | 751 | return attributes; |
648 | 752 | }, |
649 | 753 | |
650 | 754 | /** |
651 | | - * Transform smil attributes into css attributes |
652 | | - * @param {object} $smilElement The smil element to be transformed |
653 | | - */ |
| 755 | + * Transform smil attributes into css attributes |
| 756 | + * |
| 757 | + * @param {object} |
| 758 | + * $smilElement The smil element to be transformed |
| 759 | + */ |
654 | 760 | transformSmilCss: function( smilElement, targetWidth ){ |
655 | 761 | $smilElement = $j( smilElement ); |
656 | 762 | |
657 | | - //Set target with to master targetWidth if unset. |
| 763 | + // Set target with to master targetWidth if unset. |
658 | 764 | if( ! targetWidth ){ |
659 | 765 | targetWidth = this.targetWidth |
660 | 766 | } |
— | — | @@ -682,7 +788,8 @@ |
683 | 789 | } |
684 | 790 | |
685 | 791 | // Make the font size fixed so it can be scaled |
686 | | - // based on: http://style.cleverchimp.com/font_size_intervals/altintervals.html |
| 792 | + // based on: |
| 793 | + // http://style.cleverchimp.com/font_size_intervals/altintervals.html |
687 | 794 | var sizeMap = { |
688 | 795 | 'xx-small' : '.57em', |
689 | 796 | 'x-small' : '.69em', |
— | — | @@ -696,7 +803,7 @@ |
697 | 804 | cssAttributes['font-size'] = sizeMap[ cssAttributes['font-size'] ]; |
698 | 805 | } |
699 | 806 | |
700 | | - // If the font size is pixel based parent span will have no effect, |
| 807 | + // If the font size is pixel based parent span will have no effect, |
701 | 808 | // directly resize the pixels |
702 | 809 | if( cssAttributes['font-size'] && cssAttributes['font-size'].indexOf('px') != -1 ){ |
703 | 810 | cssAttributes['font-size'] = ( parseFloat( cssAttributes['font-size'] ) |
— | — | @@ -704,7 +811,7 @@ |
705 | 812 | } |
706 | 813 | |
707 | 814 | |
708 | | - // Translate rootLayout properties into div |
| 815 | + // Translate rootLayout properties into div |
709 | 816 | return cssAttributes; |
710 | 817 | } |
711 | 818 | } |
\ No newline at end of file |
Index: branches/MwEmbedStandAlone/modules/SmilPlayer/mw.SmilAnimate.js |
— | — | @@ -317,7 +317,7 @@ |
318 | 318 | //mw.log( "transformImageForTime:: animateTime:" + animateTime ); |
319 | 319 | |
320 | 320 | if( $j( smilImgElement ).children().length == 0 ){ |
321 | | - // No image transform children |
| 321 | + // No animation transform children |
322 | 322 | return ; |
323 | 323 | } |
324 | 324 | |
— | — | @@ -336,15 +336,10 @@ |
337 | 337 | // No animate elements in range, make sure we transform to previous or to initial state if time is zero |
338 | 338 | if( !animateInRange ) { |
339 | 339 | if( animateTime == 0 ) { |
340 | | - // just a hack for now ( should read from previous animation or from source attribute |
341 | | - // this.updateElementLayout( smilImgElement, { 'top':1,'left':1,'width':1, 'height':1 } ); |
342 | | - var $target = $j( '#' + this.smil.getPageDomId( smilImgElement )); |
343 | | - $target.css( { |
344 | | - 'top' : '0px', |
345 | | - 'left' :'0px', |
346 | | - 'width' : '100%', |
347 | | - 'height' : '100%' |
348 | | - } ); |
| 340 | + // Check if we have native resolution information |
| 341 | + // xxx here would be a good place to check the "fit" criteria |
| 342 | + // http://www.w3.org/TR/SMIL3/smil-layout.html#adef-fit |
| 343 | + // for now we assume fit "attribute" value is "meet" |
349 | 344 | } |
350 | 345 | // xxx should check for transform to previous |
351 | 346 | } |
— | — | @@ -402,30 +397,35 @@ |
403 | 398 | // Let Top Width Height |
404 | 399 | // translate values into % values |
405 | 400 | // NOTE this is dependent on the media being "loaded" and having natural width and height |
| 401 | + |
| 402 | + var percentValues = this.getPercentFromPanZoomValues( targetValue, |
| 403 | + this.smil.getLayout().getNaturalSize( smilImgElement ) |
| 404 | + ); |
| 405 | + |
| 406 | + // Now we have "hard" layout info try and render it. |
| 407 | + this.updateElementLayout( smilImgElement, percentValues ); |
| 408 | + }, |
| 409 | + // transforms pan zoom target value into layout percentages |
| 410 | + getPercentFromPanZoomValues: function(targetValue, naturalSize){ |
406 | 411 | var namedValueOrder = ['left', 'top', 'width', 'height' ]; |
407 | | - var htmlAsset = $j( '#' + this.smil.getPageDomId( smilImgElement ) ).get(0); |
408 | | - |
409 | 412 | var percentValues = {}; |
410 | 413 | for( var i =0 ;i < targetValue.length ; i++ ){ |
411 | 414 | if( targetValue[i].indexOf('%') == -1 ) { |
412 | 415 | switch( namedValueOrder[i] ){ |
413 | 416 | case 'left': |
414 | 417 | case 'width': |
415 | | - percentValues[ namedValueOrder[i] ] = parseFloat( targetValue[i] ) / htmlAsset.naturalWidth; |
| 418 | + percentValues[ namedValueOrder[i] ] = parseFloat( targetValue[i] ) / naturalSize.width; |
416 | 419 | break; |
417 | 420 | case 'height': |
418 | 421 | case 'top': |
419 | | - percentValues[ namedValueOrder[i] ] = parseFloat( targetValue[i] ) / htmlAsset.naturalHeight |
| 422 | + percentValues[ namedValueOrder[i] ] = parseFloat( targetValue[i] ) / naturalSize.height |
420 | 423 | break; |
421 | 424 | } |
422 | 425 | } else { |
423 | 426 | percentValues[ namedValueOrder[i] ] = parseFloat( targetValue[i] ) / 100; |
424 | 427 | } |
425 | | - } |
426 | | - |
427 | | - // Now we have "hard" layout info try and render it. |
428 | | - this.updateElementLayout( smilImgElement, percentValues ); |
429 | | - |
| 428 | + } |
| 429 | + return percentValues; |
430 | 430 | }, |
431 | 431 | |
432 | 432 | // xxx need to refactor move to "smilLayout" |
— | — | @@ -445,14 +445,14 @@ |
446 | 446 | var fullHeight = $target.parents('.smilRegion').height() ; |
447 | 447 | var targetWidth = fullWidth; |
448 | 448 | var targetHeight = targetWidth * ( |
449 | | - ( percentValues['height'] * htmlAsset.naturalHeight ) |
| 449 | + ( parseInt( percentValues['height'] ) * htmlAsset.naturalHeight ) |
450 | 450 | / |
451 | | - ( percentValues['width'] * htmlAsset.naturalWidth ) |
| 451 | + ( parseInt( percentValues['width'] ) * htmlAsset.naturalWidth ) |
452 | 452 | ) |
453 | 453 | // Check if it exceeds the height constraint: |
454 | 454 | var sourceScale = ( targetHeight < fullHeight ) |
455 | | - ? (1 / percentValues['width'] ) |
456 | | - : (1 / percentValues['height'] ) |
| 455 | + ? (1 / parseInt( percentValues['width'] ) ) |
| 456 | + : (1 / parseInt( percentValues['height'] ) ) |
457 | 457 | |
458 | 458 | |
459 | 459 | // Wrap the target and absolute the image layout ( if not already ) |
— | — | @@ -474,8 +474,8 @@ |
475 | 475 | 'position' : 'absolute', |
476 | 476 | 'width' : sourceScale *100 + '%', |
477 | 477 | 'height' : sourceScale *100 + '%', |
478 | | - 'top' : (-1 * percentValues['top'])*100 + '%', |
479 | | - 'left' : (-1 * percentValues['left'])*100 + '%', |
| 478 | + 'top' : (-1 * parseInt( percentValues['top'] ) )*100 + '%', |
| 479 | + 'left' : (-1 * parseInt( percentValues['left'] ) )*100 + '%', |
480 | 480 | } ); |
481 | 481 | }, |
482 | 482 | |
Index: branches/MwEmbedStandAlone/modules/EmbedPlayer/skins/mvpcf/mw.style.PlayerSkinMvpcf.css |
— | — | @@ -18,7 +18,8 @@ |
19 | 19 | height: 305px; |
20 | 20 | } |
21 | 21 | .mv-player .control-bar { |
22 | | - height: 29px; |
| 22 | + height: 29px; |
| 23 | + z-index: 2; |
23 | 24 | } |
24 | 25 | .mv-player .controlInnerSmall { |
25 | 26 | /* width: 430px;*/ |
Index: branches/MwEmbedStandAlone/modules/Sequencer/Sequencer.i18n.php |
— | — | @@ -31,12 +31,26 @@ |
32 | 32 | 'mwe-sequencer-tools-trim' => 'Trim', |
33 | 33 | 'mwe-sequencer-tools-trim-desc' => 'Set clip in and out points', |
34 | 34 | 'mwe-sequencer-tools-duration' => 'Duration', |
| 35 | + 'mwe-sequencer-clip-duration' => 'Clip duration', |
| 36 | + |
35 | 37 | 'mwe-sequencer-tools-duration-desc' => 'Set clip duration', |
36 | 38 | |
| 39 | + 'mwe-sequencer-tools-panzoom' => 'Layout', |
| 40 | + 'mwe-sequencer-clip-panzoom' => 'Panzoom', |
| 41 | + 'mwe-sequencer-tools-panzoom-desc' => 'Set layout position and zoom', |
| 42 | + 'mwe-sequencer-tools-panzoomhelper' => 'Layout helper', |
| 43 | + 'mwe-sequencer-tools-panzoomhelper-desc' => 'Resize and move the <i>layout helper</i> to update layout', |
| 44 | + 'mwe-sequencer-tools-panzoomhelper-resetlayout' => 'Reset layout', |
| 45 | + |
| 46 | + 'mwe-sequencer-tools-transitions' => 'Transitions', |
| 47 | + 'mwe-sequencer-tools-transitions-desc' => 'Set in and out Transitions', |
| 48 | + 'mwe-sequencer-clip-transin' => 'Transition in', |
| 49 | + 'mwe-sequencer-clip-transout' => 'Transition out', |
| 50 | + |
37 | 51 | 'mwe-sequencer-preview' => 'Preview', |
38 | 52 | 'mwe-sequencer-apply-changes' => 'Apply changes', |
39 | 53 | 'mwe-sequencer-start-time' => 'Start time', |
40 | | - 'mwe-sequencer-clip-duration' => 'Clip duration', |
| 54 | + |
41 | 55 | |
42 | 56 | 'mwe-sequencer-loading_user_rights' => 'Loading user rights ...', |
43 | 57 | 'mwe-sequencer-sequence-xml' => 'Sequence smil xml', |
— | — | @@ -108,15 +122,13 @@ |
109 | 123 | 'mwe-sequencer-publishing-success' => 'Publish success', |
110 | 124 | 'mwe-sequencer-publishing-success-desc' => 'Sequence has successfully been published. [$1 Published file]', |
111 | 125 | |
112 | | - 'mwe-sequencer-transition_in' => 'Transition in', |
113 | | - 'mwe-sequencer-transition_out' => 'Transition out', |
| 126 | + |
114 | 127 | 'mwe-sequencer-effects' => 'Effects stack', |
115 | 128 | 'mwe-sequencer-remove_transition' => 'Remove transition', |
116 | 129 | 'mwe-sequencer-edit_transin' => 'Edit transition into clip', |
117 | 130 | 'mwe-sequencer-edit_transout' => 'Edit transition out of clip', |
118 | 131 | 'mwe-sequencer-add-transition' => 'Add a transition', |
119 | | - 'mwe-sequencer-menu_clipedit' => 'Edit media', |
120 | | - 'mwe-sequencer-menu_transition' => 'Transitions and effects', |
| 132 | + 'mwe-sequencer-menu_clipedit' => 'Edit media', |
121 | 133 | 'mwe-sequencer-menu_cliplib' => 'Add media', |
122 | 134 | 'mwe-sequencer-menu_resource_overview' => 'Resource overview', |
123 | 135 | 'mwe-sequencer-menu_options' => 'Options', |
Index: branches/MwEmbedStandAlone/modules/Sequencer/actions/mw.SequencerActionsSequence.js |
— | — | @@ -45,6 +45,7 @@ |
46 | 46 | 'buttons' : buttons, |
47 | 47 | 'width' : 450 |
48 | 48 | }); |
| 49 | + $dialog.find('input').focus(); |
49 | 50 | // Add a special open button |
50 | 51 | $dialog.parent().find( '.ui-dialog-buttonpane' ).prepend( |
51 | 52 | $j.button({ |
— | — | @@ -110,6 +111,7 @@ |
111 | 112 | 'buttons' : buttons, |
112 | 113 | 'width' : 450 |
113 | 114 | }); |
| 115 | + $dialog.find('input').focus(); |
114 | 116 | // Add a special open button |
115 | 117 | $dialog.parent().find( '.ui-dialog-buttonpane' ).prepend( |
116 | 118 | $j.button({ |
— | — | @@ -231,10 +233,12 @@ |
232 | 234 | 'maxlength': 255 |
233 | 235 | }) |
234 | 236 | // Make sure keys press does not affect the sequencer interface |
235 | | - .sequencerInput( _this.sequencer ) |
236 | | - ) |
| 237 | + .sequencerInput( _this.sequencer ) |
| 238 | + ) |
237 | 239 | .dialog( "option", "buttons", saveDialogButtons ) |
238 | 240 | .dialog( "option", "title", gM('mwe-sequencer-menu-sequence-save-desc') ) |
| 241 | + // give the input focus |
| 242 | + .find('input').focus() |
239 | 243 | }, |
240 | 244 | /** |
241 | 245 | * Display the publish dialog |
— | — | @@ -457,6 +461,7 @@ |
458 | 462 | }; |
459 | 463 | buttons[ gM('mwe-sequencer-menu-sequence-exit-desc') ] = function(){ |
460 | 464 | _this.closeSequencer(); |
| 465 | + $j(this).dialog('close'); |
461 | 466 | } |
462 | 467 | // Confirm the user wants to exit |
463 | 468 | mw.addDialog( { |
Index: branches/MwEmbedStandAlone/modules/Sequencer/mw.SequencerTools.js |
— | — | @@ -14,7 +14,11 @@ |
15 | 15 | init: function( sequencer ){ |
16 | 16 | this.sequencer = sequencer; |
17 | 17 | }, |
18 | | - // The current selected tool |
| 18 | + |
| 19 | + // The current smil clip ( lazy init ) |
| 20 | + currentSmilClip: null, |
| 21 | + |
| 22 | + // The current selected tool ( lazy init ) |
19 | 23 | currentToolId: null, |
20 | 24 | |
21 | 25 | // JSON tools config |
— | — | @@ -26,14 +30,20 @@ |
27 | 31 | }, |
28 | 32 | 'duration':{ |
29 | 33 | 'editableAttributes' : [ 'dur' ], |
30 | | - 'contentTypes': ['image'] |
| 34 | + 'contentTypes': ['img'] |
31 | 35 | }, |
32 | 36 | 'panzoom' : { |
| 37 | + 'editWidgets' : ['panzoom'], |
33 | 38 | 'editableAttributes' : [ 'panZoom' ], |
34 | | - 'contentTypes': ['video', 'image'] |
| 39 | + 'contentTypes': ['video', 'img'], |
| 40 | + 'animate' : 'true' |
| 41 | + }, |
| 42 | + 'transitions' : { |
| 43 | + 'editableAttributes' : [ 'transIn', 'transOut' ], |
| 44 | + 'contentTypes': ['video', 'img' ] |
35 | 45 | } |
36 | 46 | }, |
37 | | - editableAttributes:{ |
| 47 | + editableAttributes:{ |
38 | 48 | 'clipBegin':{ |
39 | 49 | 'type': 'time', |
40 | 50 | 'title' : gM('mwe-sequencer-start-time' ) |
— | — | @@ -43,11 +53,37 @@ |
44 | 54 | 'title' : gM('mwe-sequencer-clip-duration' ) |
45 | 55 | }, |
46 | 56 | 'panZoom' :{ |
47 | | - 'type' : 'panzoom', |
48 | | - 'title' : gM('mwe-sequencer-clip-layout' ) |
| 57 | + 'type' : 'display', |
| 58 | + 'inputSize' : 15, |
| 59 | + 'title' : gM('mwe-sequencer-clip-panzoom' ), |
| 60 | + 'defaultValue' : '0, 0, 100%, 100%' |
| 61 | + }, |
| 62 | + 'transIn' : { |
| 63 | + 'type' : 'select', |
| 64 | + 'selectValues' : [ 'fadeFromColor' ] |
| 65 | + }, |
| 66 | + 'transOut' : { |
| 67 | + 'type' : 'select', |
| 68 | + 'selectValues' : [ 'fadeFromColor', 'crossfade' ] |
49 | 69 | } |
50 | 70 | }, |
51 | 71 | editableTypes: { |
| 72 | + 'display': { |
| 73 | + update: function( _this, smilClip, attributeName, value){ |
| 74 | + $j( smilClip ).attr( attributeName, value); |
| 75 | + // update the display |
| 76 | + }, |
| 77 | + getSmilVal : function( _this, smilClip, attributeName ){ |
| 78 | + if( $j( smilClip ).attr( attributeName ) ){ |
| 79 | + return $j( smilClip ).attr( attributeName ) |
| 80 | + } |
| 81 | + // Check for a default value |
| 82 | + if( _this.editableAttributes[ attributeName ].defaultValue ){ |
| 83 | + return _this.editableAttributes[ attributeName ].defaultValue; |
| 84 | + } |
| 85 | + return ''; |
| 86 | + } |
| 87 | + }, |
52 | 88 | 'time' : { |
53 | 89 | update : function( _this, smilClip, attributeName, value){ |
54 | 90 | // Validate time |
— | — | @@ -80,7 +116,7 @@ |
81 | 117 | // xxx todo update preview button to "pause" / "play" |
82 | 118 | } |
83 | 119 | }, |
84 | | - 'cancel':{ |
| 120 | + 'cancel' : { |
85 | 121 | 'icon': 'close', |
86 | 122 | 'title' : gM('mwe-cancel'), |
87 | 123 | 'action' : function( _this, smilClip ){ |
— | — | @@ -113,7 +149,150 @@ |
114 | 150 | } |
115 | 151 | }, |
116 | 152 | editWidgets: { |
117 | | - 'trimTimeline':{ |
| 153 | + 'panzoom' : { |
| 154 | + 'onChange': function( _this, target, smilClip ){ |
| 155 | + var panZoomVal = $j('#' +_this.getEditToolInputId( 'panzoom', 'panZoom')).val(); |
| 156 | + mw.log("panzoom change:" + panZoomVal ); |
| 157 | + }, |
| 158 | + 'draw': function( _this, target, smilClip ){ |
| 159 | + var orginalHelperCss = { |
| 160 | + 'position' : 'absolute', |
| 161 | + 'width' : 100, |
| 162 | + 'height' : 75, |
| 163 | + 'top' : 50, |
| 164 | + 'left' : 70, |
| 165 | + 'font-size' : 'x-small' |
| 166 | + }; |
| 167 | + // Add a input box binding: |
| 168 | + $j('#' +_this.getEditToolInputId( 'panzoom', 'panZoom')) |
| 169 | + .change(function(){ |
| 170 | + _this.editWidgets.panzoom.onChange( _this, target, smilClip); |
| 171 | + }) |
| 172 | + |
| 173 | + $j( target ).append( |
| 174 | + $j('<h3 />').html( |
| 175 | + gM('mwe-sequencer-tools-panzoomhelper-desc') |
| 176 | + ) |
| 177 | + , |
| 178 | + /*xxx Keep aspect button ?*/ |
| 179 | + // Rest layout button ( restores default position ) |
| 180 | + $j.button({ |
| 181 | + 'icon' : 'arrow-4', |
| 182 | + 'text' : gM( 'mwe-sequencer-tools-panzoomhelper-resetlayout' ) |
| 183 | + }) |
| 184 | + .attr('id', 'panzoomResetLayout') |
| 185 | + .css('float', 'left') |
| 186 | + .hide() |
| 187 | + .click(function(){ |
| 188 | + // Restore default SMIL setting |
| 189 | + _this.editableTypes['display'].update( |
| 190 | + _this, |
| 191 | + smilClip, |
| 192 | + 'panzoom', |
| 193 | + _this.editableAttributes['panzoom'].defaultValue |
| 194 | + ) |
| 195 | + }) |
| 196 | + , |
| 197 | + $j('<div />') |
| 198 | + .css({ |
| 199 | + 'border' : '1px solid #DDDDDD', |
| 200 | + 'float' : 'left', |
| 201 | + 'position' : 'relative', |
| 202 | + 'width': '240px', |
| 203 | + 'height' : '180px', |
| 204 | + 'overflow' : 'hidden' |
| 205 | + }) |
| 206 | + .append( |
| 207 | + $j('<div />') |
| 208 | + .css( orginalHelperCss ) |
| 209 | + .attr({ |
| 210 | + 'id': "panzoomHelper" |
| 211 | + }) |
| 212 | + .addClass("ui-widget-content") |
| 213 | + .text( gM('mwe-sequencer-tools-panzoomhelper') ) |
| 214 | + ) |
| 215 | + ); |
| 216 | + var startPanZoomVal = ''; |
| 217 | + var setStartPanZoomVal = function(){ |
| 218 | + startPanZoomVal = $j( smilClip ).attr( 'panZoom'); |
| 219 | + if(! startPanZoomVal ){ |
| 220 | + startPanZoomVal = _this.editableAttributes['panZoom'].defaultValue; |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + var updatePanZoomFromUiValue = function( layout ){ |
| 225 | + var pz = startPanZoomVal.split(','); |
| 226 | + // Set the new percent offset to x/2 |
| 227 | + if( layout.left ) |
| 228 | + pz[0] = ( parseInt( pz[0] ) + ( layout.left / 2 ) ) + '%'; |
| 229 | + |
| 230 | + if( layout.top ) |
| 231 | + pz[1] = ( parseInt( pz[1] ) + ( layout.top / 2 ) )+ '%'; |
| 232 | + |
| 233 | + if( layout.width ) |
| 234 | + pz[2] = ( parseInt( pz[2] ) + ( layout.width / 2 ) ) + '%' |
| 235 | + |
| 236 | + if( layout.height ) |
| 237 | + pz[2] = ( parseInt( pz[2] ) + ( layout.width / 2 ) ) + '%' |
| 238 | + |
| 239 | + var smilPanZoomValue = pz.join(', '); |
| 240 | + |
| 241 | + // Update the smil DOM: |
| 242 | + $j( smilClip ).attr( 'panZoom', smilPanZoomValue ); |
| 243 | + |
| 244 | + // Update the user input tool input value: |
| 245 | + $j('#' +_this.getEditToolInputId( 'panzoom', 'panZoom')).val( smilPanZoomValue ); |
| 246 | + |
| 247 | + // Animate the update on the current smil clip display: |
| 248 | + _this.sequencer.getSmil() |
| 249 | + .getLayout() |
| 250 | + .panZoomLayout( |
| 251 | + smilClip |
| 252 | + ); |
| 253 | + } |
| 254 | + // Add bindings |
| 255 | + $j('#panzoomHelper') |
| 256 | + .draggable({ |
| 257 | + containment: 'parent', |
| 258 | + start: function( event, ui){ |
| 259 | + setStartPanZoomVal(); |
| 260 | + }, |
| 261 | + drag: function( event, ui){ |
| 262 | + updatePanZoomFromUiValue({ |
| 263 | + 'top' : ( orginalHelperCss.top - ui.position.top ), |
| 264 | + 'left' : ( orginalHelperCss.left - ui.position.left ) |
| 265 | + }); |
| 266 | + }, |
| 267 | + stop: function( event, ui){ |
| 268 | + // run the onChange ? |
| 269 | + // Restore original css for the layout helper |
| 270 | + $j(this).css( orginalHelperCss ) |
| 271 | + } |
| 272 | + }) |
| 273 | + .css('cursor', 'move') |
| 274 | + .resizable({ |
| 275 | + handles : 'all', |
| 276 | + maxWidth : 170, |
| 277 | + maxHeight: 130, |
| 278 | + aspectRatio: 4/3, |
| 279 | + start: function( event, ui){ |
| 280 | + setStartPanZoomVal(); |
| 281 | + }, |
| 282 | + resize : function(event, ui){ |
| 283 | + updatePanZoomFromUiValue({ |
| 284 | + 'width' : ( orginalHelperCss.width - ui.size.width ), |
| 285 | + 'height' : ( orginalHelperCss.top - ui.size.height ) |
| 286 | + }); |
| 287 | + }, |
| 288 | + stop: function( event, ui){ |
| 289 | + // Restore original css |
| 290 | + $j(this).css( orginalHelperCss ) |
| 291 | + } |
| 292 | + }) |
| 293 | + |
| 294 | + } |
| 295 | + }, |
| 296 | + 'trimTimeline' : { |
118 | 297 | 'onChange': function( _this, target, smilClip ){ |
119 | 298 | var smil = _this.sequencer.getSmil(); |
120 | 299 | // Update the preview thumbs |
— | — | @@ -160,10 +339,13 @@ |
161 | 340 | ) |
162 | 341 | } |
163 | 342 | |
164 | | - // Add a trim binding: |
165 | | - $j('#editTool_trim_clipBegin,#editTool_trim_dur').change(function(){ |
| 343 | + // Add a trim binding: |
| 344 | + $j('#' + _this.getEditToolInputId( 'trim', 'clipBegin') + |
| 345 | + ',#' + _this.getEditToolInputId( 'trim', 'dur') ) |
| 346 | + .change( function(){ |
166 | 347 | _this.editWidgets.trimTimeline.onChange( _this, target, smilClip); |
167 | 348 | }) |
| 349 | + |
168 | 350 | // Update the thumbnails: |
169 | 351 | _this.editWidgets.trimTimeline.onChange( _this, target, smilClip); |
170 | 352 | |
— | — | @@ -186,7 +368,8 @@ |
187 | 369 | $j('<div />') |
188 | 370 | .attr( 'id', _this.sequencer.id + '_trimTimeline' ) |
189 | 371 | .css({ |
190 | | - 'left' : '5px', |
| 372 | + 'position': 'absolute', |
| 373 | + 'left' : '15px', |
191 | 374 | 'right' : '15px', |
192 | 375 | 'margin': '5px' |
193 | 376 | }) |
— | — | @@ -195,11 +378,12 @@ |
196 | 379 | min: 0, |
197 | 380 | max: 1000, |
198 | 381 | values: sliderValues, |
199 | | - slide: function(event, ui) { |
200 | | - $j('#editTool_trim_clipBegin').val( |
| 382 | + slide: function(event, ui) { |
| 383 | + |
| 384 | + $j('#' + _this.getEditToolInputId( 'trim', 'clipBegin') ).val( |
201 | 385 | mw.seconds2npt( sliderToTime( ui.values[0] ), true ) |
202 | 386 | ); |
203 | | - $j('#editTool_trim_dur').val( |
| 387 | + $j('#' + _this.getEditToolInputId( 'trim', 'dur') ).val( |
204 | 388 | mw.seconds2npt( sliderToTime( ui.values[1] - ui.values[0] ), true ) |
205 | 389 | ); |
206 | 390 | }, |
— | — | @@ -245,59 +429,57 @@ |
246 | 430 | */ |
247 | 431 | updateToolDisplay: function(){ |
248 | 432 | var _this = this; |
249 | | - // Update all tool input values:: trigger change event if changed |
250 | | - var smilClip = this.getCurrentSmilClip(); |
| 433 | + |
| 434 | + // If tools are displayed update them |
| 435 | + if( this.sequencer.getEditToolTarget().find('.editToolsContainer').lenght ){ |
| 436 | + this.drawClipEditTools() |
| 437 | + } |
251 | 438 | |
252 | | - $j.each( |
253 | | - _this.getToolSet( |
254 | | - _this.sequencer.getSmil().getRefType( smilClip ) |
255 | | - ), |
256 | | - function( inx, toolId ){ |
257 | | - var tool = _this.tools[toolId]; |
258 | | - for( var i=0; i < tool.editableAttributes.length ; i++ ){ |
259 | | - var attributeName = tool.editableAttributes[i]; |
260 | | - var $editToolInput = $j('#' + _this.getEditToolInputId( toolId, attributeName ) ); |
261 | | - // Sync with smilClip value |
262 | | - if( smilClip.attr( attributeName ) != $editToolInput.val() ){ |
263 | | - $editToolInput.val( smilClip.attr( attributeName ) ); |
264 | | - // trigger change event: |
265 | | - $editToolInput.change(); |
266 | | - } |
267 | | - } |
268 | | - } |
269 | | - ); |
270 | 439 | }, |
271 | 440 | getToolSet: function( refType ){ |
272 | | - var toolSet = []; |
| 441 | + var toolSet = []; |
273 | 442 | for( var toolId in this.tools){ |
274 | | - if( this.tools[toolId].contentTypes){ |
275 | | - if( $j.inArray( refType, this.tools[toolId].contentTypes) != -1 ){ |
| 443 | + if( this.tools[ toolId ].contentTypes){ |
| 444 | + if( $j.inArray( refType, this.tools[ toolId ].contentTypes) != -1 ){ |
276 | 445 | toolSet.push( toolId ); |
277 | 446 | } |
278 | 447 | } |
279 | 448 | } |
280 | 449 | return toolSet; |
281 | 450 | }, |
282 | | - drawClipEditTools: function( smilClip ){ |
| 451 | + drawClipEditTools: function( smilClip, selectedToolId ){ |
283 | 452 | var _this = this; |
284 | | - var toolId = ''; |
285 | | - var $target = this.sequencer.getEditToolTarget(); |
286 | 453 | |
287 | | - // Set the current smilClip |
288 | | - this.currentSmilClip = smilClip; |
| 454 | + // Update the current clip and tool : |
| 455 | + if( smilClip ){ |
| 456 | + this.setCurrentSmilClip( smilClip ); |
| 457 | + } |
| 458 | + if( selectedToolId ){ |
| 459 | + this.setCurrentToolId( selectedToolId ); |
| 460 | + } |
289 | 461 | |
290 | | - |
291 | | - $target.empty().append( |
292 | | - $j('<div />') |
293 | | - .addClass( 'editToolsContainer' ) |
294 | | - .append( |
295 | | - $j('<ul />') |
296 | | - ) |
| 462 | + $toolsContainer = $j('<div />') |
| 463 | + .addClass( 'editToolsContainer' ) |
| 464 | + .css( { |
| 465 | + 'height': '80%' |
| 466 | + }) |
| 467 | + .append( |
| 468 | + $j('<ul />') |
297 | 469 | ); |
298 | 470 | |
299 | | - // get the toolId based on what "ref type" smilClip is: |
300 | | - $j.each( this.getToolSet( this.sequencer.getSmil().getRefType( smilClip ) ), function( inx, toolId ){ |
301 | | - |
| 471 | + this.sequencer.getEditToolTarget().empty().append( |
| 472 | + $toolsContainer |
| 473 | + ); |
| 474 | + // Get the entire tool set based on what "ref type" smilClip is: |
| 475 | + var toolSet = this.getToolSet( |
| 476 | + this.sequencer.getSmil().getRefType( |
| 477 | + this.getCurrentSmilClip() |
| 478 | + ) |
| 479 | + ); |
| 480 | + mw.log( 'Adding ' + toolSet.length + ' tools for ' + this.sequencer.getSmil().getRefType( this.getCurrentSmilClip() ) ); |
| 481 | + |
| 482 | + $j.each( toolSet, function( inx, toolId ){ |
| 483 | + |
302 | 484 | var tool = _this.tools[ toolId ]; |
303 | 485 | |
304 | 486 | // set the currentTool if not already set |
— | — | @@ -306,7 +488,7 @@ |
307 | 489 | } |
308 | 490 | |
309 | 491 | // Append the title to the ul list |
310 | | - $target.find( 'ul').append( |
| 492 | + $toolsContainer.find( 'ul').append( |
311 | 493 | $j('<li />').append( |
312 | 494 | $j('<a />') |
313 | 495 | .attr('href', '#tooltab_' + toolId ) |
— | — | @@ -315,11 +497,15 @@ |
316 | 498 | ); |
317 | 499 | |
318 | 500 | // Append the tooltab container |
319 | | - $target.append( |
| 501 | + $toolsContainer.append( |
320 | 502 | $j('<div />') |
321 | | - .attr('id', 'tooltab_' + toolId ) |
| 503 | + .css({'height' : '100%', 'overflow': 'auto'}) |
| 504 | + .attr('id', 'tooltab_' + toolId ) |
| 505 | + .append( |
| 506 | + $j('<h3 />').text( gM('mwe-sequencer-tools-' + toolId + '-desc') ) |
| 507 | + ) |
322 | 508 | ) |
323 | | - var $toolContainer = $target.find( '#tooltab_' + toolId ); |
| 509 | + var $toolContainer = $toolsContainer.find( '#tooltab_' + toolId ); |
324 | 510 | |
325 | 511 | // Build out the attribute list for the given tool: |
326 | 512 | for( var i=0; i < tool.editableAttributes.length ; i++ ){ |
— | — | @@ -358,14 +544,14 @@ |
359 | 545 | }); |
360 | 546 | |
361 | 547 | // Add tab bindings |
362 | | - $target.find('.editToolsContainer').tabs({ |
| 548 | + $toolsContainer.tabs({ |
363 | 549 | select: function(event, ui) { |
364 | | - debugger; |
| 550 | + |
365 | 551 | } |
366 | 552 | }) |
367 | | - // Build out global edit Actions buttons ( per 'current tool' ) |
| 553 | + // Build out global edit Actions buttons after the container |
368 | 554 | for( var editActionId in this.editActions ){ |
369 | | - $target.append( |
| 555 | + $toolsContainer.after( |
370 | 556 | this.getEditAction( smilClip, editActionId ) |
371 | 557 | ) |
372 | 558 | } |
— | — | @@ -373,9 +559,15 @@ |
374 | 560 | getCurrentSmilClip: function(){ |
375 | 561 | return this.currentSmilClip; |
376 | 562 | }, |
| 563 | + setCurrentSmilClip: function( smilClip ){ |
| 564 | + this.currentSmilClip = smilClip; |
| 565 | + }, |
377 | 566 | getCurrentToolId: function(){ |
378 | 567 | return this.currentToolId; |
379 | 568 | }, |
| 569 | + setCurrentToolId: function( toolId ){ |
| 570 | + this.currentToolId = toolId; |
| 571 | + }, |
380 | 572 | |
381 | 573 | getEditAction: function( smilClip, editActionId ){ |
382 | 574 | if(! this.editActions[ editActionId ]){ |
— | — | @@ -397,6 +589,7 @@ |
398 | 590 | }) |
399 | 591 | return $actionButton; |
400 | 592 | }, |
| 593 | + /* get the editiable attribute input html */ |
401 | 594 | getEditableAttribute: function( smilClip, toolId, attributeName ){ |
402 | 595 | if( ! this.editableAttributes[ attributeName ] ){ |
403 | 596 | mw.log("Error: editableAttributes : " + attributeName + ' not found'); |
— | — | @@ -405,17 +598,23 @@ |
406 | 599 | var _this = this; |
407 | 600 | var editAttribute = this.editableAttributes[ attributeName ]; |
408 | 601 | var editType = editAttribute.type; |
409 | | - |
| 602 | + if( !_this.editableTypes[ editType ] ){ |
| 603 | + mw.log(" Error: No editableTypes interface for " + editType); |
| 604 | + return ; |
| 605 | + } |
410 | 606 | var initialValue = _this.editableTypes[ editType ].getSmilVal( |
411 | 607 | _this, |
412 | 608 | smilClip, |
413 | 609 | attributeName |
414 | 610 | ); |
| 611 | + // Set the default input size |
| 612 | + var inputSize = ( _this.editableAttributes[ attributeName ].inputSize)? |
| 613 | + _this.editableAttributes[ attributeName ].inputSize : 6; |
| 614 | + |
415 | 615 | return $j( '<div />' ) |
416 | 616 | .css({ |
417 | 617 | 'float': 'left', |
418 | | - 'font-size': '12px', |
419 | | - 'width': '160px', |
| 618 | + 'font-size': '12px', |
420 | 619 | 'border': 'solid thin #999', |
421 | 620 | 'background-color': '#EEE', |
422 | 621 | 'padding' : '2px', |
— | — | @@ -430,7 +629,7 @@ |
431 | 630 | $j('<input />') |
432 | 631 | .attr( { |
433 | 632 | 'id' : _this.getEditToolInputId( toolId, attributeName), |
434 | | - 'size': 6 |
| 633 | + 'size': inputSize |
435 | 634 | }) |
436 | 635 | .data('initialValue', initialValue ) |
437 | 636 | .sequencerInput( _this.sequencer ) |